Guides: Feature-Sliced Design: Check a Feature-Sliced Design front end with the fsd preset, which holds the rules of Steiger, each with examples. # Feature-Sliced Design > Check a Feature-Sliced Design front end with the fsd preset, which holds the rules of Steiger, each with examples. This guide checks a front end that uses [Feature-Sliced Design](https://feature-sliced.design) (FSD) with the `fsd` preset. The preset holds the rules of Steiger, the official linter for FSD. ## Before you start * Read [Getting started](/archangel/start/getting-started/). * Your front end has the FSD layers in one folder: `app`, `pages`, `widgets`, `features`, `entities` and `shared`. A repository needs only some of them. The example is a small store front. An import between two slices uses the alias `@/`, which `tsconfig.json` points at `src`: * archangel.config.ts * tsconfig.json * src/ * app/ * index.ts * pages/ * home/ * index.ts * ui/ * home-page.ts * features/ * add-to-cart/ * index.ts * model/ * cart.ts * ui/ * add-to-cart-button.ts * entities/ * product/ * index.ts * model/ * product.ts * ui/ * product-card.ts * shared/ * ui/ * button.ts * index.ts ## Add the preset 1. Put the preset in `extends`. `root` is the folder that holds the layers: archangel.config.ts ```ts import { defineConfig, fsd } from '@enhance-eng/archangel'; export default defineConfig({ extends: [fsd({ root: 'src' })], }); ``` The config has no parts and no rules of its own. The preset declares each layer as a part, and each slice as an instance. 2. Check the repository: Terminal ```ansi $ npx archangel check error[plugin]: This slice has only one reference in slice "pages/home". Consider merging them. --> src/entities/product = note: rule: fsd/insignificant-slice = note: reason: A slice that at most one other slice uses belongs in that slice. = note: see: https://feature-sliced.github.io/steiger/rules/insignificant-slice error[may-not-import]: src/entities/product/ui/product-card.ts may not import "@/features/add-to-cart", because files in "entities" may not import "fsd/features", "fsd/widgets", "fsd/pages", "fsd/app" --> src/entities/product/ui/product-card.ts:1:33 | 1 | import { addToCartButton } from '@/features/add-to-cart'; | ^^^^^^^^^^^^^^^^^^^^^^^^ remove the import, or move the code to files that may import it | = note: rule: fsd/forbidden-imports = note: reason: A module may import only from layers below its own, and a slice not from another slice of its layer, except through @x. = note: see: https://feature-sliced.github.io/steiger/rules/forbidden-imports checked 12 files and 13 imports: 2 problems fsd/widgets matches nothing in this repository fsd/processes matches nothing in this repository For what a code means, run `archangel docs plugin` or `archangel docs may-not-import`. ``` The check finds two problems: * `fsd/forbidden-imports`: the entity `product` imports the feature `add-to-cart`. In FSD, a layer imports only from the layers below it. * `fsd/insignificant-slice`: only one other slice uses the entity `product`. Steiger tells you to merge it into that slice. The summary also lists the preset parts that find no file, here `widgets` and `processes`. This is not an error. A preset declares each layer of FSD, and most repositories have only some of them. 3. Run the examples: Output of `archangel test` Terminal ```ansi $ npx archangel test pass fsd/forbidden-imports accepted #1 src/entities/example-user/index.ts, src/features/example-login/model/a.ts pass fsd/forbidden-imports accepted #2 src/entities/example-a/model/a.ts, src/entities/example-b/@x/example-a.ts pass fsd/forbidden-imports rejected #1 src/features/example-login/index.ts, src/shared/example-lib/index.ts pass fsd/forbidden-imports rejected #2 src/features/example-a/model/a.ts, src/features/example-b/index.ts pass fsd/no-public-api-sidestep accepted #1 src/features/example-login/index.ts, src/pages/example-home/ui/a.ts pass fsd/no-public-api-sidestep rejected #1 src/features/example-login/model/x.ts, src/pages/example-home/ui/a.ts pass fsd/no-public-api-sidestep rejected #2 src/features/example-login/model/a.ts, src/shared/example-api/client.ts, src/shared/example-api/index.ts pass fsd/public-api accepted #1 src/features/example-login/index.ts, src/features/example-login/model/x.ts pass fsd/public-api rejected #1 src/features/example-login/model/x.ts pass fsd/no-layer-public-api rejected #1 src/entities/index.ts pass fsd/no-reserved-folder-names rejected #1 src/shared/example-kit/ui/button.ts pass fsd/no-segmentless-slices accepted #1 src/features/example-login/index.ts, src/features/example-login/model/login.ts pass fsd/no-segmentless-slices rejected #1 src/features/example-login/index.ts, src/features/example-login/login.ts pass fsd/no-segments-on-sliced-layers rejected #1 src/features/ui/button.ts pass fsd/no-ui-in-app rejected #1 src/app/ui/root.tsx pass fsd/no-ui-in-app rejected #2 src/app/ui.tsx pass fsd/segments-by-purpose rejected #1 src/features/example-login/utils/format.ts pass fsd/segments-by-purpose rejected #2 src/shared/helpers.ts pass fsd/no-processes rejected #1 src/processes/example-checkout/index.ts pass fsd/typo-in-layer-name accepted #1 src/features/example-login/index.ts pass fsd/typo-in-layer-name rejected #1 src/featurez/example.ts pass fsd/ambiguous-slice-names accepted #1 src/features/example-login/model/a.ts pass fsd/ambiguous-slice-names rejected #1 src/features/example-kit/model/a.ts, src/shared/example-kit/index.ts pass fsd/inconsistent-naming accepted #1 src/entities/example-group/apples/model/a.ts, src/entities/example-group/pears/model/a.ts pass fsd/inconsistent-naming rejected #1 src/entities/example-group/order-line/model/a.ts, src/entities/example-group/orders/model/a.ts pass fsd/insignificant-slice accepted #1 src/entities/example-used/index.ts, src/entities/example-used/model/a.ts, src/pages/example-one/ui/a.ts, src/pages/example-two/ui/b.ts pass fsd/insignificant-slice rejected #1 src/entities/example-lonely/index.ts, src/entities/example-lonely/model/a.ts pass fsd/repetitive-naming accepted #1 src/features/example-group/drop/model/a.ts, src/features/example-group/load/model/a.ts, src/features/example-group/save/model/a.ts pass fsd/repetitive-naming rejected #1 src/features/example-group/item-drop/model/a.ts, src/features/example-group/item-load/model/a.ts, src/features/example-group/item-save/model/a.ts pass fsd/excessive-slicing accepted #1 src/widgets/example-group/footer/ui/a.ts, src/widgets/example-group/header/ui/a.ts pass fsd/excessive-slicing rejected #1 src/widgets/example-group/w01/ui/a.ts, src/widgets/example-group/w02/ui/a.ts, src/widgets/example-group/w03/ui/a.ts, src/widgets/example-group/w04/ui/a.ts, src/widgets/example-group/w05/ui/a.ts, src/widgets/example-group/w06/ui/a.ts, src/widgets/example-group/w07/ui/a.ts, src/widgets/example-group/w08/ui/a.ts, src/widgets/example-group/w09/ui/a.ts, src/widgets/example-group/w10/ui/a.ts, src/widgets/example-group/w11/ui/a.ts, src/widgets/example-group/w12/ui/a.ts, src/widgets/example-group/w13/ui/a.ts, src/widgets/example-group/w14/ui/a.ts, src/widgets/example-group/w15/ui/a.ts, src/widgets/example-group/w16/ui/a.ts, src/widgets/example-group/w17/ui/a.ts, src/widgets/example-group/w18/ui/a.ts, src/widgets/example-group/w19/ui/a.ts, src/widgets/example-group/w20/ui/a.ts, src/widgets/example-group/w21/ui/a.ts pass fsd/shared-lib-grouping accepted #1 src/shared/lib/example-dates/index.ts pass fsd/shared-lib-grouping rejected #1 src/shared/lib/example-01.ts, src/shared/lib/example-02.ts, src/shared/lib/example-03.ts, src/shared/lib/example-04.ts, src/shared/lib/example-05.ts, src/shared/lib/example-06.ts, src/shared/lib/example-07.ts, src/shared/lib/example-08.ts, src/shared/lib/example-09.ts, src/shared/lib/example-10.ts, src/shared/lib/example-11.ts, src/shared/lib/example-12.ts, src/shared/lib/example-13.ts, src/shared/lib/example-14.ts, src/shared/lib/example-15.ts, src/shared/lib/example-16.ts 33 examples, none failed; 0 rules without a rejected example ``` Each rule of the preset has examples. Archangel checks them in your repository, under `root`, with your `tsconfig.json`. So a passing test also tells you that `root` is correct. ## Fix the problems 1. Remove the import of the feature from the entity. An entity shows a product. It does not add it to a cart: src/entities/product/ui/product-card.ts ```ts import { button } from '@/shared/ui'; export function productCard(name: string) { return button(name, () => {}); } ``` 2. Check again: Terminal ```ansi $ npx archangel check error[plugin]: This slice has only one reference in slice "pages/home". Consider merging them. --> src/entities/product = note: rule: fsd/insignificant-slice = note: reason: A slice that at most one other slice uses belongs in that slice. = note: see: https://feature-sliced.github.io/steiger/rules/insignificant-slice error[plugin]: This slice has only one reference in slice "pages/home". Consider merging them. --> src/features/add-to-cart = note: rule: fsd/insignificant-slice = note: reason: A slice that at most one other slice uses belongs in that slice. = note: see: https://feature-sliced.github.io/steiger/rules/insignificant-slice checked 12 files and 12 imports: 2 problems fsd/widgets matches nothing in this repository fsd/processes matches nothing in this repository For what a code means, run `archangel docs plugin`. ``` Now no entity imports `add-to-cart`, so only the page uses each slice. `fsd/insignificant-slice` finds both slices. ## Change the preset A store front this small has slices that only one page uses. The team decides that this is correct. It also wants the four rules of Steiger that are off by default. 1. Add `strict: true` and put the rule name in `off`: archangel.config.ts ```ts import { defineConfig, fsd } from '@enhance-eng/archangel'; export default defineConfig({ extends: [fsd({ root: 'src', strict: true })], off: ['fsd/insignificant-slice'], }); ``` `strict: true` adds the rules `fsd/no-cross-imports`, `fsd/no-higher-level-imports`, `fsd/import-locality` and `fsd/no-wildcard-exports`. 2. Check again: Terminal ```ansi $ npx archangel check checked 12 files and 12 imports: no problems fsd/widgets matches nothing in this repository fsd/processes matches nothing in this repository ``` import-locality needs an alias `fsd/import-locality` wants a relative import inside a slice, and an alias such as `@/` between slices and layers. If your code uses relative paths between slices, `strict: true` refuses each of them. You can also change one rule of the preset: * **Remove a rule.** Put its name in `off`, as above. `archangel rules` lists the rules that `off` removes. * **Replace a rule.** Write a rule with the same name, for example `fsd/public-api`. Your rule replaces the rule of the preset. * **A typing error stops the check.** A name in `off`, and the name of a replacement rule, must match a rule of the preset. If it does not, archangel stops and shows the nearest correct name. ## More than one front end To use the preset two times, give the second use its own `name`, for example `fsd({ root: 'apps/admin/src', name: 'fsd-admin' })`. Each name in a preset starts with the preset’s name, so the rules of the second use are `fsd-admin/forbidden-imports` and so on. ## What changes from Steiger * Archangel finds a slice by its folder. Each folder in a sliced layer is a slice. Steiger finds slices by the segments in them. * Each rule has examples, and `archangel test` runs them. * If all parts of the preset find no file, the `root` is probably wrong. Archangel shows `preset-empty`. Read [Migrate from Steiger](/archangel/guides/from-steiger/) to move an existing Steiger setup. ## Next steps * [Presets](/archangel/reference/presets/) lists the options of `fsd` and `nextjs`. * [Exceptions and baselines](/archangel/guides/exceptions-and-baselines/), to start on code that already breaks the rules.