Next.js Data

Forms and Mutations

Server Actions, form handling, validation, and progressive enhancement.

Advertisement

Forms and Mutations

Server Actions, form handling, validation, and progressive enhancement.

Overview

Server Actions enable form submissions without API routes.

Key Concepts

  • Server Actions — Functions that run on the server
  • Form Actions — Native form submission with actions
  • Validation — Server-side validation with errors
  • Optimistic Updates — UI updates before server response
  • Progressive Enhancement — Forms work without JavaScript

Code Examples

// app/actions.js
'use server';
import { revalidatePath } from 'next/cache';

export async function createPost(formData) {
  const title = formData.get('title');
  const content = formData.get('content');
  
  if (!title) {
    return { error: 'Title is required' };
  }

  await db.posts.create({ data: { title, content } });
  revalidatePath('/posts');
}

// app/posts/new/page.js
import { createPost } from '../actions';

export default function NewPost() {
  return (
    <form action={createPost}>
      <input name="title" placeholder="Title" required />
      <textarea name="content" placeholder="Content" />
      <button type="submit">Create Post</button>
    </form>
  );
}

// With validation and error handling
export async function createPostWithValidation(formData) {
  const title = formData.get('title');
  
  const errors = {};
  if (!title) errors.title = 'Title is required';
  if (title && title.length < 3) errors.title = 'Title must be 3+ characters';
  
  if (Object.keys(errors).length > 0) {
    return { errors };
  }

  await db.posts.create({ data: { title } });
  revalidatePath('/posts');
  redirect('/posts');
}

// Client component with useActionState
'use client';
import { useActionState } from 'react';

function PostForm() {
  const [state, formAction, pending] = useActionState(createPostWithValidation, null);

  return (
    <form action={formAction}>
      <input name="title" />
      {state?.errors?.title && <span>{state.errors.title}</span>}
      <button disabled={pending}>Submit</button>
    </form>
  );
}

Practice

Build a form with validation, error display, and optimistic updates.