安装
npx skills add https://github.com/feature-sliced/skills --skill 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/
or
shared/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/api
and logic in the current slice's
model/
segment may
be sufficient.
Place CRUD in
shared/api/
.
CRUD is infrastructure, not entities.
Place auth data in
shared/auth/
or
shared/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
user
entity just for auth data.
Tokens and login DTOs
belong in
shared/auth/
or
shared/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/
into
auth/
,
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 code
model/
— Data models, state stores, business logic, validation
api/
— Backend integration, request functions, API-specific types
lib/
— Internal utility functions for this slice
config/
— 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 types
auth/
— Auth tokens, login utilities, session management
config/
— Environment variables, app settings
assets/
— 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.md
Note: If you already loaded
layer-structure.md
in this conversation,
avoid loading this file simultaneously. Address structure first, then load
patterns in a follow-up step if needed.
← 返回排行榜