Claude-skill-registry fsd-architecture
Feature-Sliced Design architecture for frontend. Use when creating new features, slices, or understanding the FSD layer structure.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/fsd-architecture" ~/.claude/skills/majiayu000-claude-skill-registry-fsd-architecture && rm -rf "$T"
manifest:
skills/data/fsd-architecture/SKILL.mdsource content
Feature-Sliced Design (FSD) Architecture
The frontend uses Feature-Sliced Design. This skill documentation helps you understand the architecture and correctly create new features.
Layer Hierarchy (STRICT)
app/ → widgets, features, entities, shared widgets/ → features, entities, shared features/ → entities, shared entities/ → shared shared/ → (only external libs)
Import direction: ONLY downward!
Project Structure
frontend/src/ ├── app/ # Next.js App Router (outside FSD) │ ├── widgets/ # Composite UI Blocks │ └── header/ │ ├── ui/ │ │ ├── header.tsx │ │ └── mode-toggle.tsx │ └── index.ts # Public API │ ├── features/ # User Interactions / Use Cases │ ├── auth/ │ │ ├── ui/ │ │ │ ├── login-form.tsx │ │ │ └── register-form.tsx │ │ ├── model/ │ │ │ └── use-auth-sync.ts │ │ └── index.ts │ └── stats/ │ ├── ui/ │ │ └── stats-grid.tsx │ ├── model/ │ │ └── use-sse.ts │ └── index.ts │ ├── entities/ # Business Objects │ └── user/ │ ├── ui/ │ │ └── user-info.tsx │ ├── model/ │ │ └── types.ts │ └── index.ts │ └── shared/ # Reusable Code ├── ui/ # shadcn/ui components ├── api/ # Orval-generated │ ├── endpoints/ │ ├── models/ │ └── custom-fetch.ts ├── lib/ │ ├── auth-client/ # Client-safe Auth │ ├── auth-server/ # Server-only Auth │ ├── query-client.ts │ └── utils.ts └── config/ ├── providers.tsx └── theme-provider.tsx
TypeScript Path Aliases
// Always use layer aliases: import { Button } from "@shared/ui/button" import { useAuthSync } from "@features/auth" import { SessionUser } from "@entities/user" import { Header } from "@widgets/header"
Slice Segments
Each slice can have these segments:
| Segment | Purpose | Example |
|---|---|---|
| React Components | |
| Hooks, State, Types | |
| API Calls (rare, mostly in shared) | |
| Utilities for the slice | |
Public API Pattern (IMPORTANT!)
Every slice MUST have an
:index.ts
// features/auth/index.ts export { LoginForm } from "./ui/login-form" export { RegisterForm } from "./ui/register-form" export { useAuthSync, broadcastSignOut } from "./model/use-auth-sync"
Always import via index.ts:
// ✅ Correct import { LoginForm } from "@features/auth" // ❌ Wrong (Public API Sidestep) import { LoginForm } from "@features/auth/ui/login-form"
Creating a New Feature
1. Create folder structure
mkdir -p src/features/<name>/ui mkdir -p src/features/<name>/model # if hooks/state needed
2. Create UI Component
// src/features/<name>/ui/<name>-form.tsx "use client" import { Button } from "@shared/ui/button" import { Card } from "@shared/ui/card" export function NameForm() { return ( <Card> <Button>Action</Button> </Card> ) }
3. Create Model/Hook (optional)
// src/features/<name>/model/use-<name>.ts "use client" import { useQueryClient } from "@tanstack/react-query" import { useCallback } from "react" export function useName() { const queryClient = useQueryClient() // ... return { /* ... */ } }
4. Create Public API
// src/features/<name>/index.ts export { NameForm } from "./ui/name-form" export { useName } from "./model/use-name"
5. Use in App
// app/(protected)/page.tsx import { NameForm } from "@features/<name>" export default function Page() { return <NameForm /> }
Creating a New Entity
// src/entities/<name>/model/types.ts export interface Product { id: string name: string price: number } // src/entities/<name>/ui/product-card.tsx "use client" import type { Product } from "../model/types" import { Card } from "@shared/ui/card" export function ProductCard({ product }: { product: Product }) { return <Card>{product.name}</Card> } // src/entities/<name>/index.ts export type { Product } from "./model/types" export { ProductCard } from "./ui/product-card"
Import Rules
// ✅ ALLOWED // In app/: import { Header } from "@widgets/header" import { LoginForm } from "@features/auth" import { SessionUser } from "@entities/user" import { Button } from "@shared/ui/button" // In widgets/: import { useAuthSync } from "@features/auth" import { SessionUser } from "@entities/user" import { Button } from "@shared/ui/button" // In features/: import { SessionUser } from "@entities/user" import { Button } from "@shared/ui/button" // In entities/: import { Button } from "@shared/ui/button" // ❌ FORBIDDEN // In shared/ NEVER import features/! // In entities/ NEVER import features/! // In features/ NEVER import other features/!
Steiger Linting
FSD rules are automatically checked:
# Integrated in lint bun run lint # Only Steiger bunx steiger src
Common Steiger Errors
| Error | Cause | Solution |
|---|---|---|
| Direct import instead of via index.ts | Import via index.ts |
| Slice has only 1 reference | Use more or move to widget/higher layer |
| Import from higher layer | Fix import direction |
Server vs Client Components
// Server Component (no "use client") // → Can only import shared/, no hooks // Client Component "use client" // → Can use hooks, but NEVER server-only code
Auth Separation
// In Client Components: import { signIn, signOut } from "@shared/lib/auth-client" // In Server Components: import { getSession } from "@shared/lib/auth-server"
Summary
- Respect layers: Only import downward
- Public API: Always export/import via index.ts
- Segments: ui/, model/, api/, lib/ as needed
- Aliases: @shared/, @entities/, @features/, @widgets/
- Linting:
checks FSD rulesbun run lint