Next.js + React + TypeScript House Style
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.
The skill file
Cursor · .cursorrules.cursorrules2876 chars
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.
Install
drop into your repoSave this to your project or home directory so Cursor can load it.
path./.cursorrules
Discussion
What people are saying
Forks
0 forks of this skillLoading forks…