Frontend Large Feature Architecture
Use this skill when building, changing, or refactoring a large frontend
surface.
In this skill, "controller" means a component or hook that owns feature logic:
data fetching, view state, table/list state, effects, actions, and expensive
rendering. The problem is not the name; it is one place owning too many
changing responsibilities.
Big Feature Rules
When a feature grows, split rendering from logic. Most components should be
view-only. Data preparation should be pure. Complex user actions should live in
external async functions or local-store actions. Effects are integration
boundaries, not the normal way to derive state.
For the full rules, read
references/big-feature-rules.md.
React Without useEffect
UI is a pure function of state. Do not use useEffect to derive, prepare, or
sync data:
- Derived values are computed in render (
const title = data?.name ?? ""),
never mirrored into state by an effect. Client state that refers to server
data stores only the user's intent (an id) and derives the effective value
by merging with query data in render.
- When loaded data seeds editable state, split the component: an outer
data-preparer/controller fetches and renders a loading state (prefer a
skeleton;
<Spinner /> as minimal fallback); the inner component receives
the loaded value as an initialValue prop and seeds useState(initialValue).
Do not render UI before its data is ready.
- Define a form only where all initial values are already prepared; if data is
still loading, the form lives deeper in the tree.
- Complex actions live outside React as plain functions using the store or
query client — not inside components, not behind effects.
useCallback/useMemo are premature optimization; fix re-render problems by
splitting components, not memoizing.
useEffect is legitimate only for DOM/browser API integration: no (or
minimal) dependencies, one concern, and a cleanup function.
For the golden example and full reasoning, read
references/react-without-useeffect.md.
Required Model
- The page/view owns lifecycle and creates feature-scoped dependencies.
- Server/query state stays in tRPC/React Query; route state stays in the
router/filter hooks.
- High-frequency feature UI state belongs in a per-mount local vanilla Zustand
store. Create it with lazy
useState, not useMemo.
- Global stores are only for truly cross-feature, cross-route product state.
- React context may provide a stable store or action owner. Do not put
frequently changing state directly in provider values.
- Rendered rows/cells/items should be view-only or narrow containers. Put
effects, subscriptions, data loading, and workflows outside expensive views.
- Shared
src/components/* exports should stay context-free or receive
explicit props. Put view-scoped Zustand consumers in src/features/*.
- Large feature folders should have a concise
README.md owner map.
- Migrate real features toward this model whenever you touch them: improve
state boundaries, action workflows, data-preparation seams, and render
boundaries. Hundreds of legacy cases remain — do not preserve a legacy shape
just to keep a change small.
Local Store Default
Prefer a local vanilla Zustand store for large or high-frequency feature state.
The store is created by the page/view and destroyed on unmount.
Use selectors that return primitives or stable references. If a component needs
multiple values, use shallow selector helpers or split subscriptions so one
changing field does not rerender unrelated UI.
Complex user workflows should live in actions/*.ts files or store actions.
The component wires hooks and passes dependencies; the action owns the workflow.
For a complete effect audit or component-splitting recipes, use
../refactor-react-effects/SKILL.md.
When To Read References
- For the core big-feature rules and migration reality, read
references/big-feature-rules.md.
- For the React-without-useEffect model — derived values, gating render on
loaded data,
initialValue props, forms, actions outside React, and why not
to memoize — read
references/react-without-useeffect.md.
- For virtualized lists, translated DOM, row measurement, and scroll rerenders,
read
references/virtualized-lists.md.
- For local feature stores and splitting large components, read
references/local-feature-state.md.
- For feature
README.md owner maps, read
references/feature-readmes.md.
- For a step-by-step migration from a controller component to a managed feature
pattern, read
references/controller-migration.md.
This is the default reference for traces/observations tables, sessions,
experiments, prompts, evals, datasets, and other controller-heavy surfaces.
1---2name: frontend-large-feature-architecture3description: Architect large or state-heavy Langfuse frontend features. Use for virtualized lists, large tables, controllers, Zustand stores, row selection, high-frequency state, rendering performance, or before adding useEffect, useMemo, useCallback, or form defaults derived from loaded data.4---5
6# Frontend Large Feature Architecture
7
8Use this skill when building, changing, or refactoring a large frontend
9surface.
10
11In this skill, "controller" means a component or hook that owns feature logic:
12data fetching, view state, table/list state, effects, actions, and expensive
13rendering. The problem is not the name; it is one place owning too many
14changing responsibilities.
15
16## Big Feature Rules
17
18When a feature grows, split rendering from logic. Most components should be
19view-only. Data preparation should be pure. Complex user actions should live in
20external async functions or local-store actions. Effects are integration
21boundaries, not the normal way to derive state.
22
23For the full rules, read
24[`references/big-feature-rules.md`](references/big-feature-rules.md).
25
26## React Without useEffect
27
28UI is a pure function of state. Do not use `useEffect` to derive, prepare, or
29sync data:
30
31- Derived values are computed in render (`const title = data?.name ?? ""`),
32 never mirrored into state by an effect. Client state that refers to server
33 data stores only the user's intent (an id) and derives the effective value
34 by merging with query data in render.
35- When loaded data seeds editable state, split the component: an outer
36 data-preparer/controller fetches and renders a loading state (prefer a
37 skeleton; `<Spinner />` as minimal fallback); the inner component receives
38 the loaded value as an `initialValue` prop and seeds `useState(initialValue)`.
39 Do not render UI before its data is ready.
40- Define a form only where all initial values are already prepared; if data is
41 still loading, the form lives deeper in the tree.
42- Complex actions live outside React as plain functions using the store or
43 query client — not inside components, not behind effects.
44- `useCallback`/`useMemo` are premature optimization; fix re-render problems by
45 splitting components, not memoizing.
46- `useEffect` is legitimate only for DOM/browser API integration: no (or
47 minimal) dependencies, one concern, and a cleanup function.
48
49For the golden example and full reasoning, read
50[`references/react-without-useeffect.md`](references/react-without-useeffect.md).
51
52## Required Model
53
54- The page/view owns lifecycle and creates feature-scoped dependencies.
55- Server/query state stays in tRPC/React Query; route state stays in the
56 router/filter hooks.
57- High-frequency feature UI state belongs in a per-mount local vanilla Zustand
58 store. Create it with lazy `useState`, not `useMemo`.
59- Global stores are only for truly cross-feature, cross-route product state.
60- React context may provide a stable store or action owner. Do not put
61 frequently changing state directly in provider values.
62- Rendered rows/cells/items should be view-only or narrow containers. Put
63 effects, subscriptions, data loading, and workflows outside expensive views.
64- Shared `src/components/*` exports should stay context-free or receive
65 explicit props. Put view-scoped Zustand consumers in `src/features/*`.
66- Large feature folders should have a concise `README.md` owner map.
67- Migrate real features toward this model whenever you touch them: improve
68 state boundaries, action workflows, data-preparation seams, and render
69 boundaries. Hundreds of legacy cases remain — do not preserve a legacy shape
70 just to keep a change small.
71
72## Local Store Default
73
74Prefer a local vanilla Zustand store for large or high-frequency feature state.
75The store is created by the page/view and destroyed on unmount.
76
77Use selectors that return primitives or stable references. If a component needs
78multiple values, use shallow selector helpers or split subscriptions so one
79changing field does not rerender unrelated UI.
80
81Complex user workflows should live in `actions/*.ts` files or store actions.
82The component wires hooks and passes dependencies; the action owns the workflow.
83
84For a complete effect audit or component-splitting recipes, use
85[`../refactor-react-effects/SKILL.md`](../refactor-react-effects/SKILL.md).
86
87## When To Read References
88
89- For the core big-feature rules and migration reality, read
90 [`references/big-feature-rules.md`](references/big-feature-rules.md).
91- For the React-without-useEffect model — derived values, gating render on
92 loaded data, `initialValue` props, forms, actions outside React, and why not
93 to memoize — read
94 [`references/react-without-useeffect.md`](references/react-without-useeffect.md).
95- For virtualized lists, translated DOM, row measurement, and scroll rerenders,
96 read [`references/virtualized-lists.md`](references/virtualized-lists.md).
97- For local feature stores and splitting large components, read
98 [`references/local-feature-state.md`](references/local-feature-state.md).
99- For feature `README.md` owner maps, read
100 [`references/feature-readmes.md`](references/feature-readmes.md).
101- For a step-by-step migration from a controller component to a managed feature
102 pattern, read
103 [`references/controller-migration.md`](references/controller-migration.md).
104 This is the default reference for traces/observations tables, sessions,
105 experiments, prompts, evals, datasets, and other controller-heavy surfaces.