Opinionated rule set for building large-scale, high-performance React front-end applications using TypeScript, Next.js, Vite, and modern tooling.
You're building React applications that need to scale beyond the prototype phase. You know the drill—what starts as a simple component tree becomes an unmanageable maze of prop drilling, inconsistent patterns, and performance bottlenecks. You've hit the wall where "just add another component" turns into technical debt that slows your entire team down.
React gives you the tools, but it doesn't give you the blueprint. Without clear architectural decisions, you end up with:
The real challenge isn't learning React—it's making the right architectural decisions that keep your codebase maintainable as it grows from hundreds to thousands of components.
These Cursor Rules implement battle-tested patterns from large-scale React applications. Instead of debating architecture decisions in every PR, you get consistent, opinionated guidance that enforces best practices automatically.
Here's what you get:
Architectural Consistency: Every component follows the same structure—imports, types, constants, hooks, component body, memoization. Your team stops reinventing patterns.
Performance by Default: Automatic optimization guidance with React.memo, code splitting, and bundle analysis integration. No more accidental performance regressions.
Type Safety Without Friction: Strict TypeScript patterns that catch errors at compile time, not in production. Interface over type, named exports over default exports, unknown over any.
Scalable Testing Strategy: Integrated patterns for unit, integration, and end-to-end testing that actually work with your architecture.
// Developer A's approach
export default function UserProfile(props) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// ... mixed patterns, unclear error handling
}
// Developer B's approach
class UserProfile extends React.Component {
// ... class component with different patterns
}
import { useUser } from '@/hooks/use-user';
interface Props {
readonly userId: string;
}
export const UserProfile: React.FC<Props> = ({ userId }) => {
const { user, error, loading } = useUser(userId);
if (error) return <ErrorMessage error={error} />;
if (loading) return <ProfileSkeleton />;
return <ProfileCard user={user} />;
};
Before: Manual bundle analysis and optimization
# Manual process every sprint
npm run build
npm run analyze
# Manually identify large bundles
# Manually implement code splitting
After: Automated performance monitoring
// Automatic code splitting for heavy components
const HeavyChart = lazy(() => import('./heavy-chart'));
// Automatic memoization guidance
export const ExpensiveList = React.memo(({ items }) => {
// Component automatically optimized
});
Before: Fragmented testing approaches
// Inconsistent testing patterns
test('component works', () => {
// Different testing approaches per developer
});
After: Unified testing strategy
// Consistent hook testing
import { renderHook } from '@testing-library/react-hooks';
import { useUser } from '../use-user';
describe('useUser', () => {
it('handles loading and error states', async () => {
const { result, waitForNextUpdate } = renderHook(() => useUser('123'));
// Consistent patterns across all tests
});
});
.cursorrules file in your project rootsrc/
app/ # Next.js 13+ app router
components/ # Domain-specific components
hooks/ # Custom hooks for stateful logic
ui/ # Reusable design system components
lib/ # Shared utilities
styles/ # Global styles and themes
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"exactOptionalPropertyTypes": true
}
}
Create your first component using the enforced pattern:
import { useState, useEffect } from 'react';
interface Props {
readonly title: string;
}
export const MyComponent: React.FC<Props> = ({ title }) => {
// Cursor will guide you through the correct patterns
return <div>{title}</div>;
};
// next.config.js
module.exports = {
webpack: (config) => {
// Automatic bundle analysis integration
config.plugins.push(new BundleAnalyzerPlugin());
return config;
},
};
Stop debating React architecture decisions. Start building scalable applications with proven patterns that work at scale. These rules transform your development workflow from reactive problem-solving to proactive architecture that supports your team's growth.
The difference between a React app that scales and one that doesn't isn't the complexity of your components—it's the consistency of your architecture decisions. Make those decisions once, enforce them automatically, and focus on building features that matter.
Your future self (and your teammates) will thank you for implementing these patterns today rather than refactoring them tomorrow.
You are an expert in React, TypeScript, Next.js, Remix, Vite, Webpack 5, Module Federation, Jest, React Testing Library, styled-components/Emotion, and modern performance tooling.
Key Principles
- Prefer functional, declarative programming. Ban class components.
- Co-locate stateful logic in reusable custom hooks; keep components as thin presentational layers.
- Build for progressive enhancement: SSR/SSG first, then hydrate with client interactivity.
- Optimize before you scale: measure using React DevTools Profiler & Webpack Bundle Analyzer before micro-optimizing.
- Small, composable units: one component per file; compose, don’t inherit.
- Consistency > cleverness: follow the same patterns everywhere to flatten the learning curve.
- Folders and files use kebab-case; React components use PascalCase; variables & functions use camelCase.
TypeScript (primary language)
- Always use `strict: true`. Disallow `any`; use `unknown` + type-guards instead.
- Prefer `interface` for public shapes, `type` for unions/intersections.
- Never use default exports; prefer named exports to enable better tree-shaking and refactoring.
- File skeleton order:
1. Imports (std → 3rd-party → aliases → relative)
2. Types & interfaces
3. Constants
4. Hooks/helpers
5. Component/body
6. Memoization & export
- Example functional component template:
```tsx
import { useUser } from '@/hooks/use-user';
interface Props {
readonly greeting?: string;
}
export const Welcome: React.FC<Props> = ({ greeting = 'Hello' }) => {
const { user, error } = useUser();
if (error) return <ErrorMessage error={error} />; // early return
if (!user) return <Spinner />; // loading state
return <h1>{greeting}, {user.name}</h1>; // happy path
};
```
Error Handling & Validation
- Catch & surface async errors inside hooks; return `{data, error}` or throw for boundary.
- Wrap pages in React 18 error boundaries; provide retry actions.
- SSR/SSG handlers (`getServerSideProps`, loaders) must `try/catch` and set appropriate HTTP status codes.
- Validate external data with Zod or Yup at the edge (server loaders) before passing to the client.
- Use early returns to avoid deep nesting; log full error stacks serverside, expose sanitized messages clientside.
React / Next.js Rules
- Use the `/app` directory & React Server Components when on Next 13+.
- Place shared UI primitives in `ui/`, domain widgets in `components/`, and data-fetching hooks in `hooks/`.
- Implement routing via file-system routes; keep loaders/data functions colocated with the route file.
- Opt-in to edge runtime where possible for lower latency.
- Implement Incremental Static Regeneration (ISR) for frequently changing content.
- Configure `next.config.js` for webpack 5 Module Federation if adopting micro-frontends.
- Always expose a `HEAD` element per page for SEO.
- Throw user-friendly `createError({statusCode, message})` that the global `_error.tsx` boundary can catch.
Performance
- Turn on React Strict Mode & Concurrent Features; fix double-invoke side-effects.
- Memoize expensive components with `React.memo` and deep compare sparingly.
- Use dynamic imports + `React.lazy`/`next/dynamic` to code-split heavy widgets.
- Defer non-critical CSS via `styled-components`’s `StyleSheetManager` + `disableCSSOMInjection`.
- Cache data at the fetch layer with SWR/React Query; configure stale-while-revalidate policies.
- Store large lists in virtualized components (e.g., `react-window`).
Testing
- Unit test hooks with `@testing-library/react-hooks`; mock network with `msw`.
- Use jest + `jest-each` for table-driven tests.
- Aim for 80% branch coverage, but prioritize critical paths.
- Write integration tests for pages using Playwright; run them in CI with GitHub Actions.
Security
- Default all fetches to `credentials: 'same-origin'` & CSRF token headers.
- Sanitize user input on the server; escape output when dangerously setting HTML.
- Activate React DOM Purify for third-party HTML.
- Set `Content-Security-Policy` headers in Next.js via `middleware.ts`.
Directory Structure Example
```
src/
app/ # Next.js 13 app router pages
components/ # domain-specific UI
hooks/ # custom hooks
ui/ # headless design-system primitives
lib/ # shared util functions
styles/ # theme + global stylesheets
tests/ # test utilities & fixtures
```
Common Pitfalls & Guards
- Don’t mutate props or state; always spread into new objects.
- Watch for stale closures in hooks—list all dependencies or disable rule with justification.
- Avoid `any` prop drilling; lift state or use context/Zustand.
- Keep bundle size under 250 kB gzipped; fail CI if exceeded (`webpack-size-plugin`).
- Never ship a blocking `<Image>` without explicit width & height; causes CLS.