Home › React › React 19 + Vite SPA rules
React 19 + Vite SPA rules
Cursor rules for React 19 single-page apps on Vite 6+ with TypeScript, hooks, and Vitest.
CursorReact
How to use
Save this as .cursor/rules/react.mdc in your repository root, commit it, and your agent will pick it up automatically. Edit the commands to match your project.
.cursor/rules/react.mdcDownload ---
description: React 19 + Vite SPA conventions
globs: ["src/**/*.{ts,tsx}"]
alwaysApply: false
---
# React 19 + Vite
- Function components and hooks only. One component per file, `PascalCase.tsx`; hooks in `src/hooks/useThing.ts`.
- Props are typed with a local `type Props = {...}`; no `React.FC`. Avoid `any`; use `unknown` and narrow.
- Derive values during render instead of syncing them with `useEffect`. An effect is only for talking to something outside React (DOM, subscriptions, timers).
- Server data goes through the project's data layer (TanStack Query or the existing fetch wrapper), never ad hoc `fetch` inside `useEffect`.
- Use React 19 APIs where they fit: `use()` for promises/context, form `action` + `useActionState`, `useOptimistic`. Pass `ref` as a normal prop; don't add `forwardRef` to new code.
- Keys are stable ids, never array indexes for lists that reorder.
- Env vars are read via `import.meta.env.VITE_*` only; never put secrets in client code.
- Accessibility: real `<button>`/`<a>`, labels on inputs, visible focus states.
## Don't
- Don't add a state library for local state; `useState`/`useReducer` first.
- Don't memoize by reflex (`useMemo`/`useCallback`) unless profiling or a dependency array needs it. If the React Compiler is enabled, don't memoize at all.
- Don't edit `vite.config.ts` or add dependencies without saying why.
## Done when
`npm run lint && npx tsc --noEmit && npx vitest run && npm run build` all pass.