Build full-stack React 19 + React Router 7 apps with RSC, Drizzle ORM, Conform forms, daisyUI, and server actions following jacob-ebey's opinionated conventions.
This skill has safety concerns that you should review before use. Some patterns were detected that may pose a risk.Safety score: 75/100.
KillerSkills scans all public content for safety. Use caution before installing or executing flagged content.
Build full-stack React 19 + React Router 7 applications with React Server Components (RSC), Drizzle ORM, Conform forms, daisyUI, and server actions following the conventions established in jacob-ebey's react-router-dashboard-saas-template.
Required environment variables:
Development commands:
Each route folder typically contains:
Root shells:
Existing domains: `auth`, `organization`, `invitation`, `profile`
1. **Route files (`route.tsx`) are server by default.** Do data fetching here (DB, session, etc.). Avoid DB access in client components.
2. **Client components must opt-in** with `"use client"` and should live in `client.tsx` (or adjacent files) within the route directory.
3. **Pass server-only values to client via props**; avoid leaking server functions or DB clients.
4. **Use the `createHandle`/`getServerHandle` pattern** when a route needs to expose server-only components to a parent shell (see `src/routes/app/handle.ts`).
5. **Route middleware:** export `unstable_middleware` from the server route module to protect or redirect (see `redirectIfLoggedInMiddleware`, `requireUserMiddleware`).
6. **Caching:** call `cacheRoute()` at the top of server route components when appropriate to set CDN/edge cache headers.
Example imports:
```typescript
import { z } from "zod/v4"
import { parseWithZod } from "@conform-to/zod/v4"
```
In `src/actions/<domain>/actions.ts` define actions with `"use server"` and the shape:
```typescript
async (prev: SubmissionResult | undefined, formData: FormData) => Promise<SubmissionResult>
```
Steps:
1. Parse on the server: `const submission = parseWithZod(formData, { schema })`
2. On validation failure: `return submission.reply({ ...options })`
3. On success: perform side effects, optionally `redirect(...)`, and return `submission.reply({ resetForm: boolean })`
4. Use `requireUser()` inside actions that need auth
Wire the server action using React 19's `useActionState`:
```typescript
const [lastResult, action, pending] = useActionState(serverAction, undefined)
```
Initialize Conform via wrapper:
```typescript
const [form, fields] = useForm({ action, lastResult, schema })
```
Render:
```tsx
<Form action={action} form={form}>
<Input field={fields.email} ... />
<FormErrors form={form} />
<FormSuccessMessage lastResult={lastResult}>...</FormSuccessMessage>
</Form>
```
Keep redirect/query state in hidden inputs when needed (e.g., `redirectTo`).
Define schemas in `src/actions/<domain>/schema.ts` and import into both the action and the client form.
Prefer descriptive verbs; suffix with `Action` when it clarifies intent, but keep consistency within a domain.
1. **Define tables** in `src/db/schema.ts`
2. **Get a DB instance** via `getDb()`; do not instantiate pools in actions/components
3. **Separate reads/writes**: put read functions in `src/db/queries/*` and mutations in `src/db/mutations/*`
4. **Keep transactions and authorization checks** in server actions or server route loaders (not in client)
5. **Generate and run migrations** with drizzle-kit scripts
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/*`.
When building a new authenticated feature:
1. Create route folder: `src/routes/app/new-feature/`
2. Add server route: `route.tsx` with data fetching via `getDb()` and `requireUser()`
3. Add client islands: `client.tsx` with form UI
4. Define schema: `src/actions/new-feature/schema.ts` with zod v4
5. Create action: `src/actions/new-feature/actions.ts` with `"use server"` and `parseWithZod`
6. Wire form: Use `useActionState` + `useForm` in client component
7. Add queries/mutations: `src/db/queries/new-feature.ts` and `src/db/mutations/new-feature.ts`
8. Protect route: Add `unstable_middleware = [requireUserMiddleware]` to `route.tsx`
9. Style with daisyUI: Use semantic classes and responsive utilities
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/react-router-7-dashboard-saas-template/raw