Layers
Layers are the first level of organisational hierarchy in Feature-Sliced Design. Their purpose is to separate code based on how much responsibility it needs and how many other modules in the app it depends on.
On this page, a module refers to an internal module in the application β a file or directory with an index file. Not to be confused with npm packages.
Every layer carries special semantic meaning to help you determine how much responsibility you should allocate to a module in your code. The names and meanings of layers are standardized across all projects built with Feature-Sliced Design.
There are 7 layers in total, arranged from most responsibility andΒ dependency to least:
- App
- Processes (deprecated)
- Pages
- Widgets
- Features
- Entities
- Shared
You don't have to use every layer in your project β only add them if you think it brings value to your project.
Import rule on layersβ
Layers are made up of slices β highly cohesive groups of modules. Feature-Sliced Design promotes low coupling, which is why dependencies between slices are regulated by the import rule on layers:
A module in a slice can only import other slices when they are located on layers strictly below.
For example, in ~/features/aaa
, aaa
is the slice, so a file ~/features/aaa/api/request.ts
cannot import code from any module in ~/features/bbb
, but can import code from ~/entities
and ~/shared
, as well as any sibling code from ~/features/aaa
.
Layer definitionsβ
Sharedβ
Isolated modules, components and abstractions that are detached from the specifics of the project or business. Warning: not to be treated like a utility dump!
This layer, unlike others, does not consist of slices, and instead consists of segments directly.
Content examples:
- UI kit
- API client
- Code working with browser APIs
Entitiesβ
Concepts from the real world that form together the essence of the project. Commonly, these are the terms that the business uses to describe the product.
Each slice in this layer contains static UI elements, data stores and CRUD operations.
Slice examples:
For a social network | For a Git frontend (e.g., GitHub) |
---|---|
|
|
You may notice in the example of a Git frontend that a repository contains files. This makes the repository a higher-level entity which has other entities nested inside. That is a common situation with entities, and sometimes it's hard to manage such higher-level entities without breaking the import rule on layers.
Here are a few suggestions to overcome this issue:
- The UI of entities should contain slots for places where the lower-level entities are to be inserted
- The business logic related to entity interaction should be placed in features (most of the time)
- The typings of database entities can be extracted to the Shared layer below, next to the API client
Featuresβ
Actions that a user can make in the application to interact with the business entities to achieve a valuable outcome. This also includes actions that the app makes on behalf of the user to produce value for them.
Each slice in this layer can contain interactive UI elements, internal state and API calls that enable value-producing actions.
Slice examples:
For a social network | For a Git frontend (e.g., GitHub) | Actions on behalf of users |
---|---|---|
|
|
|
Widgetsβ
Self-sufficient UI blocks that emerged from the composition of lower-level units like entities and features.
This layer provides a way to fill in the slots left in the UI of Entities with other Entities and interactive elements from Features. Therefore, it is common not to have business logic on this layer, instead keeping it in Features. Each slice in this layer contains ready-to-use UI components and sometimes non-business logic such as gestures, keyboard interaction, etc.
Sometimes, however, it is more convenient to have business logic on this layer. Usually it happens when the widget is quite rich in interactivity (e.g., interactive data tables) and the business logic inside them is not used in other places.
Slice examples:
For a social network | For a Git frontend (e.g., GitHub) |
---|---|
|
|
If you're using a nested routing system (e.g. the router of Remix), it may be helpful to use the Widgets layer in the same way as a flat routing system would use the Pages layer β to create complete interface blocks, complete with related data fetching, loading states, and error boundaries. In the same way, you can store page layouts on this layer.
Pagesβ
Complete pages for a page-based application (like a website) or screens/activities for screen-based applications (like mobile apps).
This layer is similar to Widgets in its compositional nature, albeit on a larger scale. Each slice in this layer contains UI components that are ready to be plugged into a router and sometimes data-fetching logic and error handling.
Slice examples:
For a social network | For a Git frontend (e.g., GitHub) |
---|---|
|
|
Processesβ
This layer has been deprecated. The current version of the spec recommends avoiding it and moving its contents to features
and app
instead.
Escape hatches for multi-page interactions.
This layer is deliberately left undefined. Most applications should not use this layer, and keep router-level and server-level logic on the App layer. Consider using this layer only when the App layer grows large enough to become unmaintainable and needs unloading.
Appβ
All kinds of app-wide matters, both in the technical sense (e.g., context providers) and in the business sense (e.g., analytics).
This layer usually doesn't contain slices, like Shared, instead having segments directly.
Content examples:
- Styles
- Routing
- Store and other context providers
- Analytics initialization