server-actions
Provides implementation help for Next.js Server Actions, form handling, and data mutations.
Install
mkdir -p .claude/skills/server-actions && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4588" && unzip -o skill.zip -d .claude/skills/server-actions && rm skill.zipInstalls to .claude/skills/server-actions
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
This skill should be used when the user asks about "Server Actions", "form handling in Next.js", "mutations", "useFormState", "useFormStatus", "revalidatePath", "revalidateTag", or needs guidance on data mutations and form submissions in Next.js App Router.Key capabilities
- →Implement server-side data mutations
- →Handle form submissions with Server Actions
- →Revalidate cache paths and tags
- →Manage form state and loading status
- →Perform optimistic UI updates
How it works
Server Actions are asynchronous functions marked with 'use server' that execute on the server, allowing direct interaction with databases and cache revalidation.
Inputs & outputs
When to use server-actions
- →Implement form submissions using Server Actions
- →Handle data mutations in Server Components
- →Revalidate paths or tags after database updates
- →Set up form state management
About this skill
Next.js Server Actions
Overview
Server Actions are asynchronous functions that execute on the server. They can be called from Client and Server Components for data mutations, form submissions, and other server-side operations.
Defining Server Actions
In Server Components
Use the 'use server' directive inside an async function:
// app/page.tsx (Server Component)
export default function Page() {
async function createPost(formData: FormData) {
'use server'
const title = formData.get('title') as string
await db.post.create({ data: { title } })
}
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
)
}
In Separate Files
Mark the entire file with 'use server':
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
await db.post.create({ data: { title } })
}
export async function deletePost(id: string) {
await db.post.delete({ where: { id } })
}
Form Handling
Basic Form
// app/actions.ts
'use server'
export async function submitContact(formData: FormData) {
const name = formData.get('name') as string
const email = formData.get('email') as string
const message = formData.get('message') as string
await db.contact.create({
data: { name, email, message }
})
}
// app/contact/page.tsx
import { submitContact } from '@/app/actions'
export default function ContactPage() {
return (
<form action={submitContact}>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" required />
<button type="submit">Send</button>
</form>
)
}
With Validation (Zod)
// app/actions.ts
'use server'
import { z } from 'zod'
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
export async function signup(formData: FormData) {
const parsed = schema.safeParse({
email: formData.get('email'),
password: formData.get('password'),
})
if (!parsed.success) {
return { error: parsed.error.flatten() }
}
await createUser(parsed.data)
return { success: true }
}
useFormState Hook
Handle form state and errors:
// app/signup/page.tsx
'use client'
import { useFormState } from 'react-dom'
import { signup } from '@/app/actions'
const initialState = {
error: null,
success: false,
}
export default function SignupPage() {
const [state, formAction] = useFormState(signup, initialState)
return (
<form action={formAction}>
<input name="email" type="email" />
<input name="password" type="password" />
{state.error && (
<p className="text-red-500">{state.error}</p>
)}
<button type="submit">Sign Up</button>
</form>
)
}
// app/actions.ts
'use server'
export async function signup(prevState: any, formData: FormData) {
const email = formData.get('email') as string
if (!email.includes('@')) {
return { error: 'Invalid email', success: false }
}
await createUser({ email })
return { error: null, success: true }
}
useFormStatus Hook
Show loading states during submission:
// components/submit-button.tsx
'use client'
import { useFormStatus } from 'react-dom'
export function SubmitButton() {
const { pending } = useFormStatus()
return (
<button type="submit" disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
)
}
// Usage in form
import { SubmitButton } from '@/components/submit-button'
export default function Form() {
return (
<form action={submitAction}>
<input name="title" />
<SubmitButton />
</form>
)
}
Revalidation
revalidatePath
Revalidate a specific path:
'use server'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
await db.post.create({ data: { ... } })
// Revalidate the posts list page
revalidatePath('/posts')
// Revalidate a dynamic route
revalidatePath('/posts/[slug]', 'page')
// Revalidate all paths under /posts
revalidatePath('/posts', 'layout')
}
revalidateTag
Revalidate by cache tag:
// Fetching with tags
const posts = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
})
// Server Action
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost(formData: FormData) {
await db.post.create({ data: { ... } })
revalidateTag('posts')
}
Redirects After Actions
'use server'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
const post = await db.post.create({ data: { ... } })
// Redirect to the new post
redirect(`/posts/${post.slug}`)
}
Optimistic Updates
Update UI immediately while action completes:
'use client'
import { useOptimistic } from 'react'
import { addTodo } from '@/app/actions'
export function TodoList({ todos }: { todos: Todo[] }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: string) => [
...state,
{ id: 'temp', title: newTodo, completed: false }
]
)
async function handleSubmit(formData: FormData) {
const title = formData.get('title') as string
addOptimisticTodo(title) // Update UI immediately
await addTodo(formData) // Server action
}
return (
<>
<form action={handleSubmit}>
<input name="title" />
<button>Add</button>
</form>
<ul>
{optimisticTodos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</>
)
}
Non-Form Usage
Call Server Actions programmatically:
'use client'
import { deletePost } from '@/app/actions'
export function DeleteButton({ id }: { id: string }) {
return (
<button onClick={() => deletePost(id)}>
Delete
</button>
)
}
Error Handling
'use server'
export async function createPost(formData: FormData) {
try {
await db.post.create({ data: { ... } })
return { success: true }
} catch (error) {
if (error instanceof PrismaClientKnownRequestError) {
if (error.code === 'P2002') {
return { error: 'A post with this title already exists' }
}
}
return { error: 'Failed to create post' }
}
}
Security Considerations
- Always validate input - Never trust client data
- Check authentication - Verify user is authorized
- Use CSRF protection - Built-in with Server Actions
- Sanitize output - Prevent XSS attacks
'use server'
import { auth } from '@/lib/auth'
export async function deletePost(id: string) {
const session = await auth()
if (!session) {
throw new Error('Unauthorized')
}
const post = await db.post.findUnique({ where: { id } })
if (post.authorId !== session.user.id) {
throw new Error('Forbidden')
}
await db.post.delete({ where: { id } })
}
Resources
For detailed patterns, see:
references/form-handling.md- Advanced form patternsreferences/revalidation.md- Cache revalidation strategiesexamples/mutation-patterns.md- Complete mutation examples
When not to use it
- →When performing client-side only state management
- →When not using Next.js App Router
Limitations
- →Requires Next.js App Router
- →Input validation is necessary as client data is untrusted
How it compares
This approach replaces manual API route creation by allowing functions to be called directly from components.
Compared to similar skills
server-actions side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| server-actions (this skill) | 1 | 7mo | No flags | Intermediate |
| javascript-typescript-typescript-scaffold | 3 | 4mo | Review | Beginner |
| open-notebook-lm-guidelines | 0 | 5mo | No flags | Advanced |
| hr-portal-skill | 0 | 1mo | No flags | Beginner |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by davepoon
View all by davepoon →You might also like
javascript-typescript-typescript-scaffold
sickn33
You are a TypeScript project architecture expert specializing in scaffolding production-ready Node.js and frontend applications. Generate complete project structures with modern tooling (pnpm, Vite, N
open-notebook-lm-guidelines
RainLib
Core project guidelines, architecture, and UI styling instructions for the FerrisMind (openNotebookLm) AI workspace project. Use this whenever working on the frontend or backend of this project.
hr-portal-skill
hariventures2000-ship-it
Use this skill when developing, testing, debugging, or modifying code inside the HR Portal frontend (apps/hr-portal). It applies to handling HR administration tasks such as employee roster management, contract seeding, leave request approvals, attendance monitoring, payroll calculation, and recruitm
nextjs-developer
zenobi-us
Expert Next.js developer mastering Next.js 14+ with App Router and full-stack features. Specializes in server components, server actions, performance optimization, and production deployment with focus on building fast, SEO-friendly applications.
landing-page-guide-v2
bear2u
Create distinctive, high-converting landing pages that combine proven conversion elements with exceptional design quality. Build beautiful, memorable landing pages using Next.js 14+ and ShadCN UI that avoid generic AI aesthetics while following the 11 essential elements framework.
frontend-developer
sickn33
Build React components, implement responsive layouts, and handle client-side state management. Masters React 19, Next.js 15, and modern frontend architecture. Optimizes performance and ensures accessibility. Use PROACTIVELY when creating UI components or fixing frontend issues.