--- name: feature-sliced-design description: Apply Feature-Sliced Design (FSD) v2.1 architectural methodology to frontend projects. Use when organizing code structure, decomposing features, creating new components or features, refactoring existing codebases, or when users mention "FSD", "Feature-Sliced", layers, slices, or frontend architecture patterns. --- # Feature-Sliced Design (FSD) Skill - v2.1.0 An architectural methodology skill for scaffolding and organizing frontend applications using Feature-Sliced Design principles. ## Overview Feature-Sliced Design v2.1 is a compilation of rules and conventions for organizing frontend code to make projects more understandable, maintainable, and stable in the face of changing business requirements. **Version 2.1 introduces the "Pages First" approach** - keeping more code in pages and widgets rather than prematurely extracting it to features and entities. ## Core Principles ### The "Pages First" Approach (FSD v2.1) **The fundamental principle of FSD v2.1: Keep code where it's used until you need to reuse it.** Instead of immediately extracting everything into entities and features, start by keeping code in pages and widgets. Only move code to lower layers when you actually need to reuse it. #### What stays in Pages and Widgets: ✅ **Large UI blocks** that are only used on one page ✅ **Forms and their validation logic** specific to a page ✅ **Data fetching and state management** for page-specific data ✅ **Business logic** that serves only this page/widget ✅ **API interactions** needed only here #### When to extract to lower layers: - **To Shared**: When you need the same *infrastructure* in multiple places (modal manager, date formatter, UI components) - **To Entities**: When you have a clear *business domain model* that's used across multiple features - **To Features**: When you have a complete *user interaction* that's reused in multiple places #### Why "Pages First"? 1. **Better code cohesion** - related code stays together 2. **Easier to delete** - unused code is right there with its usage 3. **Less abstraction overhead** - no need to identify entities/features prematurely 4. **Natural decomposition** - pages are intuitive to understand 5. **Faster development** - no time wasted on premature optimization ### 1. Layered Architecture (Vertical Organization) FSD uses **6 active standardized layers** organized by responsibility and dependencies. Layers are ordered from most specific (top) to most generic (bottom): ``` app/ ← Application initialization, providers, global styles pages/ ← Full page compositions with their own logic, routing widgets/ ← Large composite UI blocks with their own logic features/ ← Reusable user interactions and business features entities/ ← Reusable business entities (user, product, order) shared/ ← Reusable infrastructure code (UI kit, utils, API) ``` **Note**: Historically, FSD included `processes/` as a 7th layer, but it is **deprecated** in v2.1. If you're using it, move the code to `features/` with help from `app/` if needed. **Import Rule**: A module can only import from layers **strictly below** it. - ✅ `features/` → `entities/`, `shared/` - ✅ `pages/` → `widgets/`, `features/`, `entities/`, `shared/` - ❌ `entities/` → `features/` (upward import) - ❌ `features/comments/` → `features/posts/` (same-layer cross-import) ### 2. Slices (Horizontal Organization) Slices group code by **business domain meaning**. Each slice represents a specific business concept: ``` features/ ├── auth/ ← Authentication feature ├── comments/ ← Comments functionality └── post-editor/ ← Post editing feature entities/ ├── user/ ← User business entity ├── product/ ← Product business entity └── order/ ← Order business entity ``` **Key Rules**: - Slices must be **independent** from other slices on the same layer (zero coupling) - Slices should contain **most code related to their primary goal** (high cohesion) - Slice names are **not standardized** - they reflect your business domain ### 3. Segments (Technical Organization) Segments group code within slices by **technical purpose**: ``` features/ └── auth/ ├── ui/ ← React components, styles, formatters ├── api/ ← API requests, data types, mappers ├── model/ ← State management, business logic, stores ├── lib/ ← Internal utilities for this slice ├── config/ ← Configuration, feature flags └── index.ts ← Public API (exports only what other slices need) ``` **Standard Segments**: - `ui` - UI components, styles, date formatters - `api` - Backend interactions, request functions, data types - `model` - Data models, state stores, business logic - `lib` - Utility functions needed by this slice - `config` - Configuration files, feature flags ### 4. Public API Every slice must define a **public API** through an index file: ```typescript // features/auth/index.ts export { LoginForm } from './ui/LoginForm'; export { useAuth } from './model/useAuth'; export { loginUser } from './api/loginUser'; // Internal files not exported remain private to the slice ``` **Rule**: Modules outside a slice can **only import from the public API**, not from internal files. #### Public API for Cross-Imports (@x notation) **New in v2.1**: You can now create explicit connections between slices on the same layer (typically entities) using the `@x` notation. This allows entities to reference each other when there's a legitimate business relationship: ```typescript // entities/user/index.ts export { UserCard } from './ui/UserCard'; export { userModel } from './model'; // entities/user/@x/order.ts // Cross-import API specifically for the order entity export { UserOrderHistory } from './ui/UserOrderHistory'; export { getUserOrders } from './api/getUserOrders'; // entities/order/index.ts import { UserOrderHistory } from '@/entities/user/@x/order'; // Now order can import from user's cross-import API ``` **When to use cross-imports**: - There's a clear business relationship between entities (e.g., User and Order) - The dependency is bidirectional or circular in the business domain - You want to keep the code together while acknowledging the relationship **Important**: Regular cross-imports between slices (without `@x`) are still not allowed. Use `@x` notation to make cross-dependencies explicit and controlled. ## Layer Definitions & Examples ### App Layer Application-wide settings, providers, routing setup. ``` app/ ├── providers/ ← Redux Provider, React Query, Theme Provider ├── styles/ ← Global CSS, resets, theme variables ├── index.tsx ← Application entry point └── router.tsx ← Route configuration ``` ### Pages Layer Route-level compositions with their own logic and data management. ``` pages/ ├── home/ │ ├── ui/ │ │ ├── HomePage.tsx │ │ ├── HeroSection.tsx ← Large UI blocks │ │ └── FeaturesGrid.tsx │ ├── model/ │ │ └── useHomeData.ts ← Page-specific state │ ├── api/ │ │ └── fetchHomeData.ts ← Page-specific API │ └── index.ts ├── profile/ │ ├── ui/ │ │ ├── ProfilePage.tsx │ │ ├── ProfileForm.tsx ← Forms specific to this page │ │ └── ProfileStats.tsx │ ├── model/ │ │ ├── profileStore.ts ← State for profile page │ │ └── validation.ts ← Form validation │ ├── api/ │ │ ├── updateProfile.ts │ │ └── fetchProfile.ts │ └── index.ts └── settings/ ``` **v2.1 Approach**: Pages can now contain: - ✅ Large UI blocks used only on this page - ✅ Forms and their validation logic - ✅ Data fetching and state management - ✅ Business logic that serves only this page - ✅ API interactions specific to this page **Only extract to lower layers when you need to reuse the code elsewhere.** ### Widgets Layer Complex, composite UI blocks with their own logic, used across multiple pages. ``` widgets/ ├── header/ │ ├── ui/ │ │ ├── Header.tsx │ │ ├── Navigation.tsx │ │ └── UserMenu.tsx │ ├── model/ │ │ └── headerStore.ts ← Widget state │ ├── api/ │ │ └── fetchNotifications.ts ← Widget-specific API │ └── index.ts ├── sidebar/ │ ├── ui/ │ ├── model/ │ │ └── sidebarState.ts │ └── index.ts └── footer/ ``` **v2.1 Approach**: Widgets are no longer just compositional blocks. They can contain: - ✅ UI components of the widget - ✅ Widget-specific state management - ✅ Business logic that serves the widget - ✅ API interactions the widget needs - ✅ Internal utilities **Only extract code to entities/features when other widgets or pages need it.** ### Features Layer **Reusable user interactions** and complete business features used in multiple places. ``` features/ ├── auth/ │ ├── ui/ │ │ ├── LoginForm.tsx │ │ └── RegisterForm.tsx │ ├── model/ │ │ └── useAuth.ts │ ├── api/ │ │ ├── login.ts │ │ └── register.ts │ └── index.ts ├── add-to-cart/ ├── like-post/ └── comment-create/ ``` **v2.1 Approach**: Only create a feature when: - ✅ The user interaction is used on **multiple pages/widgets** - ✅ It's a complete, self-contained user action - ✅ It has clear business value **Don't create features prematurely.** If a user interaction is only used in one place, keep it in the page or widget until you actually need to reuse it. ### Entities Layer **Reusable business entities** - the core domain models used across the application. ``` entities/ ├── user/ │ ├── ui/ │ │ ├── UserCard.tsx │ │ └── UserAvatar.tsx │ ├── model/ │ │ ├── types.ts │ │ └── userStore.ts │ ├── api/ │ │ └── userApi.ts │ ├── @x/ │ │ └── order.ts ← Cross-import API for order │ └── index.ts ├── product/ └── order/ ``` **v2.1 Approach**: Only create an entity when: - ✅ It represents a clear **business domain concept** - ✅ It's used in **multiple features, pages, or widgets** - ✅ It has well-defined boundaries and responsibilities **Don't prematurely extract entities.** If a data structure is only used in one place, keep it there until you need to share it. ### Shared Layer Reusable infrastructure code with **no business logic**. ``` shared/ ├── ui/ ← UI kit components │ ├── Button/ │ ├── Input/ │ └── Modal/ ├── lib/ ← Utilities │ ├── formatDate/ │ ├── debounce/ │ └── classnames/ ├── api/ ← API client setup, base config │ ├── client.ts │ └── apiRoutes.ts ← Route constants (v2.1: allowed!) ├── config/ ← Environment variables, constants │ ├── env.ts │ └── appConfig.ts ├── assets/ ← Images, fonts, icons │ ├── logo.svg ← Company logo (v2.1: allowed!) │ └── icons/ └── types/ ← Common TypeScript types ``` **v2.1 Update**: Shared can now contain **application-aware** code: - ✅ Route constants and path builders - ✅ API endpoint definitions - ✅ Company branding assets (logos, colors) - ✅ Application configuration - ✅ Common type definitions **Still not allowed**: - ❌ Business logic (calculations, workflows, domain rules) - ❌ Feature-specific code - ❌ Entity-specific code **No slices in Shared** - organized by segments only. Segments can import from each other within Shared. ## Common Patterns ### 1. Working with API (Pages First Approach) **Start simple** - keep API logic in the page until you need to reuse it: ```typescript // pages/profile/api/fetchProfile.ts export const fetchProfile = (id: string) => apiClient.get(`/users/${id}`); // pages/profile/model/useProfile.ts export const useProfile = (id: string) => { const [user, setUser] = useState(null); useEffect(() => { fetchProfile(id).then(setUser); }, [id]); return user; }; // pages/profile/ui/ProfilePage.tsx import { useProfile } from '../model/useProfile'; const ProfilePage = () => { const user = useProfile('123'); return
{user?.name}
; }; ``` **Only move to entities when other pages need the same API:** ```typescript // entities/user/api/userApi.ts export const fetchUser = (id: string) => apiClient.get(`/users/${id}`); // entities/user/model/userStore.ts export const useUser = (id: string) => { const [user, setUser] = useState(null); useEffect(() => { fetchUser(id).then(setUser); }, [id]); return user; }; // entities/user/index.ts export { useUser } from './model/userStore'; export type { User } from './model/types'; // Now multiple pages can use it: // pages/profile/ui/ProfilePage.tsx // pages/user-list/ui/UserListPage.tsx import { useUser } from '@/entities/user'; ``` ### 2. Feature Composition Features can use entities and other features: ```typescript // features/post-card/ui/PostCard.tsx import { UserAvatar } from '@/entities/user'; import { LikeButton } from '@/features/like-post'; import { CommentButton } from '@/features/comment-create'; export const PostCard = ({ post }) => (

{post.title}

{post.content}

); ``` ### 3. Handling Routes Routes should be defined in the App layer, pages composed in Pages layer: ```typescript // app/router.tsx import { HomePage } from '@/pages/home'; import { ProfilePage } from '@/pages/profile'; export const router = createBrowserRouter([ { path: '/', element: }, { path: '/profile/:id', element: }, ]); // app/index.tsx import { RouterProvider } from 'react-router-dom'; import { router } from './router'; export const App = () => ; ``` ### 4. Shared UI Components ```typescript // shared/ui/Button/Button.tsx export const Button = ({ children, onClick, variant = 'primary' }) => ( ); // shared/ui/Button/index.ts export { Button } from './Button'; export type { ButtonProps } from './Button'; // Usage in feature import { Button } from '@/shared/ui/Button'; const LoginForm = () => (
); ``` ## Path Aliases Use path aliases for clean imports: ```json // tsconfig.json { "compilerOptions": { "baseUrl": ".", "paths": { "@/app/*": ["src/app/*"], "@/pages/*": ["src/pages/*"], "@/widgets/*": ["src/widgets/*"], "@/features/*": ["src/features/*"], "@/entities/*": ["src/entities/*"], "@/shared/*": ["src/shared/*"] } } } ``` ## Decision Framework (v2.1 "Pages First") ### When creating new code, follow this decision tree: 1. **Start with: "Where is this code used?"** - Used in only one page? → Keep it in that **page/** - Used in one widget across multiple pages? → Keep it in that **widget/** - Used across multiple pages/widgets? → Continue to question 2 2. **Is it reusable infrastructure?** - UI component with no business logic? → **shared/ui/** - Utility function (date formatting, etc.)? → **shared/lib/** - API client setup, route constants? → **shared/api/** or **shared/config/** - If yes to any → **shared/** - If no → Continue to question 3 3. **Is it a complete user action?** - User interaction (login, add to cart, like post)? → **features/** - But only if it's **reused in multiple places**! 4. **Is it a business domain concept?** - Core business entity (user, product, order)? → **entities/** - But only if it's **reused in multiple places**! 5. **Is it app-wide setup?** - Global provider, router, theme? → **app/** ### Quick Decision Examples: **"User profile form with validation"** - Used only on profile page? → `pages/profile/ui/ProfileForm.tsx` - Used on profile + settings pages? → Consider `features/profile-form/` - Still not sure? → Start in page, extract when you actually need it elsewhere **"Product card component"** - Shows on product list page only? → `pages/products/ui/ProductCard.tsx` - Shows on multiple pages? → `widgets/product-card/` or `entities/product/ui/ProductCard.tsx` - Generic card layout? → `shared/ui/Card/` **"Fetch product data"** - Only product detail page needs it? → `pages/product-detail/api/fetchProduct.ts` - Multiple pages need it? → `entities/product/api/productApi.ts` **"Modal manager"** - Infrastructure for showing modals? → `shared/ui/modal-manager/` - Content of specific modals? → Keep in pages that use them ### The Golden Rule: **"When in doubt, keep it in pages/widgets. Extract to lower layers when you actually need to reuse it."** Don't try to predict reusability. Wait for actual reuse to emerge, then refactor. ## Anti-Patterns to Avoid ❌ **Premature extraction** (v2.1 key anti-pattern) ```typescript // Immediately creating an entity/feature before knowing if it's needed // entities/user-profile-form/ ← Used only on one page! ``` ✅ **Solution**: Keep in page until actually needed elsewhere ```typescript // pages/profile/ui/ProfileForm.tsx ← Start here // Only move to features/ when another page needs it ``` ❌ **Cross-imports between slices on same layer** ```typescript // features/comments/ui/CommentList.tsx import { likePost } from '@/features/like-post'; // BAD! ``` ✅ **Solution**: Use @x notation for entities, or compose at higher layer ```typescript // For entities with business relationships: // entities/user/@x/order.ts export { UserOrderHistory } from './ui/UserOrderHistory'; // For features, compose at page level: // pages/post/ui/PostPage.tsx import { CommentList } from '@/features/comments'; import { LikeButton } from '@/features/like-post'; ``` ❌ **Business logic in Shared** ```typescript // shared/lib/userHelpers.ts export const calculateUserReputation = (user) => { ... }; // BAD! ``` ✅ **Solution**: Move to entities layer ```typescript // entities/user/lib/calculateReputation.ts export const calculateUserReputation = (user) => { ... }; ``` ❌ **Bypassing public API** ```typescript import { LoginButton } from '@/features/auth/ui/LoginButton'; // BAD! ``` ✅ **Use public API** ```typescript import { LoginButton } from '@/features/auth'; // GOOD! ``` ❌ **God slices** (too much responsibility) ```typescript // features/user-management/ ← TOO BROAD // - login, register, profile-edit, password-reset, etc. ``` ✅ **Split into focused features** ```typescript // features/auth/ // features/profile-edit/ // features/password-reset/ ``` ## Migration from FSD v2.0 to v2.1 If you have an existing FSD 2.0 project, migration to 2.1 is **non-breaking**. You can adopt the "pages first" approach gradually. ### Migration Steps: 1. **Audit current features and entities** - Which features/entities are used in only one place? - Mark them for potential moving to pages/widgets 2. **Move page-specific code back to pages** - Forms used on one page → `pages/[page]/ui/` - Page-specific API calls → `pages/[page]/api/` - Page-specific state → `pages/[page]/model/` 3. **Move widget-specific code to widgets** - Logic only used in one widget → keep in that widget - Don't extract to features prematurely 4. **Keep truly reusable code in features/entities** - Used in 2+ places → stays in features/entities - Clear business value → stays in features/entities 5. **Update Shared with application-aware code** - Move route constants → `shared/api/routes.ts` - Move company assets → `shared/assets/` - Keep it free of business logic 6. **Deprecate Processes layer** - Move code to features/ with help from app/ if needed 7. **Consider using @x notation** - For entities with bidirectional relationships - Makes cross-dependencies explicit ### Example Migration: **Before (v2.0):** ``` features/ └── user-profile-form/ ← Only used on profile page ├── ui/ ├── model/ └── api/ pages/ └── profile/ └── ui/ └── ProfilePage.tsx ← Just composition ``` **After (v2.1):** ``` pages/ └── profile/ ├── ui/ │ ├── ProfilePage.tsx │ └── ProfileForm.tsx ← Moved here ├── model/ │ └── profileStore.ts ← Moved here └── api/ └── updateProfile.ts ← Moved here ``` ## Migration Strategy When migrating existing code to FSD: 1. **Start with Shared**: Move UI kit, utils, API client to `shared/` 2. **Identify Entities**: Extract business domain models to `entities/` 3. **Extract Features**: Isolate user interactions to `features/` 4. **Create Pages**: Compose pages from widgets and features 5. **Setup App**: Move global providers and routing to `app/` Do it **gradually** - you don't need to refactor everything at once. ## Working with Different Technologies ### React + Redux ``` features/ └── todo-list/ ├── ui/ │ └── TodoList.tsx ├── model/ │ ├── todoSlice.ts ← Redux slice │ ├── selectors.ts ← Selectors │ └── thunks.ts ← Async actions └── index.ts ``` ### React + React Query ``` entities/ └── user/ ├── ui/ ├── api/ │ └── userQueries.ts ← React Query hooks ├── model/ │ └── types.ts └── index.ts ``` ### Next.js Place FSD structure in `src/` folder to avoid conflicts with Next.js `app/` or `pages/` folders: ``` my-nextjs-project/ ├── app/ ← Next.js App Router (if using) ├── pages/ ← Next.js Pages Router (if using) └── src/ ├── app/ ← FSD app layer ├── pages/ ← FSD pages layer ├── widgets/ ├── features/ ├── entities/ └── shared/ ``` ## Framework Integration Examples ### Vite + React + TypeScript ``` project/ ├── src/ │ ├── app/ │ ├── pages/ │ ├── widgets/ │ ├── features/ │ ├── entities/ │ └── shared/ ├── tsconfig.json ├── vite.config.ts └── package.json ``` ### Create React App ``` project/ └── src/ ├── app/ ├── pages/ ├── widgets/ ├── features/ ├── entities/ └── shared/ ``` ## Key Reminders (v2.1) 1. **Pages First**: Start by keeping code in pages/widgets, extract only when you need to reuse 2. **Wait for actual reuse**: Don't predict reusability, let it emerge naturally 3. **Think in layers**: Determine responsibility level before creating files 4. **Slices are independent**: No imports between slices on the same layer (except @x for entities) 5. **High cohesion**: Keep related code together in slices 6. **Public API is mandatory**: Always define what's exported via index.ts 7. **Business logic can live in pages/widgets**: Don't extract prematurely 8. **Shared is for infrastructure**: Now can include app-aware code (routes, assets) but no business logic 9. **Processes layer deprecated**: Move code to features/ with app/ layer help 10. **Use @x for entity relationships**: Make cross-dependencies explicit and controlled 11. **Segments can import each other**: In App and Shared layers 12. **Follow the import rule**: Only import from layers below ## Quick Reference **Layer Selection**: - Global setup → `app/` - Routes → `pages/` - Reusable composites → `widgets/` - User actions → `features/` - Business models → `entities/` - Infrastructure → `shared/` **Import Direction**: App → Pages → Widgets → Features → Entities → Shared **Public API**: Always create `index.ts` for slices to export public interface ## Additional Resources For more detailed information and edge cases: - Cross-imports: When slices need to communicate - Desegmentation: Why grouping by tech role is anti-pattern - Routing: Advanced routing patterns - SSR: Server-side rendering implementation - Monorepos: Multi-package FSD setup ## Implementation Checklist When implementing FSD in a project: - [ ] Setup path aliases in tsconfig.json - [ ] Create layer folders: app/, pages/, widgets/, features/, entities/, shared/ - [ ] Move UI kit to shared/ui/ - [ ] Move utilities to shared/lib/ - [ ] Start with pages/ - keep code there first - [ ] Extract to features/entities only when you see actual reuse - [ ] Setup providers and routing in app/ - [ ] Add public API (index.ts) to each slice - [ ] Configure Steiger linter for FSD rules (recommended) - [ ] Document architecture decisions for team ### Steiger - Architectural Linter for FSD [Steiger](https://github.com/feature-sliced/steiger) is an official linter that helps enforce FSD rules automatically: - ✅ Detects import rule violations - ✅ Checks public API usage - ✅ Identifies cross-imports between slices - ✅ Ensures proper layer structure - ✅ Provides actionable error messages **Installation:** ```bash npm install -D @feature-sliced/steiger ``` **Usage:** ```bash npx steiger src ``` Steiger is production-ready and actively maintained. It's the best way to ensure your team follows FSD conventions consistently. --- ## When to Use This Skill Trigger this skill when: - User mentions "FSD", "Feature-Sliced Design", "feature sliced" - Creating new frontend project structure - Refactoring existing frontend codebase - Discussing code organization or architecture - Questions about where to put specific code - Issues with cross-imports or dependencies - Need to decompose features or components - Setting up project structure for React/Vue/Angular/Svelte - Migrating from FSD v2.0 to v2.1 ## Core Philosophy of FSD v2.1 **"Start simple, extract when needed."** Don't try to predict the future architecture. Build features in pages and widgets first. When you see actual reuse patterns emerging, then extract to features and entities. This leads to: - ✅ Better code cohesion (related code stays together) - ✅ Easier refactoring (everything is in one place) - ✅ Faster development (no premature abstractions) - ✅ Clearer architecture (only necessary abstractions exist) - ✅ Less cognitive overhead (simpler mental model) This skill provides the foundational knowledge to structure any frontend application using Feature-Sliced Design v2.1 methodology. Always prioritize code cohesion, wait for actual reuse before extracting, and maintain proper layering to ensure maintainable and scalable code architecture.