Overview
react-ecc-starter is a batteries-included boilerplate for production React 19 SPAs. The first week of every new project goes to re-wiring the same twenty things: auth, routing, theme, forms, error boundaries, i18n, deploy configs, lint hooks. This repo does all of it — modern, opinionated, out of the way — so day one is product code.
Unlike create-react-app-style generators that stop at scaffolding, react-ecc-starter also bundles Everything Claude Code (ECC): 11 subagents, 15 slash commands, 11 skills, layered rules, AgentShield, and a VITE_* secret guard — all audited and ready for real-world use.
- Build: Vite 6 + React 19.2 + TypeScript 5 strict, React Compiler enabled (no manual
useMemo/useCallback). - Routing: React Router v7 data router with lazy routes, route-level + root error boundaries.
- Server state: TanStack Query v5 + Zod — schema-validated, typed errors, optimistic-update pattern.
- Client state: Zustand v5 with
persist, strict boundary against server state. - UI: Tailwind CSS v4 + shadcn/ui pattern; tokens in
@theme {}, primitives owned in-repo (no CLI). - Auth: Mock JWT +
ProtectedRoute+AuthStore, MSW-backed in dev; swap in a real provider when ready. - i18n: react-i18next + LanguageDetector, ships with EN + VI, type-safe keys.
- Tests: Vitest 2 + Testing Library + Playwright, 80% coverage threshold.
- Security: AgentShield 1.4, secret guard preventing
VITE_*leaks.
Why I built it
After 10 years shipping React, every new project rebuilds the same five things: auth guards, layout, forms, tables, role checks. That's 1-2 weeks of work each time.
react-ecc-starter is a battle-tested kit for those pieces — strict enough for production, open enough to add your own domain logic without fighting the framework.
What sets it apart is the bundled Everything Claude Code (ECC) integration. Instead of configuring subagents, slash commands, and skills from scratch for every project, this repo ships with 37 audited AI components. Open Claude Code inside the project and review, scaffold, and debug patterns are already there — no week-one AI tooling setup.
Goal: day one is product code, not scaffolding; week one ships a feature, not still tuning TypeScript path aliases.
Architecture
Feature-first organisation with a strict server-state / client-state boundary.
src/routes/— React Router v7 (data router) route definitions, lazy-loaded with per-route error boundaries.src/components/— UI primitives (shadcn/ui owned in-repo) plus composite components.src/features/— domain logic by feature (auth, posts, contact, dashboard).src/stores/— Zustand stores with thepersistmiddleware.src/lib/— typed HTTP client, Zod schemas, pure utilities (no React deps).src/mocks/— MSW 2 handlers, loaded only in dev (tree-shaken from the prod bundle).src/i18n/— EN + VI namespaces with type-safe keys..claude/— 11 subagents + 15 slash commands + 11 skills (ECC), audited by AgentShield.
Path alias @/ points at ./src/ — no ../../ imports. Enforced by the ESLint flat config.
Bundle: MSW + dev tooling tree-shake out of production. AgentShield blocks commits when it detects secrets (VITE_* outside the allowlist) or prompt-injection inside .claude/.
Installation
Get the project running locally in a couple of commands.
git clone https://github.com/pldkhoi/react-ecc-starter.git my-app
cd my-appbun installcp .env.example .env.local
# fill in VITE_APP_NAME, VITE_API_URL
bun dev # http://localhost:5173bunx playwright installUsage
Common patterns and snippets to get started fast.
import { createBrowserRouter, RouterProvider } from 'react-router';
import { ProtectedRoute } from '@/features/auth/protected-route';
import { DashboardPage } from '@/features/dashboard/page';
import { LoginPage } from '@/features/auth/login-page';
const router = createBrowserRouter([
{ path: '/login', element: <LoginPage /> },
{
path: '/dashboard',
element: (
<ProtectedRoute>
<DashboardPage />
</ProtectedRoute>
),
},
]);
export function App() {
return <RouterProvider router={router} />;
}import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Form, FormField } from '@/components/ui/form';
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
type FormValues = z.infer<typeof schema>;
export function LoginForm() {
const form = useForm<FormValues>({ resolver: zodResolver(schema) });
return (
<Form {...form} onSubmit={(values) => console.log(values)}>
<FormField name="email" label="Email" type="email" />
<FormField name="password" label="Password" type="password" />
<button type="submit">Sign in</button>
</Form>
);
}import { useQuery } from '@tanstack/react-query';
import { z } from 'zod';
import { http } from '@/lib/http';
const Post = z.object({
id: z.string(),
title: z.string(),
body: z.string(),
});
const PostList = z.array(Post);
export function usePosts() {
return useQuery({
queryKey: ['posts'],
queryFn: async () => PostList.parse(await http.get('/posts')),
});
}Roadmap
- Storybook coverage for every UI primitive in
src/components/ui/. - Derivative templates: e-commerce dashboard, CRM admin, internal tool kit — split into separate branches for fast cloning.
- Extract
@kpboards/ui+@kpboards/formsinto standalone npm packages for cross-project reuse. - React 19 → React 20 migration guide as soon as the RC stabilises, with an auto-fix script for breaking changes.
- ECC: add a
/migration-architectsubagent that auto-drafts an ADR whenever a major dependency changes.