Install any skill in seconds. Free to start, no credit card required.
Get Started Free →React enterprise architecture guidelines for structuring scalable, production-grade applications. Use when scaffolding a new React project, reviewing project structure, setting up feature slices, choosing state management, configuring data fetching, or making architectural decisions in a React/TypeScript codebase. Covers folder structure, routing (TanStack Router, React Router v7, Next.js App Router), TanStack Query v5, Zustand, React Compiler, Vite 6, Turbopack, Vitest, Playwright, Tailwind v4,
.claude/skills/mamamou-react-structure/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 8 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 177% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 155% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 197% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 259% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 174% | 0% |
Feature-first, enterprise-grade React architecture that scales across teams and codebases. Assumes TypeScript strict, modern tooling (Vite 6 or Next.js 16 App Router), and emphasizes feature isolation, data-access boundaries, performance, and operability.
src/
app/
providers/ # app-level providers (query client, i18n, theme)
routing/ # router setup & guards
layouts/ # app chrome (top bar, sidebar, auth shell)
config/ # runtime config loader & tokens
errors/ # boundary, fallback UI, error instrumentation
shared/ # reusable & stateless UI/utilities
ui/ # presentational components (buttons, cards, table)
forms/ # form controls, hooks, schemas, validators
hooks/ # generic hooks not tied to a feature
utils/ # pure TS helpers
styles/ # design tokens, mixins, global CSS
features/ # domain-driven feature slices (lazy where possible)
users/
index.ts # public API for the feature
routes.tsx # feature routes (SPA) or page segments (Next.js)
components/ # presentational, feature-scoped
pages/ # feature shells/screens
api/ # data-access (clients, models, adapters)
state/ # local store/facade (Zustand)
testing/ # test builders/mocks
test/ # global test utilities
main.tsx # app bootstrap (SPA) or entry (Next.js client)Philosophy
> Next.js (App Router): Map features to app/(features)/<feature> segments and keep UI/data boundaries the same. For an SPA, use TanStack Router or React Router v7 + Vite 6.
app/providers to host QueryClientProvider, ThemeProvider, I18nProvider, StoreProvider, etc./config.json (or env) early; expose via a typed context or simple module.ErrorBoundary + error reporting (Sentry, etc.).app/layouts/, not in shared or features. Use <Outlet /> (React Router) or {children} (Next.js) for content slots.Example (SPA, Vite 6)
tsx// main.tsx createRoot(document.getElementById('root')!).render( <StrictMode> <AppProviders> <RouterProvider router={appRouter} /> </AppProviders> </StrictMode> );
shared/
ui/
PageHeader/
DataTable/
EmptyState/
Skeleton/
forms/
Form/
TextField/
PhoneInput/
validators.ts
hooks/
useDebounce.ts
useToggle.ts
utils/
date.ts
array.ts
styles/
tokens.css
globals.cssRules
> React Compiler note: With React Compiler stable (1.0), manual React.memo wrapping is no longer needed in most cases. The compiler auto-memoizes at the expression level. Retain manual memoization only at third-party library interop boundaries where identity comparison is explicitly required.
Each feature encapsulates UI, data-access, and local state.
features/users/
index.ts # exports public units (components/hooks)
routes.tsx # SPA route config for this feature
pages/
UsersListPage.tsx # screen/shell: orchestrates data + renders UI
UserCreatePage.tsx
UserEditPage.tsx
components/
UsersTable.tsx # presentational
UserCard.tsx
UserForm.tsx # shared between create/edit
api/
users.client.ts # HTTP client
users.models.ts # DTOs & ViewModels
users.adapter.ts # DTO <-> VM mappings
queries.ts # TanStack Query v5 hooks
state/
users.store.ts # Zustand store for UI state
testing/
builders.tsRules
index.ts.defaultValues and an onSubmit handler.['users', 'list']).useSuspenseQuery for simpler loading states with <Suspense> boundaries.v5 key changes from v4:
onSuccess/onError/onSettled callbacks removed from useQuery — handle in calling code.keepPreviousData removed — use placeholderData: (prev) => prev.useErrorBoundary renamed to throwOnError.initialPageParam.Example
ts// features/users/api/queries.ts export const usersKeys = { all: ['users'] as const, list: () => [...usersKeys.all, 'list'] as const, detail: (id: string) => [...usersKeys.all, 'detail', id] as const, }; export function useUsers() { return useSuspenseQuery({ queryKey: usersKeys.list(), queryFn: api.users.list, }); } export function useCreateUser() { const qc = useQueryClient(); return useMutation({ mutationFn: api.users.create, onSuccess: () => qc.invalidateQueries({ queryKey: usersKeys.all }), }); }
| State type | Solution | |---|---| | Server cache | TanStack Query v5 — do not mirror into client stores | | Local UI state | useState / useReducer inside components | | Cross-feature client state (auth, theme, feature flags) | Zustand (lightweight, ~3KB) | | Complex atomic state (many interdependent values) | Jotai (fine-grained reactivity) | | Legacy large codebases | Redux Toolkit + feature slices (maintain, don't adopt fresh) |
api/.app/providers.Example
ts// users.adapter.ts export const toUserVM = (dto: UserDto): UserVM => ({ id: dto.id, name: `${dto.firstName} ${dto.lastName}`, role: dto.role ?? 'user', });
Type-safe routes, params, and search params with full inference and autocomplete. Built-in data loading and caching. DevTools included.
Library mode for traditional SPA. Adequate for most use cases; lacks built-in type-safe params.
Feature routes under app/(features)/users with page.tsx per route. Use route groups for layout boundaries.
Layout pattern:
app/layouts/ — not feature code.PageHeader renders inside each page's <main> content area.app/
layout.tsx # global minimal layout/providers
(dashboard)/
layout.tsx # top bar + sidebar + {children}
users/
page.tsx # list
new/page.tsx # create
[id]/edit/page.tsx # edit
(public)/
layout.tsx
login/page.tsxReact Compiler 1.0 is stable (October 2025). It is a build-time Babel plugin that performs automatic expression-level memoization via static analysis.
Impact on your code:
useMemo, useCallback, and React.memo are largely unnecessary for new code.react-compiler-runtime.Adoption:
reactCompiler: true in next.config.ts).children pattern).@tanstack/react-virtual), and stable item render.<ViewTransition> (canary): Wraps browser View Transitions API for smooth route animations. Safe to experiment, not production-critical yet.<Suspense> for granular loading states and streaming.dangerouslySetInnerHTML.Secure, HttpOnly cookies where possible; rotate and refresh securely.shared/styles.| Layer | Tool | Notes | |---|---|---| | Unit / Component | Vitest + React Testing Library | Co-locate *.test.ts(x) with code. Vitest is ~6x faster cold start than Jest, native ESM/TS support. | | API mocking | MSW (Mock Service Worker) | Intercept at the network level for realistic mocks. | | Integration | Vitest + real providers (QueryClient, Router) | Test screens with actual provider tree. | | E2E | Playwright | 3-5 critical flows in CI. UI Mode for time-traveling debugger. |
features/*/testing and test/.| Tool | Use case | |---|---| | Vite 6 | Standalone React SPAs. Fastest HMR, broadest plugin support. Environment API for multi-target dev. | | Turbopack | Next.js projects. Default bundler in Next.js 16. | | Rspack | Webpack migration without full rewrite (Rust-based, Webpack-compatible). |
@app, @shared, @features/*.features/*/index.ts public API.type:feature, type:shared).json{ "paths": { "@app/*": ["src/app/*"], "@shared/*": ["src/shared/*"], "@features/*": ["src/features/*"] } }
import.meta.env / Next.js process.env) for non-sensitive flags./config.json (or server) at startup.tsx// features/users/routes.tsx export const usersRoute = createRoute({ getParentRoute: () => dashboardRoute, path: '/users', component: UsersListPage, }); export const userCreateRoute = createRoute({ getParentRoute: () => dashboardRoute, path: '/users/new', component: UserCreatePage, }); export const userEditRoute = createRoute({ getParentRoute: () => dashboardRoute, path: '/users/$id/edit', component: UserEditPage, });
tsx// features/users/pages/UsersListPage.tsx export function UsersListPage() { const { data } = useUsers(); // useSuspenseQuery return ( <main className="container"> <PageHeader title="Users" subtitle="Manage accounts" /> <UsersTable users={data} /> </main> ); }
tsx// Create <main className="container"> <PageHeader title="Add User" /> <UserForm onSubmit={(input) => createUser.mutate(input)} /> </main> // Edit <main className="container"> <PageHeader title="Edit User" /> <UserForm defaultValues={toFormValues(user)} onSubmit={(input) => updateUser.mutate({ id, input })} /> </main>
src/app/layouts/
AppLayout.tsx # top bar + sidebar + <Outlet />
AuthLayout.tsxtsxconst router = createBrowserRouter([ { element: <AppLayout />, children: [ { path: '/users', element: <UsersListPage /> }, { path: '/users/new', element: <UserCreatePage /> }, { path: '/users/:id/edit', element: <UserEditPage /> }, { index: true, element: <Navigate to="/users" replace /> }, ], }, { element: <AuthLayout />, children: [{ path: '/login', element: <LoginPage /> }], }, ]);
| Layer | Purpose | Rules | |---|---|---| | app/ | Wiring (routing, providers, layouts, config) | No feature code here | | shared/ | Stateless UI + generic hooks/utils | No data fetching/HTTP | | features/ | Domain UI + data + local store | Export via index.ts | | test/ | Global test utils | Reuse across features |
| Concern | Tool | |---|---| | Framework | Vite 6 (SPA) or Next.js 16 (SSR/RSC) | | Language | TypeScript 5.8+ strict | | Compiler | React Compiler 1.0 | | Routing (SPA) | TanStack Router (new) / React Router v7 (existing) | | Server cache | TanStack Query v5 | | Client state | Zustand | | Forms | React Hook Form | | Styling | Tailwind CSS v4 + shadcn/ui | | Unit testing | Vitest + React Testing Library | | E2E testing | Playwright | | API mocking | MSW | | Monorepo | Nx or Turborepo |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 19,482 | 24,996 | +28% | 1 | 1 | 0% | 3,726 | 10,329 | +177% | 0 | 0 | — |
case-02 | fail→pass | 18,960 | 20,711 | +9% | 1 | 1 | 0% | 3,164 | 8,059 | +155% | 0 | 0 | — |
case-03 | pass→pass | 11,209 | 7,888 | -30% | 1 | 1 | 0% | 1,461 | 6,107 | +318% | 0 | 0 | — |
case-04 | pass→pass | 13,158 | 13,823 | +5% | 1 | 1 | 0% | 2,345 | 7,180 | +206% | 0 | 0 | — |
case-05 | fail→pass | 16,488 | 16,934 | +3% | 1 | 1 | 0% | 2,688 | 7,978 | +197% | 0 | 0 | — |
case-06 | pass→pass | 14,616 | 18,844 | +29% | 1 | 1 | 0% | 2,380 | 7,330 | +208% | 0 | 0 | — |
case-07 | fail→pass | 12,285 | 13,766 | +12% | 1 | 1 | 0% | 1,925 | 6,916 | +259% | 0 | 0 | — |
case-08 | fail→pass | 14,652 | 11,960 | -18% | 1 | 1 | 0% | 2,379 | 6,507 | +174% | 0 | 0 | — |
case-09 | pass→pass | 10,712 | 9,870 | -8% | 1 | 1 | 0% | 1,916 | 6,347 | +231% | 0 | 0 | — |
case-10 | fail→pass | 12,259 | 10,912 | -11% | 1 | 1 | 0% | 2,039 | 6,491 | +218% | 0 | 0 | — |
case-11 | pass→pass | 11,048 | 8,086 | -27% | 1 | 1 | 0% | 2,062 | 6,081 | +195% | 0 | 0 | — |
case-12 | pass→pass | 14,448 | 15,158 | +5% | 1 | 1 | 0% | 2,288 | 7,527 | +229% | 0 | 0 | — |
case-13 | pass→pass | 13,177 | 13,406 | +2% | 1 | 1 | 0% | 2,159 | 6,914 | +220% | 0 | 0 | — |
case-14 | fail→pass | 8,932 | 7,630 | -15% | 1 | 1 | 0% | 1,442 | 5,851 | +306% | 0 | 0 | — |
case-15 | pass→pass | 8,783 | 6,030 | -31% | 1 | 1 | 0% | 1,717 | 5,792 | +237% | 0 | 0 | — |
case-16 | pass→pass | 14,397 | 18,377 | +28% | 1 | 1 | 0% | 2,157 | 6,215 | +188% | 0 | 0 | — |
case-17 | pass→pass | 11,375 | 14,192 | +25% | 1 | 1 | 0% | 2,324 | 7,004 | +201% | 0 | 0 | — |
case-18 | pass→pass | 16,697 | 14,291 | -14% | 1 | 1 | 0% | 2,983 | 7,059 | +137% | 0 | 0 | — |
case-19 | pass→pass | 6,330 | 5,672 | -10% | 1 | 1 | 0% | 1,184 | 5,611 | +374% | 0 | 0 | — |
case-20 | pass→pass | 30,060 | 17,268 | -43% | 1 | 1 | 0% | 2,795 | 7,691 | +175% | 0 | 0 | — |
case-21 | pass→pass | 13,400 | 25,323 | +89% | 1 | 1 | 0% | 2,333 | 7,677 | +229% | 0 | 0 | — |
case-22 | pass→pass | 7,958 | 4,511 | -43% | 1 | 1 | 0% | 1,490 | 5,374 | +261% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.