server-actions
npx machina-cli add skill JanSzewczyk/claude-plugins/server-actions --openclawServer Actions Skill
Create Next.js Server Actions with TypeScript following best practices for validation, error handling, and React integration.
Reference Files:
- types.md - ActionResponse, RedirectAction type definitions
- examples.md - Complete implementation examples
- patterns.md - Best practices and anti-patterns
- validation.md - Zod schema patterns
- hooks.md - React hooks (useActionState, useFormStatus)
- react-hook-form.md - React Hook Form integration
First Step: Read Project Context
IMPORTANT: Before creating server actions, check project configuration files:
.claude/project-context.md for:
- Authentication provider (Clerk, NextAuth, JWT)
- Database patterns (tuple error handling, DbError class)
- Logging conventions
CLAUDE.md for:
- Server Actions patterns (ActionResponse type)
- Database error handling (DbError class)
- Toast notification system
- File organization conventions
Context
This skill helps you create:
- Form actions - Handle form submissions with validation
- CRUD operations - Create, read, update, delete mutations
- Redirect flows - Multi-step wizards, onboarding
- Data mutations - Any server-side data changes
Instructions
When the user requests a server action:
1. Analyze Requirements
Gather information about:
- What data does the action handle?
- Does it return data or redirect?
- What validation is needed?
- What authentication/authorization is required?
- What errors need to be handled?
- What cache paths need revalidation?
2. Choose Response Type
| Scenario | Type | Description |
|---|---|---|
| Returns data | ActionResponse<T> | Action returns data to client |
| Redirects on success | RedirectAction | Action navigates to new page |
Form with useActionState | Modified signature | Accepts previousState as first param |
3. Create Server Action
File Location: features/[feature]/server/actions/[action-name].ts
Standard Template:
"use server";
import { redirect } from "next/navigation";
import { revalidatePath } from "next/cache";
import { auth } from "@clerk/nextjs/server"; // or your auth provider
import { createLogger } from "~/lib/logger";
import type { ActionResponse, RedirectAction } from "~/lib/action-types";
const logger = createLogger({ module: "[feature]-actions" });
export async function actionName(data: InputType): ActionResponse<OutputType> {
// 1. Authentication
const { userId } = await auth();
if (!userId) {
return { success: false, error: "Authentication required" };
}
// 2. Validation
const parsed = schema.safeParse(data);
if (!parsed.success) {
return {
success: false,
error: "Validation failed",
fieldErrors: parsed.error.flatten().fieldErrors,
};
}
// 3. Database operation
const [error, result] = await dbOperation(parsed.data);
if (error) {
logger.error({ userId, error }, "Operation failed");
return { success: false, error: "Operation failed" };
}
// 4. Revalidate cache
revalidatePath("/affected-path");
// 5. Return response
logger.info({ userId, resultId: result.id }, "Operation succeeded");
return { success: true, data: result };
}
4. Create Validation Schema
File Location: features/[feature]/schemas/[schema-name].ts
import { z } from "zod";
export const inputSchema = z.object({
name: z.string().min(2, "Name must be at least 2 characters"),
email: z.string().email("Invalid email address"),
});
export type InputData = z.infer<typeof inputSchema>;
5. Integrate with React Component
Choose integration pattern based on form complexity:
| Complexity | Pattern | Use When |
|---|---|---|
| Simple | useActionState | Native form, simple state |
| Complex | React Hook Form + useTransition | Dynamic fields, complex validation |
| Redirect | Bound action prop | Multi-step flows |
See hooks.md and react-hook-form.md for implementation details.
Core Patterns
Error Handling
Use tuple pattern for database operations:
const [error, data] = await dbOperation();
if (error) {
if (error.isNotFound) return { success: false, error: "Not found" };
if (error.isRetryable) return { success: false, error: "Please try again" };
return { success: false, error: "Operation failed" };
}
Cache Revalidation
Always revalidate after mutations:
revalidatePath("/posts"); // Specific path
revalidateTag("posts"); // By cache tag
Toast Notifications
For user feedback with redirects:
import { setToastCookie } from "~/lib/toast/server/toast.cookie";
await setToastCookie("Saved successfully!", "success");
redirect("/dashboard");
Security Checklist
- ✅ Verify authentication (
await auth()) - ✅ Check authorization (ownership, permissions)
- ✅ Validate all inputs (Zod schema)
- ✅ Log important operations
- ✅ Never expose internal errors to client
File Organization
features/
users/
server/
actions/
create-user.ts
update-user.ts
delete-user.ts
index.ts # Re-exports
db/
users.ts
schemas/
user.ts
types/
user.ts
components/
user-form.tsx
Running and Testing
# Type check
npm run type-check
# Run related tests
npm run test -- features/[feature]
# Test in Storybook (if form component)
npm run storybook:dev
Questions to Ask
When creating server actions:
- What data does this action create/update/delete?
- Does it return data or redirect to another page?
- What validation rules apply to the input?
- What authentication/authorization is needed?
- What error cases should be handled?
- What paths/tags need cache revalidation?
- Should there be toast notification feedback?
- Is this part of a multi-step flow?
Source
git clone https://github.com/JanSzewczyk/claude-plugins/blob/main/plugins/nextjs-react/skills/server-actions/SKILL.mdView on GitHub Overview
Learn to build Next.js Server Actions with TypeScript, covering forms, mutations, and data flows. It emphasizes type-safe responses, robust validation, and error handling aligned with project patterns. Use cases include server-side form handling, CRUD mutations, and multi-step redirects.
How This Skill Works
Actions live in features/[feature]/server/actions/[action-name].ts and run with the 'use server' directive. They perform authentication checks, validate inputs with Zod (safeParse), and execute database mutations, returning ActionResponse or RedirectAction. Errors and field-level feedback are standardized to support the client via hooks like useActionState.
When to Use It
- Implement server-side form submissions with validation
- Perform CRUD mutations (create, update, delete) on server
- Create redirect flows for multi-step onboarding
- Handle data mutations requiring authentication/authorization
- Implement client-facing error handling and cache revalidation when data changes
Quick Start
- Step 1: Read project context (.claude/project-context.md) and auth/db patterns
- Step 2: Create a server action file at features/[feature]/server/actions/[action-name].ts with 'use server' and skeleton logic
- Step 3: Validate input with a Zod schema, perform DB operation, and return ActionResponse or RedirectAction; test with React hooks like useActionState
Best Practices
- Return ActionResponse<T> for data results and RedirectAction for navigations
- Validate inputs with Zod schemas using safeParse and propagate fieldErrors
- Enforce authentication (e.g., Clerk/NextAuth) before mutations
- Use next/cache revalidatePath to refresh affected pages after mutations
- Organize actions under features/[feature]/server/actions with clear logging
Example Use Cases
- Create a server action for a user registration form
- Implement CRUD actions for budget management
- Add form validation with Zod to the contact form
- Create redirect action for onboarding flow
- Guard server mutations with auth checks and structured error handling