A minimal, modern React starter built on Vite — with data fetching, API mocking, and testing already wired up.
- ⚛️ React 19 + TypeScript
- ⚡ Vite 8 with @vitejs/plugin-react
- 🔄 TanStack Query 5 with a
useCustomQuerywrapper hook - 🌐 Axios API client with interceptors and error mapping
- 🎭 MSW 2 API mocking — browser worker in dev, Node server in tests
- 🧪 Vitest 4 + Testing Library (jsdom, coverage via v8)
- 🧹 ESLint 10 flat config + Prettier
- 🪝 Husky + lint-staged pre-commit hook
- 🤖 GitHub Actions CI (lint, typecheck, test, build)
- Node.js ≥ 22.12 (see
.nvmrc) - npm
npm install
npm run devnpm install also sets up the Husky git hooks via the prepare script.
| Script | Description |
|---|---|
npm run dev |
Start the dev server with HMR (MSW mocks enabled) |
npm run build |
Typecheck and build for production into build/ |
npm run preview |
Preview the production build locally |
npm test |
Run tests in watch mode |
npm run test:ui |
Run tests with the Vitest UI |
npm run test:coverage |
Run tests once with a coverage report |
npm run lint |
Lint with ESLint (zero warnings allowed) |
npm run tsc |
Typecheck without emitting |
npm run prettier |
Format the whole project with Prettier |
src/
├── main.tsx # Entry point — mounts App, starts the MSW worker in dev
├── App.tsx # Demo component fetching a user via useCustomQuery
├── renderWithProvider.tsx # StrictMode + QueryClientProvider wrapper
├── config.ts # BASE_URL, REQUEST_TIMEOUT
├── common/hooks/ # useCustomQuery — TanStack Query wrapper around the API client
├── service/mocks/ # MSW handlers, browser worker (dev), Node server (tests)
└── utils/api/ # Axios client: instance factory, interceptors, error mapping
setup/tests/ # vitest-setup.ts — jest-dom matchers + MSW server lifecycle
.github/workflows/ # CI pipeline
Requests go through the axios client singleton (src/utils/api/client.ts) via the useCustomQuery hook:
const query = useCustomQuery<User>({
queryKey: ['user'],
config: { url: '/user', method: 'get' },
})Set BASE_URL and REQUEST_TIMEOUT in src/config.ts.
Handlers live in src/service/mocks/handlers.ts.
- Dev: the browser worker starts automatically in
npm run dev(seesrc/main.tsx). SetVITE_MSW=1to enable it in other modes. - Tests: the Node server is wired up globally in
setup/tests/vitest-setup.ts.
After upgrading the msw package, regenerate the worker script with npx msw init public/.
On every commit, lint-staged runs ESLint (--fix) and Prettier on staged files (.husky/pre-commit).
.github/workflows/ci.yml runs lint, typecheck, tests with coverage, and the production build on every push and pull request to master.
CC0 1.0 — public domain, use it however you like.