feature-sliced-design
Feature-Sliced Design (FSD) v2.1
Source: fsd.how | Strictness can be adjusted based on project scale and team context.
1. Core Philosophy & Layer Overview
FSD v2.1 core principle: "Start simple, extract when needed."
Place code in pages/ first. Duplication across pages is acceptable and does
not automatically require extraction to a lower layer. Extract only when the
team agrees it is necessary.
Not all layers are required. Most projects can start with only shared/,
pages/, and app/. Add widgets/, features/, entities/ only when they
provide clear value. Do not create empty layer folders "just in case."
FSD uses 6 standardized layers with a strict top-down import direction:
app/ → App initialization, providers, routing
pages/ → Route-level composition, owns its own logic
widgets/ → Large composite UI blocks reused across multiple pages
features/ → Reusable user interactions (only when used in 2+ places)
entities/ → Reusable business domain models (only when used in 2+ places)
shared/ → Infrastructure with no business logic (UI kit, utils, API client)
Import rule: A module may only import from layers strictly below it. Cross-imports between slices on the same layer are forbidden.
// ✅ Allowed
import { Button } from "@/shared/ui/Button"; // features → shared
import { useUser } from "@/entities/user"; // pages → entities
// ❌ Violation
import { loginUser } from "@/features/auth"; // entities → features
import { likePost } from "@/features/like-post"; // features → features
Note: The processes/ layer is deprecated in v2.1. For migration
details, read references/migration-guide.md.
2. Decision Framework
When writing new code, follow this tree:
Step 1 — Where is this code used?
- Used in only one page → keep it in that
pages/slice. - Used in 2+ pages but duplication is manageable → keeping separate copies in each page is also valid.
- An entity or feature used in only one page → keep it in that page
(Steiger:
insignificant-slice).
Step 2 — Is it reusable infrastructure with no business logic?
- UI components →
shared/ui/ - Utility functions →
shared/lib/ - API client, route constants →
shared/api/orshared/config/ - Auth tokens, session management →
shared/auth/ - CRUD operations →
shared/api/
Step 3 — Is it a complete user action reused in 2+ places, and does the team agree to extract it?
- Yes →
features/ - Uncertain or single use → keep in the page.
Step 4 — Is it a business domain model reused in 2+ places, and does the team agree to extract it?
- Yes →
entities/ - Uncertain or single use → keep in the page.
Step 5 — Is it app-wide configuration?
- Global providers, router, theme →
app/
Golden Rule: When in doubt, keep it in pages/. Extract only when the
team agrees.
3. Quick Placement Table
| Scenario | Single use | Multi-use (with team agreement) |
|---|---|---|
| User profile form | pages/profile/ui/ProfileForm.tsx |
features/profile-form/ |
| Product card | pages/products/ui/ProductCard.tsx |
entities/product/ui/ProductCard.tsx |
| Product data fetching | pages/product-detail/api/fetch-product.ts |
entities/product/api/ |
| Auth token/session | shared/auth/ (always) |
shared/auth/ (always) |
| Auth login form | pages/login/ui/LoginForm.tsx |
features/auth/ |
| CRUD operations | shared/api/ (always) |
shared/api/ (always) |
| Generic Card layout | — | shared/ui/Card/ |
| Modal manager | — | shared/ui/modal-manager/ |
| Modal content | pages/[page]/ui/SomeModal.tsx |
— |
| Date formatting util | — | shared/lib/format-date.ts |
4. Architectural Rules (MUST)
These rules are the foundation of FSD. Violations weaken the architecture. If you must break a rule, ensure it is an intentional design decision — document the reason and obtain team consensus.
4-1. Import only from lower layers
app → pages → widgets → features → entities → shared.
Upward imports and cross-imports between slices on the same layer are
forbidden.
4-2. Public API — every slice exports through index.ts
External consumers may only import from a slice's index.ts. Direct imports
of internal files are forbidden.
// ✅ Correct
import { LoginForm } from "@/features/auth";
// ❌ Violation — bypasses public API
import { LoginForm } from "@/features/auth/ui/LoginForm";
RSC / meta-framework exception: In environments with distinct client/server
boundaries, split entry points are permitted (index.client.ts,
index.server.ts). See references/framework-integration.md for details.
4-3. No cross-imports between slices on the same layer
If two slices on the same layer need to share logic, follow the resolution order in Section 7. Do not create direct imports.
4-4. Domain-based file naming (no desegmentation)
Name files after the business domain they represent, not their technical role.
Technical-role names like types.ts, utils.ts, helpers.ts mix unrelated
domains in a single file and reduce cohesion.
// ❌ Technical-role naming
model/types.ts ← Which types? User? Order? Mixed?
model/utils.ts
// ✅ Domain-based naming
model/user.ts ← User types + related logic
model/order.ts ← Order types + related logic
api/fetch-profile.ts ← Clear purpose
4-5. No business logic in shared/
Shared contains only infrastructure: UI kit, utilities, API client setup,
route constants, assets. Business calculations, domain rules, and workflows
belong in entities/ or higher layers.
// ❌ Business logic in shared
// shared/lib/userHelpers.ts
export const calculateUserReputation = (user) => { ... };
// ✅ Move to the owning domain
// entities/user/lib/reputation.ts
export const calculateUserReputation = (user) => { ... };
5. Recommendations (SHOULD)
5-1. Pages First — place code where it is used
Place code in pages/ first. Extract to lower layers only when truly needed.
When extraction seems worthwhile, discuss with the team — this is a design
decision that affects the whole project.
What stays in pages:
- Large UI blocks used only in one page
- Page-specific forms, validation, data fetching, state management
- Page-specific business logic and API integrations
- Code that looks reusable but is simpler to keep local
Evolution pattern: Start with everything in pages/profile/. When another
page needs the same user data and the team agrees, extract the shared model to
entities/user/. Keep page-specific API calls and UI in the page.
5-2. Be conservative with entities
The entities layer is highly accessible — almost every other layer can import from it, so changes propagate widely.
- Start without entities.
shared/+pages/+app/is valid FSD. Thin-client apps rarely need entities. - Do not split slices prematurely. Keep code in pages. Extract to entities only when 2+ consumers are confirmed and the team agrees.
- Business logic does not automatically require an entity. Keeping types
in
shared/apiand logic in the current slice'smodel/segment may be sufficient. - Place CRUD in
shared/api/. CRUD is infrastructure, not entities. - Place auth data in
shared/auth/orshared/api/. Tokens and login DTOs are auth-context-dependent and rarely reused outside authentication.
5-3. Start with minimal layers
// ✅ Valid minimal FSD project
src/
app/ ← Providers, routing
pages/ ← All page-level code
shared/ ← UI kit, utils, API client
// Add layers only when the team decides they are needed:
// + widgets/ ← UI blocks reused in 2+ pages
// + features/ ← User interactions reused in 2+ pages
// + entities/ ← Domain models reused in 2+ pages/features
5-4. Validate with the Steiger linter
Steiger is the official FSD linter. Key rules:
insignificant-slice: Suggests merging an entity/feature into its page if only one page uses it.excessive-slicing: Suggests merging or grouping when a layer has too many slices.
npm install -D @feature-sliced/steiger
npx steiger src
6. Anti-patterns (AVOID)
- Do not create entities prematurely. Data structures used in only one place belong in that place.
- Do not put CRUD in entities. Use
shared/api/. Consider entities only for complex transactional logic. - Do not create a
userentity just for auth data. Tokens and login DTOs belong inshared/auth/orshared/api/. - Do not abuse @x. It is a last resort, not a recommended pattern (see Section 7).
- Do not extract single-use code. A feature or entity used by only one page should stay in that page.
- Do not use technical-role file names. Use domain-based names (see Rule 4-4).
- Be cautious adding UI to entities. Entity UI tempts cross-imports from other entities. If you add UI segments to entities, only import them from higher layers (features, widgets, pages) — never from other entities.
- Do not create god slices. Slices with excessively broad responsibilities
should be split into focused slices (e.g., split
user-management/intoauth/,profile-edit/,password-reset/).
7. Cross-Import Resolution Order
When two slices on the same layer need to share code, try these strategies in order. Always attempt earlier strategies first.
- Merge slices — If two slices always change together, they likely belong in one slice.
- Extract shared logic to entities — If multiple features/widgets share
domain logic, move that logic to
entities/. Keep UI in features/widgets. - Compose in a higher layer (IoC) — Use inversion of control. The parent layer (pages or app) imports both slices and connects them via render props, slots, or dependency injection.
- @x notation (last resort) — Create explicit, controlled cross-imports between entities only. Document why other strategies do not apply. Review periodically.
For detailed code examples of each strategy, read
references/cross-import-patterns.md.
8. Segments & Structure Rules
Standard segments
Segments group code within a slice by technical purpose:
ui/— UI components, styles, display-related codemodel/— Data models, state stores, business logic, validationapi/— Backend integration, request functions, API-specific typeslib/— Internal utility functions for this sliceconfig/— Configuration, feature flags
Layer structure rules
- App and Shared: No slices — organized directly by segments. Segments within these layers may import from each other.
- Pages, Widgets, Features, Entities: Slices first, then segments inside each slice.
File naming within segments
Always use domain-based names that describe what the code is about:
model/user.ts ← User types + logic + store
model/order.ts ← Order types + logic + store
api/fetch-profile.ts ← Profile fetching
api/update-settings.ts ← Settings update
If a segment has only one domain concern, the filename may match the slice
name (e.g., features/auth/model/auth.ts).
9. Shared Layer Guide
Shared contains infrastructure with no business logic. It is organized by segments only (no slices). Segments within shared may import from each other.
Allowed in shared:
ui/— UI kit (Button, Input, Modal, Card)lib/— Utilities (formatDate, debounce, classnames)api/— API client, route constants, CRUD helpers, base typesauth/— Auth tokens, login utilities, session managementconfig/— Environment variables, app settingsassets/— Images, fonts, icons
Shared may contain application-aware code (route constants, API endpoints, branding assets, common types). It must never contain business logic, feature-specific code, or entity-specific code.
10. Quick Reference
- Import direction:
app → pages → widgets → features → entities → shared - Minimal FSD:
app/+pages/+shared/ - Create entities when: 2+ pages/features/widgets share the same business domain model, and the team agrees.
- Create features when: 2+ pages/widgets share the same user interaction, and the team agrees.
- Breaking rules: Only as an intentional design choice — document the reason, get team consensus.
- Cross-import resolution: Merge → Extract to entities → Compose in higher layer → @x (last resort)
- File naming: Domain-based (
user.ts,order.ts). Never technical-role (types.ts,utils.ts). - Processes layer: Deprecated. See
references/migration-guide.md.
11. Conditional References
Read the following reference files only when the specific situation applies. Do not preload all references.
-
When creating, reviewing, or reorganizing folder and file structure for FSD layers and slices (e.g., "set up project structure", "where does this folder go"): → Read
references/layer-structure.md -
When resolving cross-import issues between slices on the same layer, evaluating the @x pattern, or dealing with excessive entity coupling: → Read
references/cross-import-patterns.md -
When migrating from FSD v2.0 to v2.1, converting a non-FSD codebase to FSD, or deprecating the processes layer: → Read
references/migration-guide.md -
When integrating FSD with a specific framework (Next.js, Nuxt, Vite, CRA) or configuring path aliases: → Read
references/framework-integration.md -
When implementing concrete code patterns for authentication, API request handling, type definitions, or state management (Redux, React Query) within FSD structure: → Read
references/practical-examples.mdNote: If you already loadedlayer-structure.mdin this conversation, avoid loading this file simultaneously. Address structure first, then load patterns in a follow-up step if needed.