feature-sliced design

Feature-Sliced Design

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "feature-sliced design" with this command: npx skills add constellos/claude-code-plugins/constellos-claude-code-plugins-feature-sliced-design

Feature-Sliced Design

Purpose

Feature-Sliced Design (FSD) is an architectural methodology for organizing frontend applications into a standardized, scalable structure. It provides clear separation of concerns through a layered hierarchy that prevents circular dependencies and promotes maintainability.

Why use FSD:

  • Scalability: Grows naturally as your application expands

  • Maintainability: Clear boundaries make refactoring safer

  • Team collaboration: Consistent structure enables parallel development

  • Onboarding: New developers understand architecture quickly

Custom 'views' layer: This skill uses 'views' instead of the standard FSD 'pages' layer to avoid confusion with Next.js App Router's /app directory. The /app directory handles routing only (minimal logic), while /src/views contains your actual page business logic.

Next.js integration: FSD works seamlessly with Next.js App Router by separating routing concerns (in /app ) from business logic (in /src/views and other FSD layers). This keeps your routing configuration clean while maintaining FSD's architectural benefits.

When to Use

Apply Feature-Sliced Design when:

  • Starting new Next.js projects that require clear architectural boundaries

  • Refactoring growing codebases that lack consistent structure

  • Working with multi-developer teams needing standardized organization

  • Building applications with complex business logic requiring separation of concerns

  • Developing Turborepo monorepo applications where each app needs independent FSD structure

  • Scaling applications where circular dependencies become problematic

  • Creating enterprise applications with long-term maintenance requirements

Core Principles

Layer Hierarchy

FSD organizes code into 7 standardized layers (from highest to lowest):

  • app - Application initialization, global providers, routing configuration

  • processes - Deprecated (functionality moved to features and app)

  • views - Page-level business logic (custom naming, replaces standard 'pages')

  • widgets - Large composite UI blocks that span multiple features

  • features - User-facing interactions with business value

  • entities - Business domain objects and models

  • shared - Reusable utilities, UI kit, third-party integrations

Import rule: A module can only import from layers strictly below it in the hierarchy.

┌─────────────────┐ │ app │ ← Can import from all layers below ├─────────────────┤ │ views │ ← Can import: widgets, features, entities, shared ├─────────────────┤ │ widgets │ ← Can import: features, entities, shared ├─────────────────┤ │ features │ ← Can import: entities, shared ├─────────────────┤ │ entities │ ← Can import: shared only ├─────────────────┤ │ shared │ ← Cannot import from any FSD layer └─────────────────┘

This hierarchy prevents circular dependencies and ensures clear architectural boundaries.

'Views' vs 'Pages' Layer

Why 'views' instead of 'pages':

  • Next.js uses /app directory for routing (App Router)

  • Standard FSD uses 'pages' layer for page business logic

  • Using 'views' eliminates confusion between routing (/app ) and business logic (/src/views )

Separation of concerns:

  • /app directory (root level): Next.js routing only, minimal logic

  • Contains page.tsx , layout.tsx , route groups

  • Imports and renders from /src/views

  • /src/views layer (FSD): Page business logic, component composition

  • Contains view components, models, API calls

  • Composes widgets, features, entities

This separation keeps routing configuration clean while maintaining FSD architectural principles.

Slices

Slices are domain-based partitions within layers (except app and shared, which have no slices).

Examples:

  • views/dashboard

  • Dashboard page slice

  • widgets/header

  • Header widget slice

  • features/auth

  • Authentication feature slice

  • entities/user

  • User entity slice

Public API pattern: Each slice exports through index.ts to control its public interface:

// src/features/auth/index.ts export { LoginForm } from './ui/LoginForm'; export { useAuth } from './model/useAuth'; export type { AuthState } from './model/types'; // Internal implementation details NOT exported

This prevents deep imports and maintains encapsulation.

Segments

Segments are purpose-based groupings within slices:

  • ui/ - React components, visual elements

  • model/ - Business logic, state management, TypeScript types

  • api/ - API clients, data fetching, external integrations

  • lib/ - Utility functions, helpers specific to the slice

  • config/ - Configuration constants, feature flags

Example structure:

features/ └── auth/ ├── ui/ │ ├── LoginForm.tsx │ └── SignupForm.tsx ├── model/ │ ├── useAuth.ts │ └── types.ts ├── api/ │ └── authApi.ts └── index.ts

FSD with Next.js App Router

Routing Architecture

Next.js App Router uses /app directory for routing. FSD layers live in /src directory.

File organization:

my-nextjs-app/ ├── app/ # Next.js routing (minimal logic) │ ├── layout.tsx # Root layout │ ├── page.tsx # Home route │ ├── dashboard/ │ │ └── page.tsx # Dashboard route │ └── settings/ │ └── page.tsx # Settings route │ ├── src/ # FSD layers │ └── views/ # Page business logic │ ├── home/ │ │ ├── ui/ │ │ │ └── HomeView.tsx │ │ └── index.ts │ ├── dashboard/ │ │ ├── ui/ │ │ │ └── DashboardView.tsx │ │ ├── model/ │ │ │ └── useDashboard.ts │ │ └── index.ts │ └── settings/ │ ├── ui/ │ │ └── SettingsView.tsx │ └── index.ts

Routing pages import from views:

// app/dashboard/page.tsx - Routing only import { DashboardView } from '@/views/dashboard';

export default function DashboardPage() { return <DashboardView />; }

// src/views/dashboard/ui/DashboardView.tsx - Business logic import { Header } from '@/widgets/header'; import { StatsCard } from '@/features/analytics';

export function DashboardView() { return ( <div> <Header /> <StatsCard /> </div> ); }

Standalone Next.js Structure

Complete FSD structure for a standalone Next.js application:

my-nextjs-app/ ├── app/ # Next.js App Router │ ├── layout.tsx # Root layout │ ├── page.tsx # Home route │ ├── (auth)/ # Route group │ │ ├── login/ │ │ │ └── page.tsx │ │ └── signup/ │ │ └── page.tsx │ ├── dashboard/ │ │ └── page.tsx │ ├── api/ # API routes │ │ └── users/ │ │ └── route.ts │ └── not-found.tsx │ ├── src/ │ ├── app/ # App layer (no slices) │ │ ├── providers/ │ │ │ ├── AuthProvider.tsx │ │ │ └── QueryProvider.tsx │ │ ├── styles/ │ │ │ └── globals.css │ │ └── config/ │ │ └── constants.ts │ │ │ ├── views/ # Views layer (page logic) │ │ ├── home/ │ │ ├── dashboard/ │ │ ├── login/ │ │ └── signup/ │ │ │ ├── widgets/ # Widgets layer │ │ ├── header/ │ │ ├── sidebar/ │ │ ├── footer/ │ │ └── notification-panel/ │ │ │ ├── features/ # Features layer │ │ ├── auth/ │ │ ├── search/ │ │ ├── theme-toggle/ │ │ └── user-profile/ │ │ │ ├── entities/ # Entities layer │ │ ├── user/ │ │ ├── post/ │ │ ├── comment/ │ │ └── session/ │ │ │ └── shared/ # Shared layer (no slices) │ ├── ui/ # UI components │ │ ├── button/ │ │ ├── input/ │ │ └── card/ │ ├── lib/ # Utilities │ │ ├── format.ts │ │ └── validation.ts │ ├── api/ # API client │ │ └── client.ts │ └── config/ │ └── env.ts │ ├── public/ │ ├── images/ │ └── fonts/ │ └── package.json

Turborepo Monorepo Structure

FSD structure within a Turborepo monorepo where each app has independent FSD organization:

turborepo-root/ ├── apps/ │ ├── web/ # Consumer-facing app │ │ ├── app/ # Next.js routing │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ └── shop/ │ │ │ └── page.tsx │ │ ├── src/ # Independent FSD structure │ │ │ ├── app/ │ │ │ ├── views/ │ │ │ │ ├── home/ │ │ │ │ └── shop/ │ │ │ ├── widgets/ │ │ │ │ ├── product-grid/ │ │ │ │ └── shopping-cart/ │ │ │ ├── features/ │ │ │ │ ├── add-to-cart/ │ │ │ │ └── checkout/ │ │ │ ├── entities/ │ │ │ │ ├── product/ │ │ │ │ └── order/ │ │ │ └── shared/ │ │ └── package.json │ │ │ └── admin/ # Admin dashboard app │ ├── app/ # Next.js routing │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── products/ │ │ └── page.tsx │ ├── src/ # Independent FSD structure │ │ ├── app/ │ │ ├── views/ │ │ │ ├── dashboard/ │ │ │ └── products/ │ │ ├── widgets/ │ │ │ ├── admin-header/ │ │ │ └── stats-panel/ │ │ ├── features/ │ │ │ ├── product-editor/ │ │ │ └── user-management/ │ │ ├── entities/ │ │ │ ├── product/ │ │ │ └── admin/ │ │ └── shared/ │ └── package.json │ ├── packages/ # Optional shared packages │ ├── ui/ # Shared UI components (can mirror shared/ui) │ │ ├── button/ │ │ └── input/ │ ├── utils/ # Shared utilities │ │ └── validation.ts │ └── types/ # Shared TypeScript types │ └── common.ts │ ├── turbo.json └── package.json

Key Turborepo principles:

  • Each app (web , admin ) has its own complete FSD structure

  • Apps are independent - no cross-app imports from FSD layers

  • Shared code goes in packages/ directory (optional)

  • Use workspace:* protocol for package dependencies

Layer Definitions

app Layer

Purpose: Application-wide setup, initialization, and global configuration.

Responsibilities:

  • Global providers (theme, auth, query client)

  • Root styles and CSS imports

  • Application-level configuration

  • Error boundaries

Import rules: Can import from all layers below (views, widgets, features, entities, shared).

No slices: The app layer contains segments directly (providers/, styles/, config/).

Example:

// src/app/providers/Providers.tsx 'use client';

import { QueryClientProvider } from '@tanstack/react-query'; import { queryClient } from '@/shared/api/queryClient'; import { AuthProvider } from '@/features/auth';

export function Providers({ children }: { children: React.ReactNode }) { return ( <QueryClientProvider client={queryClient}> <AuthProvider> {children} </AuthProvider> </QueryClientProvider> ); }

// app/layout.tsx import { Providers } from '@/app/providers/Providers'; import '@/app/styles/globals.css';

export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <Providers>{children}</Providers> </body> </html> ); }

views Layer

Purpose: Page-level business logic and component composition.

Responsibilities:

  • Compose widgets, features, and entities into complete pages

  • Page-specific state management

  • Data fetching for the page

  • SEO metadata

Import rules: Can import from widgets, features, entities, shared.

Has slices: Each page gets its own slice (e.g., views/dashboard , views/settings ).

Example:

// src/views/dashboard/ui/DashboardView.tsx import { Header } from '@/widgets/header'; import { Sidebar } from '@/widgets/sidebar'; import { StatsCard } from '@/features/analytics'; import { RecentActivity } from '@/features/activity'; import { User } from '@/entities/user';

interface DashboardViewProps { user: User; }

export function DashboardView({ user }: DashboardViewProps) { return ( <div className="dashboard"> <Header user={user} /> <div className="dashboard-content"> <Sidebar /> <main> <StatsCard userId={user.id} /> <RecentActivity userId={user.id} /> </main> </div> </div> ); }

// src/views/dashboard/index.ts export { DashboardView } from './ui/DashboardView';

// app/dashboard/page.tsx import { DashboardView } from '@/views/dashboard'; import { getCurrentUser } from '@/entities/user';

export default async function DashboardPage() { const user = await getCurrentUser(); return <DashboardView user={user} />; }

widgets Layer

Purpose: Large, self-contained composite UI blocks that combine multiple features.

Responsibilities:

  • Reusable across multiple pages

  • Compose multiple features together

  • Complex UI layouts (headers, sidebars, footers)

  • Navigation components

Import rules: Can import from features, entities, shared.

Has slices: Each widget gets its own slice (e.g., widgets/header , widgets/sidebar ).

Example:

// src/widgets/header/ui/Header.tsx import { SearchBar } from '@/features/search'; import { UserMenu } from '@/features/user-menu'; import { NotificationBell } from '@/features/notifications'; import { User } from '@/entities/user'; import { Logo } from '@/shared/ui/logo';

interface HeaderProps { user: User; }

export function Header({ user }: HeaderProps) { return ( <header className="header"> <Logo /> <SearchBar /> <div className="header-actions"> <NotificationBell userId={user.id} /> <UserMenu user={user} /> </div> </header> ); }

// src/widgets/header/index.ts export { Header } from './ui/Header';

features Layer

Purpose: User-facing interactions and business logic with clear business value.

Responsibilities:

  • Specific user actions (login, add to cart, like post)

  • Feature-specific state management

  • Business logic and validation

  • API interactions for the feature

Import rules: Can import from entities, shared.

Has slices: Each feature gets its own slice (e.g., features/auth , features/search ).

Example:

// src/features/auth/model/types.ts export interface LoginCredentials { email: string; password: string; }

// src/features/auth/api/login.ts import { User } from '@/entities/user'; import { apiClient } from '@/shared/api/client'; import type { LoginCredentials } from '../model/types';

export async function login(credentials: LoginCredentials): Promise<User> { const response = await apiClient.post('/auth/login', credentials); return response.data; }

// src/features/auth/ui/LoginForm.tsx 'use client';

import { useState } from 'react'; import { login } from '../api/login'; import { Button } from '@/shared/ui/button'; import { Input } from '@/shared/ui/input';

export function LoginForm() { const [email, setEmail] = useState(''); const [password, setPassword] = useState('');

const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); await login({ email, password }); };

return ( <form onSubmit={handleSubmit}> <Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" /> <Input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Password" /> <Button type="submit">Login</Button> </form> ); }

// src/features/auth/index.ts export { LoginForm } from './ui/LoginForm'; export { login } from './api/login'; export type { LoginCredentials } from './model/types';

entities Layer

Purpose: Business domain objects and core data models.

Responsibilities:

  • Data structures representing business concepts

  • Entity-specific utilities

  • Base API operations (CRUD)

  • Type definitions

Import rules: Can import from shared only.

Has slices: Each entity gets its own slice (e.g., entities/user , entities/post ).

Example:

// src/entities/user/model/types.ts export interface User { id: string; name: string; email: string; avatar?: string; role: 'admin' | 'user'; }

// src/entities/user/api/getUser.ts import { apiClient } from '@/shared/api/client'; import type { User } from '../model/types';

export async function getUser(id: string): Promise<User> { const response = await apiClient.get(/users/${id}); return response.data; }

export async function getCurrentUser(): Promise<User> { const response = await apiClient.get('/users/me'); return response.data; }

// src/entities/user/ui/UserCard.tsx import type { User } from '../model/types'; import { Avatar } from '@/shared/ui/avatar';

interface UserCardProps { user: User; }

export function UserCard({ user }: UserCardProps) { return ( <div className="user-card"> <Avatar src={user.avatar} alt={user.name} /> <div> <h3>{user.name}</h3> <p>{user.email}</p> </div> </div> ); }

// src/entities/user/index.ts export { UserCard } from './ui/UserCard'; export { getUser, getCurrentUser } from './api/getUser'; export type { User } from './model/types';

shared Layer

Purpose: Reusable utilities, UI components, and third-party integrations.

Responsibilities:

  • UI kit (button, input, card components)

  • Helper functions (formatters, validators)

  • API client configuration

  • Constants and environment variables

  • Third-party library integrations

Import rules: Cannot import from any FSD layer (only external packages).

No slices: Contains segments directly (ui/, lib/, api/, config/).

Example:

// src/shared/ui/button/Button.tsx import { type ButtonHTMLAttributes } from 'react';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { variant?: 'primary' | 'secondary' | 'ghost'; size?: 'sm' | 'md' | 'lg'; }

export function Button({ variant = 'primary', size = 'md', className, children, ...props }: ButtonProps) { return ( <button className={button button--${variant} button--${size} ${className}} {...props} > {children} </button> ); }

// src/shared/lib/format.ts export function formatDate(date: Date): string { return new Intl.DateTimeFormat('en-US').format(date); }

export function formatCurrency(amount: number): string { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', }).format(amount); }

// src/shared/api/client.ts import axios from 'axios';

export const apiClient = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, headers: { 'Content-Type': 'application/json', }, });

// src/shared/config/env.ts export const env = { apiUrl: process.env.NEXT_PUBLIC_API_URL!, nodeEnv: process.env.NODE_ENV, } as const;

Workflow

Step 1: Set Up Layer Directories

Create the FSD folder structure:

mkdir -p src/{app,views,widgets,features,entities,shared} mkdir -p src/app/{providers,styles,config} mkdir -p src/shared/{ui,lib,api,config}

Step 2: Create First Entity

Start with entities (bottom layer). Define your core business models:

// src/entities/user/model/types.ts export interface User { id: string; name: string; email: string; }

// src/entities/user/api/getUser.ts export async function getUser(id: string): Promise<User> { // API implementation }

// src/entities/user/index.ts export type { User } from './model/types'; export { getUser } from './api/getUser';

Step 3: Build Features Using Entities

Create features that use entities:

// src/features/user-profile/ui/UserProfile.tsx import { User } from '@/entities/user'; // ✅ Feature imports entity

export function UserProfile({ user }: { user: User }) { return <div>{user.name}</div>; }

// src/features/user-profile/index.ts export { UserProfile } from './ui/UserProfile';

Step 4: Compose Widgets from Features

Build composite widgets:

// src/widgets/header/ui/Header.tsx import { UserProfile } from '@/features/user-profile'; // ✅ Widget imports feature import { SearchBar } from '@/features/search';

export function Header({ user }) { return ( <header> <SearchBar /> <UserProfile user={user} /> </header> ); }

Step 5: Assemble Views

Create page-level views:

// src/views/dashboard/ui/DashboardView.tsx import { Header } from '@/widgets/header'; // ✅ View imports widget

export function DashboardView() { return ( <div> <Header /> {/* More content */} </div> ); }

// src/views/dashboard/index.ts export { DashboardView } from './ui/DashboardView';

Step 6: Connect to App Router

Wire views to Next.js routing:

// app/dashboard/page.tsx import { DashboardView } from '@/views/dashboard';

export default function DashboardPage() { return <DashboardView />; }

Import Rules and Dependencies

Allowed Import Patterns

// ✅ Layer importing from layer below import { User } from '@/entities/user'; // Feature → Entity import { LoginForm } from '@/features/auth'; // Widget → Feature import { Header } from '@/widgets/header'; // View → Widget

// ✅ Any layer importing from shared import { Button } from '@/shared/ui/button'; import { formatDate } from '@/shared/lib/format';

// ✅ Slice importing from different slice in lower layer import { User } from '@/entities/user'; // features/auth → entities/user import { Post } from '@/entities/post'; // features/like → entities/post

Forbidden Import Patterns

// ❌ Layer importing from same or higher layer import { DashboardView } from '@/views/dashboard'; // Feature → View (upward) import { Header } from '@/widgets/header'; // Feature → Widget (upward) import { LoginForm } from '@/features/login'; // features/auth → features/login (same layer)

// ❌ Cross-slice imports within same layer import { SearchBar } from '@/features/search'; // features/auth → features/search

// ❌ Shared importing from FSD layers import { User } from '@/entities/user'; // shared/lib → entities/user

Valid vs Invalid Examples

Invalid (cross-feature import):

// ❌ src/features/search/ui/SearchBar.tsx import { LoginForm } from '@/features/auth'; // Same layer import

Valid (extract to widget):

// ✅ src/widgets/navbar/ui/Navbar.tsx import { SearchBar } from '@/features/search'; import { LoginForm } from '@/features/auth';

export function Navbar() { return ( <nav> <SearchBar /> <LoginForm /> </nav> ); }

Fixing Circular Dependencies

Problem:

// features/auth imports features/user-settings // features/user-settings imports features/auth // ❌ Circular dependency

Solution 1: Extract to entity

// Move shared logic to entities/user // Both features import from entities/user // ✅ No circular dependency

Solution 2: Extract to widget

// Create widgets/user-panel that imports both features // ✅ Widget layer can import from features

Public API Enforcement

Always use index.ts to control exports:

// src/features/auth/index.ts export { LoginForm } from './ui/LoginForm'; export { useAuth } from './model/useAuth'; export type { AuthState } from './model/types';

// ❌ Do NOT export internal helpers // export { validatePassword } from './lib/validation'; // Keep internal

Import from public API only:

// ✅ Correct import { LoginForm } from '@/features/auth';

// ❌ Wrong (deep import) import { LoginForm } from '@/features/auth/ui/LoginForm';

Segment Patterns

ui/ Segment

Purpose: React components and visual elements.

When to use:

  • Any React component

  • UI composition

  • Visual presentation

Example:

// src/entities/user/ui/UserCard.tsx import type { User } from '../model/types';

export function UserCard({ user }: { user: User }) { return ( <div className="user-card"> <h3>{user.name}</h3> <p>{user.email}</p> </div> ); }

model/ Segment

Purpose: Business logic, state management, and type definitions.

When to use:

  • TypeScript interfaces and types

  • React hooks for state

  • Business logic functions

  • Data transformations

Example:

// src/features/auth/model/useAuth.ts 'use client';

import { create } from 'zustand'; import type { User } from '@/entities/user';

interface AuthState { user: User | null; isAuthenticated: boolean; login: (user: User) => void; logout: () => void; }

export const useAuth = create<AuthState>((set) => ({ user: null, isAuthenticated: false, login: (user) => set({ user, isAuthenticated: true }), logout: () => set({ user: null, isAuthenticated: false }), }));

api/ Segment

Purpose: API clients, data fetching, and external integrations.

When to use:

  • HTTP requests

  • WebSocket connections

  • Third-party API integrations

  • Server actions (Next.js)

Example:

// src/entities/user/api/userApi.ts 'use server';

import { apiClient } from '@/shared/api/client'; import type { User } from '../model/types';

export async function fetchUsers(): Promise<User[]> { const response = await apiClient.get('/users'); return response.data; }

export async function createUser(data: Omit<User, 'id'>): Promise<User> { const response = await apiClient.post('/users', data); return response.data; }

lib/ Segment

Purpose: Utility functions and helpers specific to the slice.

When to use:

  • Slice-specific utilities

  • Validation functions

  • Data transformation helpers

Example:

// src/features/auth/lib/validation.ts import { z } from 'zod';

export const loginSchema = z.object({ email: z.string().email('Invalid email address'), password: z.string().min(8, 'Password must be at least 8 characters'), });

export function validateLogin(data: unknown) { return loginSchema.parse(data); }

config/ Segment

Purpose: Configuration constants and feature flags.

When to use:

  • Feature-specific constants

  • Configuration objects

  • Feature flags

Example:

// src/app/config/theme.ts export const theme = { colors: { primary: '#0070f3', secondary: '#ff4081', }, breakpoints: { sm: '640px', md: '768px', lg: '1024px', }, } as const;

Migration Strategy

Migrating Existing Next.js App to FSD

Bottom-up approach (recommended):

Start with shared layer

  • Extract common UI components to shared/ui/

  • Move utilities to shared/lib/

  • Configure API client in shared/api/

Define entities

  • Identify business domain objects (User, Post, Comment)

  • Create entity types in entities/{name}/model/

  • Move CRUD operations to entities/{name}/api/

Extract features

  • Identify user interactions (login, search, like)

  • Create feature slices in features/{name}/

  • Use entities within features

Build widgets

  • Identify composite UI blocks (Header, Sidebar)

  • Create widget slices in widgets/{name}/

  • Compose features within widgets

Organize views

  • Move page logic from /app to /src/views

  • Keep routing in /app , business logic in /src/views

  • Compose widgets in views

Configure app layer

  • Move global providers to app/providers/

  • Move global styles to app/styles/

Handling Existing Code

Incremental migration:

  • Migrate one page at a time

  • Start with least complex pages

  • Use both old and new structure during transition

  • Update imports as you migrate

Testing throughout:

  • Run tests after each layer migration

  • Ensure no functionality breaks

  • Verify import paths are correct

Best Practices

Keep slices isolated:

  • No cross-slice imports within the same layer

  • Each slice should be independent

  • Extract shared logic to lower layers

Use Public API pattern:

  • Always export through index.ts

  • Prevents deep imports

  • Makes refactoring easier

Colocate tests:

features/ └── auth/ ├── ui/ │ ├── LoginForm.tsx │ └── LoginForm.test.tsx # Test next to implementation └── index.ts

Avoid "god slices":

  • Keep slices focused on single responsibility

  • Split large slices into multiple smaller ones

  • Extract common logic to shared layer

Name by domain, not tech:

  • ✅ features/product-search

  • ❌ features/search-bar-component

Use TypeScript strict mode:

{ "compilerOptions": { "strict": true } }

Document architecture decisions:

  • Keep ADR (Architecture Decision Records)

  • Document why certain slices exist

  • Explain layer boundary decisions

Common Patterns

Shared UI Components

// src/shared/ui/button/Button.tsx export function Button({ children, ...props }) { return <button {...props}>{children}</button>; }

// Usage in feature import { Button } from '@/shared/ui/button';

API Client Setup

// src/shared/api/client.ts import axios from 'axios';

export const apiClient = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, });

// Usage in entity import { apiClient } from '@/shared/api/client';

Form Handling with Features

// src/features/product-form/ui/ProductForm.tsx 'use client';

import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { productSchema } from '../lib/validation'; import { createProduct } from '../api/createProduct';

export function ProductForm() { const { register, handleSubmit } = useForm({ resolver: zodResolver(productSchema), });

return <form onSubmit={handleSubmit(createProduct)}>...</form>; }

Authentication Integration

// src/features/auth/model/useAuth.ts export const useAuth = create<AuthState>((set) => ({...}));

// src/widgets/header/ui/Header.tsx import { useAuth } from '@/features/auth';

export function Header() { const { user } = useAuth(); return <header>Welcome, {user?.name}</header>; }

Data Fetching with Server Components

// app/dashboard/page.tsx import { DashboardView } from '@/views/dashboard'; import { getUser } from '@/entities/user';

export default async function DashboardPage() { const user = await getUser('current'); return <DashboardView user={user} />; }

// src/views/dashboard/ui/DashboardView.tsx import type { User } from '@/entities/user';

export function DashboardView({ user }: { user: User }) { return <div>Welcome, {user.name}</div>; }

Troubleshooting

Circular Dependencies

Problem: Two slices import from each other.

Solution:

  • Extract shared logic to a lower layer (usually entities or shared)

  • Create a higher layer (widget) that imports both

  • Review if one slice should actually be split into multiple slices

Import Path Issues

Problem: TypeScript cannot resolve @/ imports.

Solution: Configure path aliases in tsconfig.json :

{ "compilerOptions": { "baseUrl": ".", "paths": { "@/app/": ["src/app/"], "@/views/": ["src/views/"], "@/widgets/": ["src/widgets/"], "@/features/": ["src/features/"], "@/entities/": ["src/entities/"], "@/shared/": ["src/shared/"] } } }

Build Errors

Problem: Next.js cannot find modules after restructuring.

Solution:

  • Clear .next directory: rm -rf .next

  • Reinstall dependencies: npm install

  • Restart dev server: npm run dev

Configuration

TypeScript Path Aliases

{ "compilerOptions": { "baseUrl": ".", "paths": { "@/app/": ["src/app/"], "@/views/": ["src/views/"], "@/widgets/": ["src/widgets/"], "@/features/": ["src/features/"], "@/entities/": ["src/entities/"], "@/shared/": ["src/shared/"] } }, "include": ["src", "app"] }

ESLint Import Rules (Optional)

// .eslintrc.js module.exports = { rules: { 'no-restricted-imports': [ 'error', { patterns: [ { group: ['@/views/', '@/widgets/'], message: 'Features cannot import from views or widgets', }, ], }, ], }, };

References

  • Official FSD Documentation - Complete methodology guide

  • FSD with Next.js Guide - Next.js integration patterns

  • FSD GitHub Repository - Source documentation

  • Frontend Monorepo Architecture - Turborepo and FSD

  • FSD Tutorial - Step-by-step implementation guide

  • FSD Examples - Real-world applications using FSD

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

ui wireframing

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

supabase local development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

ui interaction

No summary provided by upstream source.

Repository SourceNeeds Review