typescriptintermediate

Next.js Server Actions with Forms

Use Server Actions for form handling with validation, optimistic updates, and error handling.

typescript
// app/actions.ts
'use server';

import { revalidatePath } from 'next/cache';

interface FormState {
  message: string;
  errors?: Record<string, string[]>;
}

export async function createPost(
  prevState: FormState,
  formData: FormData
): Promise<FormState> {
  const title = formData.get('title') as string;
  const content = formData.get('content') as string;

  // Validation
  const errors: Record<string, string[]> = {};
  if (!title || title.length < 3) {
    errors.title = ['Title must be at least 3 characters'];
  }
  if (!content || content.length < 10) {
    errors.content = ['Content must be at least 10 characters'];
  }
  if (Object.keys(errors).length > 0) {
    return { message: 'Validation failed', errors };
  }

  // Save to database
  // await db.post.create({ data: { title, content } });

  revalidatePath('/posts');
  return { message: 'Post created successfully' };
}

// app/posts/new/page.tsx
'use client';
import { useActionState } from 'react';
import { createPost } from '@/app/actions';

export default function NewPostPage() {
  const [state, action, pending] = useActionState(createPost, {
    message: '',
  });

  return (
    <form action={action} className="space-y-4">
      <div>
        <input name="title" placeholder="Title" className="border p-2 w-full" />
        {state.errors?.title?.map((e) => (
          <p key={e} className="text-red-500 text-sm">{e}</p>
        ))}
      </div>
      <div>
        <textarea name="content" placeholder="Content" className="border p-2 w-full" />
        {state.errors?.content?.map((e) => (
          <p key={e} className="text-red-500 text-sm">{e}</p>
        ))}
      </div>
      <button type="submit" disabled={pending}>
        {pending ? 'Creating...' : 'Create Post'}
      </button>
      {state.message && <p>{state.message}</p>}
    </form>
  );
}

Use Cases

  • Form submissions without API routes
  • Progressive enhancement with server validation
  • Optimistic UI with pending states

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.