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
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
,
, and
. Add
,
,
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:
text
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.
typescript
// ✅ 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
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 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: ).
Step 2 — Is it reusable infrastructure with no business logic?
- UI components →
- Utility functions →
- API client, route constants → or
- Auth tokens, session management →
- CRUD operations →
Step 3 — Is it a complete user action reused in 2+ places, and does the
team agree to extract it?
- Yes →
- 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 →
- Uncertain or single use → keep in the page.
Step 5 — Is it app-wide configuration?
- Global providers, router, theme →
Golden Rule: When in doubt, keep it in . 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
| |
| Product card | pages/products/ui/ProductCard.tsx
| entities/product/ui/ProductCard.tsx
|
| Product data fetching | pages/product-detail/api/fetch-product.ts
| |
| Auth token/session | (always) | (always) |
| Auth login form | pages/login/ui/LoginForm.tsx
| |
| CRUD operations | (always) | (always) |
| Generic Card layout | — | |
| 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
. Direct imports
of internal files are forbidden.
typescript
// ✅ 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 (
,
). 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
,
,
mix unrelated
domains in a single file and reduce cohesion.
text
// ❌ 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
or higher layers.
typescript
// ❌ 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
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
. When another
page needs the same user data and the team agrees, extract the shared model to
. 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. + + 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 and logic in the current slice's segment may
be sufficient.
- Place CRUD in . CRUD is infrastructure, not entities.
- Place auth data in or . Tokens and login
DTOs are auth-context-dependent and rarely reused outside authentication.
5-3. Start with minimal layers
text
// ✅ 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:
- : Suggests merging an entity/feature into its page
if only one page uses it.
- : Suggests merging or grouping when a layer has too
many slices.
bash
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 . Consider entities only
for complex transactional logic.
- Do not create a entity just for auth data. Tokens and login DTOs
belong in or .
- 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 into
, , ).
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 . 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 components, styles, display-related code
- — Data models, state stores, business logic, validation
- — Backend integration, request functions, API-specific types
- — Internal utility functions for this slice
- — 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:
text
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 kit (Button, Input, Modal, Card)
- — Utilities (formatDate, debounce, classnames)
- — API client, route constants, CRUD helpers, base types
- — Auth tokens, login utilities, session management
- — Environment variables, app settings
- — 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: + +
- 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 (, ). Never technical-role
(, ).
- 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.md
Note: If you already loaded
in this conversation,
avoid loading this file simultaneously. Address structure first, then load
patterns in a follow-up step if needed.