@ccheney/feature-slicing

Proactively apply when creating new features/components/pages or setting up frontend project structure. Triggers on FSD, feature slicing, frontend architecture, layer structure, module boundaries, scalable frontend, slice organization. Use when restructuring React/Next.js/Vue/Remix projects, organizing frontend code, fixing import violations, or migrating legacy codebases. Feature-Sliced Design (FSD) architecture for frontend projects.

View in AI SkillSafe app
Scanned · no findings
0 downloads
0 stars
0 demos
SKILL.md
namefeature-slicing
descriptionProactively apply when creating new features/components/pages or setting up frontend project structure. Triggers on FSD, feature slicing, Feature-Sliced Design, frontend architecture, layer structure, module boundaries, scalable frontend, slice organization, public API, barrel exports, import rules, Steiger. Use when restructuring React/Next.js/Vue/Remix projects, organizing frontend code, fixing import violations, deciding where code belongs (entity vs feature vs widget vs shared), or migrating legacy codebases. Feature-Sliced Design (FSD) architecture for frontend projects.

Feature-Sliced Design Architecture

Frontend architecture methodology (spec v2.1) with a strict layer hierarchy and one import rule. FSD organizes code by business domain rather than technical role.

Official Docs: feature-sliced.design | GitHub: feature-sliced


THE IMPORT RULE (Critical)

A module in a slice may only import slices from layers strictly below. Never sideways or upward. This is what keeps slices replaceable and dependencies traceable — every violation you allow becomes an invisible coupling someone else trips over.

app → pages → widgets → features → entities → shared
 ↓      ↓        ↓          ↓          ↓         ✓
 ✓      ✓        ✓          ✓          ✓      (external only)
Violation Example Fix
Cross-slice (same layer) features/authfeatures/user-profile Move shared code to a lower layer, or compose both in the page/widget above
Upward import entities/userfeatures/auth Move the needed code down
Shared importing up shared/entities/ Shared depends only on external packages
Cross-entity entities/orderentities/product internals Use @x notation (entities layer only)

Exception: app/ and shared/ are each "a layer and a slice at the same time" — they divide directly into segments, and their segments may freely import each other.


Layer Hierarchy

Layer Purpose Has Slices Required
app/ Initialization, routing, providers, global styles No (segments only) Yes
pages/ Route-based screens — the default home for most code Yes Yes
widgets/ Large self-sufficient UI blocks reused across pages; may own their data fetching and logic Yes No
features/ User interactions reused across pages (login, add-to-cart) Yes No
entities/ Business domain models reused across pages (user, product) Yes No
shared/ Reused infrastructure: UI kit, API client, route constants, utilities. No knowledge of domain slices — but it may be app-aware (your backend's client, your route paths) No (segments only) Yes

The processes/ layer is deprecated — move its contents to features/ and app/.

Minimal setup: app/, pages/, shared/. A small app with 5 routes does not need entities, features, or widgets — adding them "for completeness" spreads single-use code across the tree and negates FSD's cohesion benefit. Add a layer only when something is genuinely reused by 2+ pages.


Pages First (v2.1 default)

When unsure where code goes, put it in the page slice that uses it — including forms, data logic, and large UI blocks. Extract downward only when a second page needs it. Why: page-local code is found instantly by newcomers; premature entities/features force jumping through several folders to change one user flow.

Where does this code go?
├─ Used by exactly ONE page              → that page's slice (even logic/forms)
├─ App-wide config, providers, routing   → app/
├─ Domain-agnostic or infra code         → shared/
├─ Reused across pages:
│  ├─ Large UI block with own logic      → widgets/
│  ├─ User action (verb)                 → features/
│  └─ Domain model/data (noun)           → entities/

"Feature or Entity?"

Entity (noun) Feature (verb)
user — user data model auth — login/logout actions
product — product info add-to-cart — adding to cart
comment — comment data write-comment — creating comments

Entities represent THINGS with identity. Features represent ACTIONS with side effects. Not everything is a feature — an interaction used on one page stays in that page.

"Which segment?"

Segments divide a slice by technical purpose:

├─ ui/      → components, styles, formatters
├─ api/     → backend calls, DTOs, mappers
├─ model/   → types, schemas, stores, business logic
├─ lib/     → slice-internal utilities
└─ config/  → feature flags, constants

Name segments by purpose, not essence: api/, model/, lib/ — never hooks/, components/, types/, utils/. Purpose names tell a reader what the code is for; essence names just restate the file extension.


Directory Structure

src/
├── app/                    # Layer + slice: segments only
│   ├── providers/          # React context, QueryClient, theme
│   ├── routes/             # Router configuration
│   └── styles/             # Global CSS, theme tokens
├── pages/
│   └── {page-name}/
│       ├── ui/             # Page component + page-local blocks
│       ├── api/            # Loaders, server actions
│       ├── model/          # Page-specific state
│       └── index.ts        # Public API
├── widgets/
│   └── {widget-name}/
│       ├── ui/
│       ├── api/            # Widgets may fetch their own data (v2.1)
│       └── index.ts
├── features/
│   └── {feature-name}/
│       ├── ui/
│       ├── api/
│       ├── model/
│       └── index.ts
├── entities/
│   └── {entity-name}/
│       ├── ui/             # Entity UI (Card, Avatar)
│       ├── api/            # CRUD operations
│       ├── model/          # Types, mappers, validation
│       └── index.ts
└── shared/                 # Layer + slice: segments only, no root index
    ├── ui/                 # Design system (index per component)
    ├── api/                # API client, interceptors
    ├── lib/                # Utilities (dates, validation)
    ├── config/             # Environment, constants
    ├── routes/             # Route path constants
    └── i18n/               # Translations

Public API Pattern

Every slice exposes ONE public API via index.ts. External code imports only from it — the index is the contract that lets a slice refactor its internals freely.

// entities/user/index.ts
export { UserCard } from './ui/UserCard';
export { getUser, updateUser } from './api/userApi';
export type { User, UserRole } from './model/types';
// ✅ Correct
import { UserCard, type User } from '@/entities/user';

// ❌ Wrong — reaches into internals
import { UserCard } from '@/entities/user/ui/UserCard';

Three rules that prevent the common failure modes:

  1. Explicit named exports, no wildcards. export * from './ui' hides the contract, leaks internals, and harms tree-shaking.
  2. No segment-level index files on sliced layers. If features/comments/index.ts exists, don't also create features/comments/ui/index.ts — extra barrels slow bundlers and invite circular imports. Inside a slice, use relative imports; never import your own index.ts.
  3. On shared/, the public API lives per segment (shared/api/index.ts), and for shared/ui/shared/lib, per component (shared/ui/button/index.ts) — one giant barrel bundles a syntax highlighter into every page.

Details and edge cases: references/PUBLIC-API.md.


Cross-Entity References (@x Notation)

Entities sometimes genuinely contain each other (an order contains products). Instead of a hidden cross-import, make it explicit with @x — used only on the entities layer:

entities/
├── product/
│   ├── @x/
│   │   └── order.ts    # exports intended specifically for the order entity
│   └── index.ts
└── order/
    └── model/types.ts  # imports from product/@x/order
// entities/product/@x/order.ts
export type { ProductId } from '../model/types';

// entities/order/model/types.ts
import type { ProductId } from '@/entities/product/@x/order';

Keep @x files tiny (usually type re-exports). If two entities cross-reference extensively, merge them.


Anti-Patterns

Anti-Pattern Problem Fix
Business logic in shared/ shared/lib/calculateDiscount.ts knows domain rules Move to the entity/feature/page that owns it; shared holds infra only
Cross-slice import (same layer) Invisible coupling between siblings Extract down, use @x (entities), or compose in the layer above
Over-slicing a small app 6 layers, 20 slices, every slice used once Pages-first: keep code in page slices; layers earn their existence via reuse
Everything is a feature features/close-modal Only reused, business-meaningful interactions
Single-use widgets Widget used by one page Keep in that page's ui/
Generic segments components/, hooks/, utils/ Purpose names: ui/, model/, lib/
Wildcard exports export * from './ui' Explicit named exports
Bypassing public API Deep imports into a slice Import from the slice index.ts

Tooling

Verify architecture automatically with Steiger, the official FSD linter:

npm i -D steiger @feature-sliced/steiger-plugin
npx steiger ./src            # add --watch during refactors

It catches cross-imports, missing public APIs, and single-use slices (fsd/insignificant-slice). ESLint alternative: @feature-sliced/eslint-config.

Path alias (required for @/entities/user-style imports):

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["./src/*"] }
  }
}

Reference Documentation

Read this When
references/LAYERS.md Deciding which layer code belongs to; full layer specs and flowchart
references/PUBLIC-API.md Writing index files, fixing circular imports, tree-shaking, @x details
references/IMPLEMENTATION.md Writing actual code: complete entity/feature/widget/page examples with React Query, Zustand, Zod
references/NEXTJS.md Any Next.js project — the app/pages folder conflict, _app/_pages renaming, server/client public APIs
references/MIGRATION.md Restructuring an existing codebase to FSD incrementally
references/CHEATSHEET.md Quick lookup: import matrix, structure templates

Resources

Embed badges

Add these to your README to show the skill's verification status.

SkillSafe verified badge
Verified badge
[![SkillSafe verified badge](https://api.skillsafe.ai/v1/badge/@ccheney/feature-slicing/verified)](https://skillsafe.ai/skill/@ccheney/feature-slicing/)
Installs badge
Installs badge
[![Installs badge](https://api.skillsafe.ai/v1/badge/@ccheney/feature-slicing/installs)](https://skillsafe.ai/skill/@ccheney/feature-slicing/)
Scan badge
Scan badge
[![Scan badge](https://api.skillsafe.ai/v1/badge/@ccheney/feature-slicing/scan)](https://skillsafe.ai/skill/@ccheney/feature-slicing/)
Eval pass rate badge
Eval pass rate
[![Eval pass rate badge](https://api.skillsafe.ai/v1/badge/@ccheney/feature-slicing/eval)](https://skillsafe.ai/skill/@ccheney/feature-slicing/)