一键导入
fsd-architecture
// Feature-Sliced Design architecture for frontend. Use when creating new features, slices, or understanding the FSD layer structure.
// Feature-Sliced Design architecture for frontend. Use when creating new features, slices, or understanding the FSD layer structure.
Generate TypeScript API client from Swagger/Go comments. Use when updating API endpoints, adding new routes, or regenerating the frontend API client after backend changes.
Manage database migrations and Better Auth schema. Use when adding tables, modifying schema, running migrations, or resetting the database.
Generate full-stack features. Backend = hand-written bounded-context aggregates (DDD); frontend = FSD slices with HydrationBoundary. Use when creating new features, adding CRUD operations, or scaffolding new pages.
Access local technical documentation before searching the internet. Use FIRST when researching any tech stack question.
Create and manage authentication pages with server-side session handling. Use when adding login, register, or protected pages WITHOUT flicker/skeleton.
Server-Side + Client-Side Data Fetching with Orval + TanStack Query HydrationBoundary Pattern. ALWAYS use Orval - NEVER manual fetch()!
| name | fsd-architecture |
| description | Feature-Sliced Design architecture for frontend. Use when creating new features, slices, or understanding the FSD layer structure. |
| allowed-tools | Read, Edit, Write, Glob, Grep |
The frontend uses Feature-Sliced Design. This skill documentation helps you understand the architecture and correctly create new features.
app/ → widgets, features, entities, shared
widgets/ → features, entities, shared
features/ → entities, shared
entities/ → shared
shared/ → (only external libs)
Import direction: ONLY downward!
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
// 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"
Each slice can have these segments:
| Segment | Purpose | Example |
|---|---|---|
ui/ | React Components | login-form.tsx |
model/ | Hooks, State, Types | use-auth-sync.ts |
api/ | API Calls (rare, mostly in shared) | user-api.ts |
lib/ | Utilities for the slice | validation.ts |
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"
mkdir -p src/features/<name>/ui
mkdir -p src/features/<name>/model # if hooks/state needed
// 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>
)
}
// 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 { /* ... */ }
}
// src/features/<name>/index.ts
export { NameForm } from "./ui/name-form"
export { useName } from "./model/use-name"
// app/(protected)/page.tsx
import { NameForm } from "@features/<name>"
export default function Page() {
return <NameForm />
}
// 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"
// ✅ 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/!
FSD rules are automatically checked:
# Integrated in lint
bun run lint
# Only Steiger
bunx steiger src
| Error | Cause | Solution |
|---|---|---|
no-public-api-sidestep | Direct import instead of via index.ts | Import via index.ts |
insignificant-slice | Slice has only 1 reference | Use more or move to widget/higher layer |
forbidden-imports | Import from higher layer | Fix import direction |
// Server Component (no "use client")
// → Can only import shared/, no hooks
// Client Component
"use client"
// → Can use hooks, but NEVER server-only code
// In Client Components:
import { signIn, signOut } from "@shared/lib/auth-client"
// In Server Components:
import { getSession } from "@shared/lib/auth-server"
bun run lint checks FSD rules