CodShot
TSX Public

React Form Validation with useActionState

Manage form validation, submission status, and server-style action results with React useActionState.

#react #hooks #form #validation
TSX
'use client';

import { useActionState } from 'react';

type FormState = {
  message: string;
  errors: {
    email?: string;
  };
};

const initialState: FormState = {
  message: '',
  errors: {},
};

async function subscribe(
  previousState: FormState,
  formData: FormData,
): Promise<FormState> {
  const email = String(formData.get('email') ?? '').trim();

  if (!email.includes('@')) {
    return {
      message: '',
      errors: { email: 'Enter a valid email address.' },
    };
  }

  await saveSubscriber(email);

  return {
    message: 'Subscription saved.',
    errors: {},
  };
}

export function NewsletterForm() {
  const [state, formAction, isPending] = useActionState(
    subscribe,
    initialState,
  );

  return (
    <form action={formAction}>
      <label htmlFor="newsletter-email">Email</label>
      <input
        id="newsletter-email"
        name="email"
        type="email"
        aria-describedby="newsletter-feedback"
        required
      />

      <button type="submit" disabled={isPending}>
        {isPending ? 'Saving…' : 'Subscribe'}
      </button>

      <p id="newsletter-feedback" aria-live="polite">
        {state.errors.email ?? state.message}
      </p>
    </form>
  );
}

async function saveSubscriber(email: string) {
  console.log('Save subscriber:', email);
}

Snippet actions

CodShot user
CodShot user
Updated: 2026-08-06 18:36
Public snippets
0
Forks
CodShot AI Help
Ask about CodShot features, plans, workspace, AI limits, referrals, and public help topics.
Hi! I can help you understand how CodShot works. What would you like to know?
For account-specific or sensitive issues, please open a support ticket. Open support