Token导航 LogoToken导航

Agent Skill · 研究检索

feature-sliced-design(功能切片设计)

用于辅助界面设计、视觉规范、排版、配色、布局和交互体验优化。它适合让 Agent 根据产品场景整理页面结构、生成 UI 方案、检查视觉一致性或改进组件层级。使用时需要结合现有品牌、设计系统和用户任务,不应只堆装饰元素;涉及真实页面改动时,应通过截图或浏览器预览检查文本溢出、对齐和响应式表现。

feature-sliced-design 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 10,763。

总安装 12,136周安装 602GitHub Stars 5下载 4,397核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:feature-sliced-design(功能切片设计)
来源仓库:https://github.com/constellos/claude-code-plugins
仓库路径:skills/feature-sliced-design
安装命令:
npx skills add https://github.com/constellos/claude-code-plugins --skill 'Feature-Sliced Design'
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/constellos/claude-code-plugins --skill 'Feature-Sliced Design'

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

功能切片设计(FSD)方法论帮助组织前端应用为分层结构,实现关注点分离与可扩展性。适用于中大型前端项目,促进团队协作、降低耦合度并支持渐进式演进。通过 GitHub 安装并使用 npx skills add 命令添加,提供目录模板与依赖管理规范。实施时应结合现有技术栈,不建议在未评估情况下全面重构项目结构。

feature-sliced-design 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/feature-sliced-design

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/constellos/claude-code-plugins --skill 'Featur…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

feature-sliced-design 怎么安装?

在终端执行 npx skills add https://github.com/constellos/claude-code-plugins --skill 'Feature-Sliced D…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

feature-sliced-design 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

feature-sliced-design 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

feature-sliced-design 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

feature-sliced-design 用的人多吗?

公开数据是累计安装 12,136 次,近一周安装 602 次,下载 4,397 次,GitHub 5 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

feature-sliced-design 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
feature-sliced-design

功能切片设计 · 本页

12,1365需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

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):

  1. app - Application initialization, global providers, routing configuration
  2. processes - Deprecated (functionality moved to features and app)
  3. views - Page-level business logic (custom naming, replaces standard 'pages')
  4. widgets - Large composite UI blocks that span multiple features
  5. features - User-facing interactions with business value
  6. entities - Business domain objects and models
  7. 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):

  1. Start with shared layer

- Extract common UI components to shared/ui/ - Move utilities to shared/lib/ - Configure API client in shared/api/

  1. Define entities

- Identify business domain objects (User, Post, Comment) - Create entity types in entities/{name}/model/ - Move CRUD operations to entities/{name}/api/

  1. Extract features

- Identify user interactions (login, search, like) - Create feature slices in features/{name}/ - Use entities within features

  1. Build widgets

- Identify composite UI blocks (Header, Sidebar) - Create widget slices in widgets/{name}/ - Compose features within widgets

  1. Organize views

- Move page logic from /app to /src/views - Keep routing in /app, business logic in /src/views - Compose widgets in views

  1. 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:

  1. Extract shared logic to a lower layer (usually entities or shared)
  2. Create a higher layer (widget) that imports both
  3. 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:

  1. Clear .next directory: rm -rf.next
  2. Reinstall dependencies: npm install
  3. 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

平台分布

Codex

34.26%
按下载量换算1,506

Claude

31.23%
按下载量换算1,373

Cursor

19.85%
按下载量换算873

Gemini CLI

9.96%
按下载量换算438

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills