Actionable coding standards for building high-performance, fully-typed Next.js 15 applications styled with Tailwind CSS.
Stop wrestling with inconsistent code patterns, slow builds, and runtime surprises. These actionable Cursor Rules establish bulletproof development standards for Next.js 15 applications that scale.
You're building with Next.js 15's cutting-edge features—App Router, Server Components, Turbopack—but your team still struggles with:
These rules solve these specific problems with battle-tested patterns that work at scale.
The Next.js 15 TypeWind Rules provide a complete development framework that:
Enforces Server-First Architecture: Defaults to React Server Components with explicit use client opt-ins, preventing hydration mismatches and reducing bundle sizes.
Guarantees Type Safety: Strict TypeScript configuration with runtime validation using Zod, typed environment variables, and structured error handling.
Optimizes Performance by Default: Built-in streaming, code splitting, image optimization, and Turbopack integration that delivers measurable speed improvements.
Standardizes Component Patterns: Consistent folder structure, import organization, and Tailwind class ordering that scales from solo projects to enterprise teams.
Instead of juggling between different configuration files and remembering framework-specific patterns, these rules provide instant context:
// ✅ Clear server/client boundaries with explicit typing
export default async function DashboardPage() {
const data = await fetchDashboard(); // Server-side, fully typed
return (
<DashboardShell data={data}>
<Suspense fallback={<Spinner />}>
<InteractiveStats /> {/* Client component, clearly marked */}
</Suspense>
</DashboardShell>
);
}
Built-in validation patterns catch errors at compile time, not in production:
// ✅ Environment variables validated on boot
export const env = z
.object({
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(1),
})
.parse(process.env);
Feature-scoped folder structure keeps related code together, reducing file hunting:
src/features/dashboard/
├── components/stats.tsx
├── hooks/use-dashboard-data.ts
├── types.ts
└── __tests__/stats.test.tsx
You spend 45 minutes tracking down why your component renders differently on server and client, finally discovering a Client Component was being treated as a Server Component.
// ✅ Server Component (default)
async function UserProfile({ userId }: { userId: string }) {
const user = await fetchUser(userId); // Server-side data fetching
return <ProfileDisplay user={user} />;
}
// ✅ Client Component (explicit)
'use client';
function InteractiveChart({ data }: ChartProps) {
const [selectedPeriod, setSelectedPeriod] = useState('week');
return <Chart data={data} period={selectedPeriod} />;
}
API routes return different error formats, making frontend error handling unpredictable and causing user-facing crashes.
// ✅ Consistent error handling across all API routes
export async function GET(): Promise<NextResponse> {
try {
const users = await fetchUsers();
return NextResponse.json({ data: users });
} catch (err) {
return handleError(err); // Standardized error response
}
}
Your client bundles are 40% larger than necessary because you're importing server-only code in Client Components.
// ✅ Dynamic imports for heavy Client Components
const ExpensiveChart = dynamic(() => import('./expensive-chart'), {
ssr: false,
loading: () => <ChartSkeleton />
});
.cursorrules fileUpdate your tsconfig.json for strict mode:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"exactOptionalPropertyTypes": true
}
}
Add path aliases for cleaner imports:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
Migrate to the feature-oriented structure:
mkdir -p src/{app,components,features,lib,hooks,styles,tests}
Start with your most frequently modified components, applying the server-first patterns and explicit type definitions.
Create centralized error types and update API routes to use consistent error responses.
These rules transform Next.js development from a configuration-heavy experience into a streamlined, type-safe workflow that scales with your team and application complexity.
Ready to eliminate development friction? Copy these rules into your .cursorrules file and experience the difference in your next component build.
You are an expert in Next.js 15, React 19, TypeScript 5.x, Tailwind CSS 3.x, Node LTS, Turbopack, ESLint, Prettier, Jest, React-Testing-Library, Playwright, Redux Toolkit, and Zustand.
Technology Stack Declaration
- Framework: Next.js v15 (app router, server components, turbopack)
- Language: TypeScript (strict mode)
- Styling: Tailwind CSS with PostCSS & Autoprefixer
- State: Redux Toolkit or Zustand when global state is unavoidable
- Testing: Vitest/Jest + React-Testing-Library for unit, Playwright for e2e
- Tooling: ESLint (next/core-web-vitals ruleset) + Prettier, Husky + lint-staged, GitHub Actions for CI/CD
Key Principles
- Server-first mindset: default to React Server Components (RSC) and server actions. Opt-in to `use client` only for interactivity.
- Type safety everywhere: `strict` compiler options, typed environment variables, API schemas (zod).
- Performance by default: streaming, code-splitting, lazy loading, image optimization, Turbopack.
- Accessibility (a11y) and security (OWASP Top-10) baked into components.
- Modular, feature-oriented folder structure: colocate UI, logic, tests, and styles.
- Prefer functional & declarative code. No legacy class components.
- Small client bundles: memoize expensive operations, avoid polyfills until needed.
- Convention over configuration: follow Next.js defaults unless there is a compelling reason.
Language-Specific Rules – TypeScript
- Enable `"strict": true`, `noImplicitAny`, `exactOptionalPropertyTypes`.
- Use `interface` for object shapes that will be extended, `type` for unions & primitives.
- Prefer `unknown` over `any`. Narrow with type guards (example below).
```ts
function isUser(u: unknown): u is User {
return typeof u === 'object' && !!u && 'email' in u;
}
```
- Use generics instead of overloaded functions.
- Organize imports: std → external → internal; alphabetize within each group.
- Path aliases: configure `@/` to `/src` via `tsconfig.json`.
- Avoid default exports; always use named exports to enable tree-shaking.
- Use `async/await` not `.then()` chaining; always type async return values (`Promise<User>`)
- Never suppress TypeScript (`// @ts-ignore`) without a TODO and ticket reference.
Error Handling and Validation
- Fail fast: validate function arguments at the top using zod.
- Centralized error types in `src/lib/errors.ts` (e.g., `AppError` with `status`, `code`, `message`).
- Server Actions & API Route handlers must return typed `Result<T, AppError>` or throw an `AppError`.
```ts
export async function GET(): Promise<NextResponse> {
try {
const data = await fetchUsers();
return NextResponse.json({ data });
} catch (err) {
return handleError(err);
}
}
```
- Frontend: use an ErrorBoundary (`app/error.tsx`) for route-level errors.
- Log unexpected errors with `next-axiom` or Sentry; never expose stack traces to clients.
- Validate ENV vars on boot:
```ts
export const env = z
.object({
NODE_ENV: z.enum(['development','test','production']),
DATABASE_URL: z.string().url(),
})
.parse(process.env);
```
Framework-Specific Rules – Next.js 15
Folder Structure
```
src/
├─ app/
│ ├─ layout.tsx # Root layout (+ metadata)
│ ├─ page.tsx # Root route
│ ├─ (marketing)/ # Parallel route group
│ ├─ dashboard/
│ │ ├─ layout.tsx
│ │ ├─ page.tsx
│ │ └─ settings/
│ │ └─ page.tsx
│ └─ api/
│ └─ users/
│ └─ route.ts # API route
├─ components/ # Reusable UI primitives
├─ features/ # Feature-scoped slices (state + UI)
├─ lib/ # Utils (fetcher, errors, auth)
├─ hooks/ # Custom hooks
├─ styles/ # Global CSS + tailwind.css
└─ tests/
```
Routing & Components
- Use file-system routing under `app/` only.
- Default export must be a Server Component; prepend `"use client"` only when using hooks like `useState`.
- Use `generateStaticParams` and `revalidate` for ISR pages.
- Stick to `next/link` and `next/image`; never use `<a>`/`<img>` directly.
- Add explicit `<Suspense>` around Client Components to improve streaming.
- Metadata: export `metadata` objects for SEO (`title`, `description`, `openGraph`).
Data Fetching & Caching
- Prefer server actions for mutations; mark as `"use server"`.
- For read operations use `fetch` with the built-in cache (`{ next: { revalidate: 60 } }`).
- Enable React Cache for expensive pure functions.
- Use edge runtime (`export const runtime = 'edge'`) for latency-sensitive APIs.
Turbopack
- Keep `turbo.json` simple: only pipeline `build`, `lint`, `test`.
- Leverage incremental static regeneration (`ISR`) & fast refresh.
Static Generation
- Pages that rarely change → `export const dynamic = 'force-static'`.
- Use `generateStaticParams` for dynamic static routes.
Tailwind CSS Rules
- Use JIT (`tailwind.config.js` → `mode: 'jit'`).
- Class order: layout → box model → typographic → visuals → states → misc.
- Create composable utility components instead of custom CSS when possible.
- Use `clsx` or `twMerge` for conditional classes.
- Extract repeated class groups to `@apply` in `globals.css` only when:
1. Reused ≥ 3 times, and
2. Semantic clarity improves.
- Theme extension belongs in `tailwind.config.js`; never edit core config.
State Management
- Use React context + hooks locally; promote to global state only if
a component 3+ levels deep requires it or cross-route sharing is needed.
- Complex global state → Redux Toolkit slices colocated in `features/`.
- Simple global/local → Zustand store in `src/stores`.
Testing
- `*.test.tsx` colocated next to source.
- Coverage ≥ 90 %; unit (Vitest), integration (React-Testing-Library), e2e (Playwright).
- Mock `next/navigation` with `@testing-library/react` helpers.
- Use MSW (Mock Service Worker) for API mocking in tests & storybook.
Performance
- Images: always set width/height; pick appropriate `sizes` for responsive.
- Fonts: self-host or use `next/font` with `display: swap`.
- Code splitting: dynamic import Client Components with `{ ssr: false }` only when SSR not required.
- Analyze bundle (`next build && npx nextjs-bundle-analyzer`). Remove heavy deps.
Security
- CSP headers via `next.config.ts` → `headers()`.
- Sanitize user-generated content with `sanitize-html`.
- Use `@next-auth` for auth; session stored in http-only cookies.
- Rotate secret keys; never commit `.env.*`.
CI/CD
- GitHub Actions: `build`, `lint`, `test`, `type-check`.
- Blocks merging to `main` if any step fails.
- Use `vercel pull` to sync env vars locally.
Common Pitfalls & Fixes
- Forgotten `"use client"` → hydration mismatch.
Fix: ensure interactive hooks reside in Client Component wrapper.
- Large Tailwind class strings → use `twMerge` to dedupe.
- API routes without dynamic revalidate header → stale content.
Fix: set `revalidate` or call `revalidatePath()` after mutation.
Example End-to-End Component
```tsx
// src/app/dashboard/page.tsx (Server Component)
import { Suspense } from 'react';
import { DashboardShell } from '@/components/dashboard-shell';
import { Stats } from './stats';
export const metadata = { title: 'Dashboard' };
export default async function DashboardPage() {
const data = await fetchDashboard(); // server-side
return (
<DashboardShell data={data}>
<Suspense fallback={<Spinner />}>
{/* Client component needing interaction */}
<Stats />
</Suspense>
</DashboardShell>
);
}
```
The above rules ensure every Next.js 15 project remains maintainable, fast, and secure while leveraging the full power of TypeScript and Tailwind CSS.