skills/feature-sliced/skills/feature-sliced-design

feature-sliced-design

SKILL.md

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.

  1. Start without entities. shared/ + pages/ + app/ is valid FSD. Thin-client apps rarely need entities.
  2. Do not split slices prematurely. Keep code in pages. Extract to entities only when 2+ consumers are confirmed and the team agrees.
  3. 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.
  4. Place CRUD in shared/api/. CRUD is infrastructure, not entities.
  5. 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.

  1. Merge slices — If two slices always change together, they likely belong in one slice.
  2. Extract shared logic to entities — If multiple features/widgets share domain logic, move that logic to entities/. Keep UI in features/widgets.
  3. 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.
  4. @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.

Weekly Installs
59
GitHub Stars
6
First Seen
7 days ago
Installed on
opencode59
gemini-cli59
github-copilot59
codex59
amp59
cline59