Skip to content

useActionState receives undefined state when Server Action calls redirect() #589

@ayonli

Description

@ayonli

Bug Description

When a Server Action used with useActionState calls redirect() (from next/navigation), the state returned by useActionState becomes undefined instead of retaining the previous state. This causes a TypeError on the next render if the component accesses any property on state.

Steps to Reproduce

  1. Create a Server Action that calls redirect() on success:
// app/actions.ts
'use server';
import { redirect } from 'next/navigation';
import type { ActionResult } from './types';

export async function createPost(
  _prev: ActionResult,
  formData: FormData,
): Promise<ActionResult> {
  // ... create post logic ...
  redirect(`/posts/${postId}`); // <-- triggers the bug
}
  1. Use the action with useActionState in a Client Component:
'use client';
import { useActionState } from 'react';
import { createPost } from './actions';

const initial = { success: false, error: '' };

export function PostForm() {
  const [state, formAction, pending] = useActionState(createPost, initial);

  // TypeError: Cannot read properties of undefined (reading 'success')
  // state is `undefined` after redirect() is called
  return (
    <form action={formAction}>
      {!state.success && state.error && <p>{state.error}</p>}
      <button type="submit">Submit</button>
    </form>
  );
}
  1. Submit the form. The Server Action runs, calls redirect(), and the component receives undefined as the new state, crashing with:
TypeError: Cannot read properties of undefined (reading 'success')

Expected Behavior

When redirect() is called inside a Server Action, the browser should navigate to the target URL. The state value from useActionState should either retain the previous state or remain in a defined shape — it should never become undefined.

This is the standard Next.js behavior: redirect() throws a special internal error that Next.js catches to perform the navigation, and useActionState is not involved in that process.

Actual Behavior

state becomes undefined after redirect() is called in the Server Action, causing a TypeError on the next render.

Workaround

Avoid calling redirect() inside Server Actions used with useActionState. Instead, return a success payload from the action and navigate client-side using router.push() in a useEffect:

useEffect(() => {
  if (state.success && state.data?.postId) {
    router.push(`/posts/${state.data.postId}`);
  }
}, [state, router]);

Environment

  • vinext: 0.0.31
    • Next.js App Router (Server Actions)
      • Cloudflare Workers (via @cloudflare/vite-plugin)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions