home / skills / hoangnguyen0403 / agent-skills-standard / server-actions

server-actions skill

/skills/nextjs/server-actions

This skill helps you implement and secure Next.js Server Actions for server mutations, form handling, and RPC-style calls without extra endpoints.

npx playbooks add skill hoangnguyen0403/agent-skills-standard --skill server-actions

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
2.0 KB
---
name: Next.js Server Actions
description: Mutations, Form handling, and RPC-style calls.
metadata:
  labels: [nextjs, actions, mutations]
  triggers:
    files: ['**/actions.ts', '**/*.tsx']
    keywords: [use server, Server Action, revalidatePath, useFormStatus]
---

# Server Actions

## **Priority: P1 (HIGH)**

Handle form submissions and mutations without creating API endpoints.

## Implementation

- **Directive**: Add `'use server'` at the top of an async function.
- **Usage**: Pass to `action` prop of `<form>` or invoke from event handlers.

```tsx
// actions.ts
'use server';
export async function createPost(formData: FormData) {
  const title = formData.get('title');
  await db.post.create({ title });
  revalidatePath('/posts'); // Refresh UI
}
```

## Client Invocation

- **Form**: `<form action={createPost}>` (Progressive enhancements work without JS).
- **Event Handler**: `onClick={() => createPost(data)}`.
- **Pending State**: Use `useFormStatus` hook (must be inside a component rendered within the form).

## **P1: Operational Standard**

### **1. Secure & Validate**

Always validate inputs and authorization within the action.

```tsx
'use server';
export async function updateProfile(prevState: any, formData: FormData) {
  const session = await auth();
  if (!session) throw new Error('Unauthorized');

  const validatedFields = ProfileSchema.safeParse(
    Object.fromEntries(formData.entries()),
  );
  if (!validatedFields.success)
    return { errors: validatedFields.error.flatten().fieldErrors };

  // mutation...
  revalidatePath('/profile');
  return { success: true };
}
```

### **2. Pending States**

Use `useActionState` (React 19/Next.js 15+) for state handling and `useFormStatus` for button loading states.

## **Constraints**

- **Closures**: Avoid defining actions inside components to prevent hidden closure encryption overhead and serialization bugs.
- **Redirection**: Use `redirect()` for success navigation; it throws an error that Next.js catches to handle the redirect.

Overview

This skill documents practical patterns for using Next.js Server Actions to handle mutations, form submissions, and RPC-style calls without separate API routes. It emphasizes secure validation, predictable state handling, and operational constraints to avoid common pitfalls. Follow these guidelines to implement server-side actions that are efficient, safe, and compatible with progressive enhancement.

How this skill works

Server Actions are ordinary async functions marked with the 'use server' directive that run on the server when invoked from a form or client event. Attach an action to a <form> via its action prop or call it from event handlers; use hooks like useFormStatus or useActionState to surface pending states. Actions can read FormData, perform mutations, revalidate paths, and use redirect() for navigation.

When to use it

  • Form submissions where progressive enhancement is required (works without JS).
  • Server-side mutations that would otherwise require custom API endpoints.
  • Simple RPC-style calls from client components to run server logic directly.
  • Operations that need built-in revalidation or server-only secrets.

Best practices

  • Always add 'use server' at the top of each exported action and keep actions defined at module scope (not inside components).
  • Validate inputs and check authorization inside the action; never trust client data. Use schemas (zod or similar) and return structured errors on validation failure.
  • Avoid closures that capture client state to prevent serialization and encryption overhead. Pass needed data explicitly.
  • Use useFormStatus or useActionState to show loading states and disable controls during mutations.
  • For navigation after success, call redirect() from the action; Next.js catches the thrown redirect and handles it server-side.

Example use cases

  • CreatePost action for submitting blog posts: read FormData, validate title, persist to DB, then revalidatePath('/posts').
  • Profile update flow: validate form entries, check session/auth inside the action, update user record, revalidate profile path.
  • Inline like/unlike RPC: call a server action from a button handler to update counts without an API route.
  • Admin mutation with secrets: run server-only logic such as sending emails or processing payments from an action, keeping secrets off the client.

FAQ

Can Server Actions run without client JavaScript?

Yes. When attached to a form via the action prop, Server Actions support progressive enhancement and will work with standard form submissions without client JS.

Where should I define my actions?

Define actions at module scope in separate files or modules. Avoid declaring them inside components to prevent closure serialization bugs and runtime overhead.

How do I handle loading states in forms?

Use useFormStatus inside a component rendered within the form to get pending state for buttons. For broader state, use useActionState (React 19 / Next.js 15+).