Cursor rules for react-router-dashboard-saas-template

These rules describe how to work in this repo using Cursor: file layout, React Server Components boundaries, where actions/forms live, and UI/data patterns.

Views1
PublishedJan 15, 2026

Loading actions...

5 minBeginnerpromptSingle file

Skill content

Main instructions and any bundled files for this skill.

markdown

Cursor rules for react-router-dashboard-saas-template

These rules describe how to work in this repo using Cursor: file layout, React Server Components boundaries, where actions/forms live, and UI/data patterns.

Stack overview

  • React 19 + React Router 7 (RSC enabled via @vitejs/plugin-rsc)
  • Server rendering entry at src/entry.rsc.tsx; SSR HTML generated by src/entry.ssr.tsx
  • Tailwind CSS v4 + daisyUI v5 (semantic component classes)
  • Drizzle ORM (PostgreSQL) with drizzle-kit migrations
  • Conform (@conform-to/react) + zod/v4 for form validation
  • Session and request-scoped context via AsyncLocalStorage

Environment

  • Required env: DATABASE_URL, SESSION_SECRET
  • Start: pnpm dev. Build: pnpm build. Typecheck: pnpm typecheck. DB: pnpm db, pnpm db:generate, pnpm db:migrate, pnpm db:studio.

Directory layout and conventions

  • src/routes/
    • Each route folder typically contains:
      • route.tsx → React Server Component (RSC) entry for the route.
      • client.tsx → co-located Client Components for that route ("islands"). Use "use client" pragma.
      • client-on.ts → small client helpers for imperative UI interactions (e.g., opening modals).
      • Optional handle.ts (see src/routes/app/handle.ts) to expose server-only pieces via the createHandle pattern.
    • Root shells:
      • src/routes/root/route.tsx (layout shell + error boundary export from client)
      • src/routes/marketing/route.tsx (public shell)
      • src/routes/app/route.tsx (authenticated app shell)
  • src/actions/<domain>/
    • actions.ts → server actions (must start with "use server").
    • schema.tszod/v4 schemas used by the actions and forms.
    • Existing domains: auth, organization, invitation, profile.
  • src/components/ → shared components across routes.
    • @/components/form → Conform + zod v4 opinionated wrappers (useForm, Form, Input, etc.).
    • src/components/ui/ → low-level UI primitives (card, modal, etc.).
  • src/db/
    • schema.ts → Drizzle table definitions.
    • queries/* and mutations/* → data access layer separated by read/write.
    • index.tsgetDb() pool + drizzle instance (reused via global).
  • src/lib/
    • session.ts → cookie session with ALS; use getSession(), destroySession().
    • auth.tsgetUser(), requireUser(), and route unstable_middleware helpers.
    • cache.ts → response caching and dataloader batching.

React Server Components model

  • Route files (route.tsx) are server by default. Do data fetching here (DB, session, etc.). Avoid DB access in client components.
  • Client components must opt-in with "use client" and should live in client.tsx (or adjacent files) within the route directory.
  • Pass server-only values to client via props; avoid leaking server functions or DB clients.
  • Use the createHandle/getServerHandle pattern when a route needs to expose server-only components to a parent shell (see src/routes/app/handle.ts).
  • Route middleware: export unstable_middleware from the server route module to protect or redirect (see redirectIfLoggedInMiddleware, requireUserMiddleware).
  • Caching: call cacheRoute() at the top of server route components when appropriate to set CDN/edge cache headers.

Forms + actions pattern (Conform + zod/v4)

  • Always use zod/v4 and @conform-to/zod/v4 to match the configured version.
    • Example imports:
      • import { z } from "zod/v4"
      • import { parseWithZod } from "@conform-to/zod/v4"
  • Server action signature:
    • In src/actions/<domain>/actions.ts define actions with "use server" and the shape (prev: SubmissionResult | undefined, formData: FormData) => Promise<SubmissionResult>.
    • Parse on the server: const submission = parseWithZod(formData, { schema }).
    • On validation failure: return submission.reply({ ...options }).
    • On success: perform side effects, optionally redirect(...), and return submission.reply({ resetForm: boolean }).
    • Use requireUser() inside actions that need auth.
  • Client form usage (see src/routes/marketing/login/client.tsx):
    • Wire the server action using React 19’s useActionState: const [lastResult, action, pending] = useActionState(serverAction, undefined).
    • Initialize Conform via our wrapper: const [form, fields] = useForm({ action, lastResult, schema }).
    • Render with <Form action={action} form={form}> and use <Input field={fields.email} ... /> etc.
    • Use <FormErrors form={form} /> and <FormSuccessMessage lastResult={lastResult}>...</FormSuccessMessage>.
    • Keep redirect/query state in hidden inputs when needed (e.g., redirectTo).
  • Where to put schemas:
    • Define schemas in src/actions/<domain>/schema.ts and import into both the action and the client form.
  • Naming:
    • Existing naming is mixed (login, signup, createOrganizationAction, updateName, etc.). Prefer descriptive verbs; suffix with Action when it clarifies intent, but keep consistency within a domain.

Data access (Drizzle)

  • Define tables in src/db/schema.ts.
  • Get a DB instance via getDb(); do not instantiate pools in actions/components.
  • Separate reads/writes: put read functions in src/db/queries/* and mutations in src/db/mutations/*.
  • Keep transactions and authorization checks in server actions or server route loaders (not in client).
  • Generate and run migrations with drizzle-kit scripts.

Auth and session

  • Use getUser() to read current user (may be undefined). Use requireUser() when user must exist.
  • For route-level protection, add unstable_middleware = [requireUserMiddleware] to route.tsx.
  • Login/signup set the session via getSession().set("user", { id }); logout destroys it via destroySession().
  • Redirect using React Router’s redirect() only from server contexts.

Routing specifics

  • The route shells use daisyUI and Tailwind for layout (navbar, drawer, etc.).
  • The marketing shell (src/routes/marketing/route.tsx) shows how to open auth modals from the navbar using client-on.ts helpers.
  • The app shell (src/routes/app/route.tsx) demonstrates co-locating a server-provided SidebarContent via the handle API and rendering notifications fed by server data.

UI and styling

  • Tailwind v4 with daisyUI v5 (semantic-first):
    • Prefer daisyUI component classes (e.g., btn, input, card) and semantic colors (primary, base-100, etc.).
    • Use container pattern max-w-screen-xl mx-auto px-4 for page shells.
    • Use responsive helpers (sm:menu-horizontal, lg:drawer-open).
  • Shared UI primitives live under src/components/ui/*. Add new ones here if broadly reusable. Route-specific UI belongs next to the route in client.tsx or sibling files.

Adding a new feature (checklist)

  1. Routing: create a new folder in src/routes/..., add route.tsx (server). If interactive UI is needed, add client.tsx with "use client" and render it from the server route.
  2. Data: add query/mutation helpers in src/db/queries/* / src/db/mutations/* as needed. Reuse getDb().
  3. Actions: create src/actions/<domain>/{schema.ts,actions.ts} with zod v4 schemas and server actions.
  4. Form: in the route’s client.tsx, use useActionState, useForm({ action, lastResult, schema }), and <Form>/<Input> components.
  5. Auth: add unstable_middleware to routes requiring auth, and call requireUser() inside actions.
  6. Caching: call cacheRoute() in server route.tsx if the page can be cached; avoid for highly dynamic/authenticated content.
  7. Styling: use daisyUI components and semantic colors; keep shared primitives in src/components/ui/*.

TypeScript and code style

  • TS is strict; avoid any. Export explicit types for public helpers.
  • Use the @/* path alias from tsconfig.json.
  • Keep functions small and descriptive; prefer early returns; handle errors close to where they occur.

Do’s and Don’ts

  • Do parse and validate all form input on the server with zod v4.
  • Do keep business logic in server actions or server route modules, not in client components.
  • Don’t mutate session after response generation; use helpers from src/lib/session.ts within the server request lifecycle.
  • Don’t access the DB from client code.
  • Don’t bypass Conform’s submission.reply pattern; it powers lastResult and error display.
Share: