Skip to main content
A feature is a vertical slice for one domain area of your Single Page App. It lives at modules/<spa>/src/features/<feature>/ and owns its full front-end stack: components, hooks, services, state, styles, translations, and types. Each feature is self-contained and never imports another feature’s internals. When two features need the same code, that code is promoted to src/shared/ and imported from there. This keeps every domain area independent and easy to reason about, and it lets you grow the app by adding slices rather than tangling existing ones. See SPA overview for the bigger picture and SPA structure for how features sit inside a module.

What a slice owns

Each folder maps to a single area (settings, billing, profile) and holds everything that area needs, so there is no shared “components” dumping ground spanning concerns. A feature never reaches into another feature’s files; shared code lives in src/shared/, which keeps dependencies explicit and one-directional. The layers each have one job: backend calls live only in hooks/, services/ stay pure domain logic, and store/ holds client state. Because a slice is self-contained, you can rename, relocate, or remove a whole domain area by touching one folder.

How it works

Scaffold a feature with spa:feature:create and you get a route file plus a feature folder. The route file wires TanStack Router to the feature’s layouts; the feature folder is organized by layer, each with a single responsibility. The layering rule keeps slices clean. Components and layouts render, hooks/ fetch and mutate against the backend, services/ transform data with no side effects, and store/ holds client state. Nothing outside hooks/ should call the backend, and nothing should reach across into a sibling feature.

What spa:feature:create generates

talos spa:feature:create --name=<Name> --module=<spa> scaffolds a complete slice. PascalCase drives component names and kebab-case drives paths, so a feature named Settings produces files under settings/ with Settings* components. It generates:
  • A route file at src/routes/<kebab>.tsx, a TanStack Router file route at path /<kebab>.
  • Four layouts/boundaries in src/features/<kebab>/layouts/: <Name>Layout.tsx (the page component), <Name>SkeletonLayout.tsx (pending/loading boundary), <Name>ErrorLayout.tsx (error boundary), and <Name>NotFoundLayout.tsx (not-found boundary).
  • Two example hooks in src/features/<kebab>/hooks/: useGet<Name>.ts (a TanStack Query READ hook) and useUpdate<Name>.ts (a MUTATION hook).
It also installs @tanstack/react-query if it is missing. The generated route wires the layouts as the route’s component and its boundaries, giving one place that maps loading, error, and not-found states to the feature’s own components:
The page layout renders the feature’s content. It accepts optional children and falls back to the router Outlet so nested routes render in place:
The two generated hooks are example starting points, a read hook and a mutation hook, that you replace with your real queries. Data-fetching hooks are covered in depth on data fetching, and the routing model behind the generated route file is covered on routing.

Keeping slices independent

Keep each slice scoped to a single area, and if a folder starts serving two concerns, split it. Never import another feature’s internals: cross-feature imports re-couple the slices you worked to separate, so treat each feature as a closed box. When two features need the same component, hook, or type, move it to src/shared/ and import it from there. Keep route files thin (they should only wire layouts and boundaries, with real logic in the feature’s layers) and read or mutate only through hooks/, leaving services/ pure and store/ for client state so the data path stays predictable.

CLI command

Scaffold a feature with the generator. It writes the route file, the four layouts, and the two example hooks into the target spa module, and installs @tanstack/react-query if it is missing.
See spa:feature:create for the full command reference.

Use with Claude and Codex

The generator ships a matching spa:feature:create skill. It runs the scaffold and then guides your AI agent through filling in the slice: implementing the layouts, replacing the example hooks with real queries, and adding components, services, and types under the feature folder. Initialize the skills once for your agent:
Then ask Claude in natural language. It maps the request to the generator, runs it, and fills in the slice:
Prompt
For example, the prompt above maps to spa:feature:create --name=Settings --module=dashboard, then implements the layouts and turns the example hooks into the real reads and mutations the settings page needs.