Feature-Sliced Design: Scalable Frontend Architecture Explained
Advanced4 minInnovialabs editorial team
In short
Feature-Sliced Design splits a frontend into layers ordered by responsibility, each layer into business-domain slices, and each slice into technical segments; modules may import only from layers below them and only through a slice's public API.
What Feature-Sliced Design is
Feature-Sliced Design (FSD) is a methodology for structuring frontend code by business meaning rather than by file type. Instead of one huge components/ and hooks/ folder, code is grouped into layers, slices and segments, with one strict rule: dependencies only point downward. The result is a codebase where you can find, change or delete a feature without breaking unrelated parts.
Layers
Layers are top-level folders, from most specific to most generic:
| Layer | Purpose | Example |
|---|---|---|
| app | Initialization: providers, routing, global styles | Theme provider, store setup |
| pages | Full pages composed from lower layers | Product page, profile page |
| widgets | Large self-contained UI blocks | Header, product list with filters |
| features | User actions that bring business value | Add to cart, log in, leave a review |
| entities | Business entities | User, product, order |
| shared | Reusable code without business logic | UI kit, API client, utils |
Older docs also mention a processes layer; it is deprecated, and its logic usually moves to features or app. You do not need every layer: small projects often use only app, pages, shared and a few entities.
Slices and segments
Slices divide a layer by business domain: entities/user, entities/product, features/add-to-cart. The app and shared layers have no slices.
Segments divide a slice by technical purpose. Common names:
ui— componentsmodel— state, stores, business logicapi— requests to the backendlib— helpers specific to this sliceconfig— constants and flags
Import rules and public API
Two rules make FSD work:
- A module may import only from layers strictly below it. A feature can use entities and shared, but not widgets or another feature. Slices on the same layer do not import each other.
- Every slice exposes a public API through an
index.ts. Other code imports only from that file, never from internal paths.
// entities/product/index.ts
export { ProductCard } from "./ui/product-card";
export { useProduct } from "./model/use-product";
export type { Product } from "./model/types";
// good
import { ProductCard } from "@/entities/product";
// bad: reaches into internals
import { ProductCard } from "@/entities/product/ui/product-card";
The linter Steiger and the ESLint plugin for boundaries can enforce these rules automatically, so architecture does not depend on code review alone.
When two entities genuinely need each other (an order references a user), FSD offers a cross-import notation with @x for explicit, controlled exceptions. Use it rarely.
A Next.js folder example
The Next.js app/ directory conflicts by name with the FSD app layer. A common solution: keep Next.js routing in the root app/ as thin files that re-export FSD pages, and move FSD code into src/.
app/ # Next.js routing only
products/[id]/page.tsx # export { ProductPage as default } from "@/pages/product"
src/
app/ # providers, global styles
pages/
product/
ui/product-page.tsx
index.ts
widgets/
product-gallery/
features/
add-to-cart/
ui/add-to-cart-button.tsx
model/use-add-to-cart.ts
index.ts
entities/
product/
api/get-product.ts
ui/product-card.tsx
model/types.ts
index.ts
shared/
ui/button.tsx
api/http.ts
Be careful with the name pages too: Next.js can treat a pages folder (including src/pages) as Pages Router routes. The official FSD guide for Next.js describes how to avoid this clash; check it before fixing the structure.
Migrating an existing codebase
Do not rewrite everything at once. A gradual path:
- Create
sharedand move the UI kit, API client and utils there. - Extract the main entities your app talks about (user, product, order).
- Move pages next, keeping them thin: they assemble, they do not contain logic.
- Extract features as you touch code for new tasks.
- Turn on linting for import rules once the structure stabilizes, and fix violations gradually.
The most common difficulty is deciding what is a feature and what is an entity. A practical test: an entity is a noun (product), a feature is a verb the user performs (add product to cart).
When FSD is overkill
- Landing pages, small marketing sites and prototypes.
- Short-lived MVPs where speed matters more than structure.
- A solo developer on a small app with a few screens.
- Teams that will not enforce the rules; half-applied FSD is worse than a simple, consistent structure.
FSD pays off on projects with several developers, many features and a long life.