Skip to content

[React 19] Incorrect infered response type in server actions with React 19 #31786

@alexanderalmstrom

Description

@alexanderalmstrom

Summary

Server actions in Next.js return Promise<void> instead of the infered response type.

This is most likely a issue in react 19 or @types/react 19 since it works as exepected with both Next.js 14 and Next.js 15 and can only be reproduced when upgrading from React 18 to 19.

Example action:
https://github.com/alexanderalmstrom/next-15-server-actions/blob/main/src/app/actions.ts

main branch with React 19:
https://github.com/alexanderalmstrom/next-15-server-actions/tree/main

Skärmavbild 2024-12-14 kl  23 49 34

react-18 branch with React 18.3.1 where the response is infered correctly with Promise<{ message: string }>
https://github.com/alexanderalmstrom/next-15-server-actions/tree/react-18

Skärmavbild 2024-12-14 kl  23 42 07

Both branches uses the same Next version 15.1.0 and the same server action.

I'm aware of the useActionState (previously called useFormState)...

"use client";

import { type ComponentProps, useActionState } from "react";
import { exampleFormActionState } from "../actions";

export default function FormWithActionState(props: ComponentProps<"form">) {
  const [state, action] = useActionState(exampleFormActionState, {
    message: "",
  });

  return (
    <form action={action} {...props}>
      <input type="text" name="name" />
      <button type="submit">Submit</button>
      {state.message && <p>{state.message}</p>}
    </form>
  );
}

but is it expected that the action should return Promise<void>?

Skärmavbild 2024-12-15 kl  00 20 04

and we have to type the state like this, instead of having the types infered automatically for us in typescript?

type FormState = {
  message: string;
};

export async function exampleFormActionState(
  prevState: FormState,
  formData: FormData
) {
  const name = formData.get("name");

  if (!name || typeof name !== "string") {
    return { message: "VALIDATION_ERROR" };
  }

  return { message: `Hello, ${name}!` };
}

The thing is, what if I don't want to use useActionState? Lets say that we pass the action as prop to the form element, and then use auseFormStatus and a pending state for the button. Then that's not possible because of this error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions