Skip to content

Next.js + React + TypeScript House Style

Cursor.cursorrulesCoding

Opinionated App Router conventions covering server/client component boundaries, suspense, data fetching, and file naming.

Use when: Drop into any Next.js 14+ App Router project with TypeScript to keep generated code aligned with server-first React conventions.

@library0 upvotes0 remixes0 comments

Get this skill

Add the skill file with the CLI, by hand, or over MCP.

Save this to your project or home directory so Cursor can load it.

npx goodprompts add nextjs-react-cursorrules
Saves to./.cursorrules
npx goodprompts add nextjs-react-cursorrules
Saves to ./.cursorrules

Skill file

.cursorrules2,876 characters
You are working in a Next.js App Router project with React 19 and strict TypeScript.
## Component boundaries
- Every component is a Server Component unless it uses state, effects, refs, or browser APIs. Only then add `"use client"` — and add it to the smallest leaf component possible, never to a page or layout.
- Never pass functions, class instances, or Dates as props across the server/client boundary. Serialize to primitives or move the logic server-side.
- Pages (`page.tsx`) and layouts (`layout.tsx`) stay server components, always. If a page needs interactivity, extract a client child.
- Co-locate route-private components in a `_components/` folder inside the route segment. Shared components live in `src/components/`.
## Data fetching
- Fetch in Server Components with `async`/`await` directly in the component body. Never fetch in a `useEffect`.
- Parallelize independent fetches: start promises first, then `await Promise.all([...])`. Sequential awaits for independent data are a bug.
- Wrap slow data branches in `<Suspense fallback={<XSkeleton />}>` with a dedicated skeleton component, not a generic spinner. One Suspense boundary per independent data region.
- Mutations go through Server Actions (`"use server"`) validated with Zod at the top of the action. Call `revalidatePath`/`revalidateTag` after every write; never rely on client-side refetch.
- Use `useActionState` for form pending/error state. Do not hand-roll `isSubmitting` booleans.
## Components and naming
- `const` arrow components with named exports: `export const UserCard = (props: UserCardProps) => {...}`. Default exports only where Next.js requires them (`page`, `layout`, `error`, `loading`, `not-found`).
- Props types are named `<Component>Props` and declared directly above the component. Use `type`, not `interface`, unless declaration merging is required.
- Files are kebab-case (`user-card.tsx`); components are PascalCase. One exported component per file.
- Event handler props are `onX`; the handler implementations are `handleX`.
## TypeScript
- No `any`, no non-null assertions (`!`), no `as` casts except `as const`. If a type is unknown, narrow it with a type guard.
- Type `searchParams` and `params` as Promises and `await` them (Next 15 behavior). Validate `searchParams` with Zod before use.
- Derive types from runtime sources: `z.infer<typeof schema>`, `ReturnType<typeof fn>` — do not duplicate shapes by hand.
## Things to never do
- No `useEffect` for derived state, data fetching, or syncing props to state.
- No `router.push` for links — use `<Link>`; reserve imperative navigation for post-mutation redirects.
- No `<img>`; use `next/image` with explicit `width`/`height` or `fill`.
- No barrel files (`index.ts` re-export hubs) — they break tree-shaking and slow the dev server.
- No `process.env` access outside a single validated `env.ts` module.
Comments

Comments

Sign in to commentSign in

Remixes

Loading remixes…