next.js: TypeScript error when using `useFormState`

Hey folks, leaving an issue open here in case people search for this. Currently, there’s a TS error you can safely ignore when importing useFormState. This is dependent on an upstream issue being merged: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/66726

Example

'use client'
 
 // @ts-ignore
import { experimental_useFormState as useFormState } from 'react-dom'
import { experimental_useFormStatus as useFormStatus } from 'react-dom'
import { createTodo } from '@/app/actions'
 
const initialState = {
  message: null,
}
 
function SubmitButton() {
  const { pending } = useFormStatus()
 
  return (
    <button type="submit" aria-disabled={pending}>
      Add
    </button>
  )
}
 
export function AddForm() {
  const [state, formAction] = useFormState(createTodo, initialState)
 
  return (
    <form action={formAction}>
      <label htmlFor="todo">Enter Task</label>
      <input type="text" id="todo" name="todo" required />
      <SubmitButton />
      <p aria-live="polite" className="sr-only">
        {state?.message}
      </p>
    </form>
  )
}

About this issue

  • Original URL
  • State: closed
  • Created 9 months ago
  • Reactions: 24
  • Comments: 30 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Nice trick @neatonk 👍 BTW, I find a little odd that we have to do all sorts of workarounds in order to get things work, and it is even odder that the official docs and examples from Vercel are misleading.

Since updating to Next.js 14, the TS runtime error persisted when importing ‘experimental_useFormState’:

// @ts-ignore
import { experimental_useFormState as useFormState } from 'react-dom'

However, the error goes away when removing ‘experimental_useFormState’:

// @ts-ignore
import { useFormState, useFormStatus } from "react-dom";

We still need '‘ts-ignore’ otherwise get:

Module '"react-dom"' has no exported member 'useFormState'.

package.json:

"@types/react": "18.2.23",
"@types/react-dom": "18.2.8",

rm -rf .next

I’m running into Module '"react-dom"' has no exported member 'useFormStatus' with @types/react-dom@18.2.14 while 18.2.13 seems to work just fine suggesting it might be more complicated than just updating to the latest versions to resolve the type error. This combination works for me:

"@types/react": "18.2.36",
"@types/react-dom": "18.2.13",

Just putting it out there in case it helps someone else.

for https://github.com/vercel/next.js/tree/canary/examples/next-forms correcting the verion of reac-dom to latest resolves the pnpm run build error “@types/react”: “^18.2.34”, “@types/react-dom”: “^18.2.14”,

OR option 2 use what @leerob prescribed above add 2.1 // @ts-ignore to the start of add-form.tsx and delete-form.tsx 2.2 and replace experimental_useFormState with useFormState //import { experimental_useFormState as useFormState } from ‘react-dom’ //import { useFormStatus } from ‘react-dom’ 2.3 and finally remove experimental serverrun from next.config.js making it simple 3 lines as below /** @type {import(‘next’).NextConfig} */ const nextConfig = {} module.exports = nextConfig

As an alternative to the above workarounds, you can use this shim with next@13.5.6:

// file: react-dom-shim.ts
import {
  //@ts-expect-error
  experimental_useFormState,
  //@ts-expect-error
  experimental_useFormStatus,
  // types are defined, but exports are not :(
  useFormState as undefined_useFormState,
  useFormStatus as undefined_useFormStatus
} from "react-dom"

export const useFormState = experimental_useFormState as typeof undefined_useFormState
export const useFormStatus = experimental_useFormStatus as typeof undefined_useFormStatus

@morellodev try adding "types": ["react-dom/experimental"] to your tsconfig.json

{
  "compilerOptions": {
    ...
    "types": ["react-dom/experimental"],
  }

And then use the import as normal:

// MyForm.tsx
import { useFormState } from 'react-dom'
import { useFormStatus } from 'react-dom'

I’m using the following versions:

  "devDependencies": {
    "@types/react-dom": "18.2.13",
    ...
  }

@jmd01 @shawnmclean The issue we are having is due to a misalignment between the members exported by react-dom and the types definitions from @types/react-dom.

The canary react-dom version shipped with Next.js exports useFormState and useFormStatus with the experimental_ prefix, while the type defs say they are not prefixed.

@jmd01 following exactly that.

Unhandled Runtime Error
TypeError: (0 , react_dom__WEBPACK_IMPORTED_MODULE_3__.useFormState) is not a function or its return value is not iterable
import { useFormState } from "react-dom";
"react": "18.2.0",
"react-dom": "18.2.0",

upgrading to the latest versions does not work. 👎

Yeah, I’d recommend just upgrading to the latest versions for the new types 😄

Hello guys

To fix linting error I have just changed below packages from:

    "react": "^18",
    "react-dom": "^18",
    "@types/react": "^18",
    "@types/react-dom": "^18",

Into:

    "react": "18.2.0",
    "react-dom": "18.2.0",
    "@types/react": "18.2.14",
    "@types/react-dom": "18.2.6",

What if i’d wanted to pass arguments to createTodo? It’s not clear how to type in the docs, and since there are no type declarations yet 🤔

It means alot coming from the author himself to second my suggestion! Thank you @leerob 😊 BTW, Lee, Just FYI, the latest Node v20 and Next v14 combination is not getting compiled on AWS amplify.

On Thu, 2 Nov, 2023, 20:03 Lee Robinson, @.***> wrote:

Yeah, I’d recommend just upgrading to the latest versions for the new types 😄

— Reply to this email directly, view it on GitHub https://github.com/vercel/next.js/issues/56041#issuecomment-1790851363, or unsubscribe https://github.com/notifications/unsubscribe-auth/AQZ4KGZRYPFGAZ6BATA7TJLYCOVK7AVCNFSM6AAAAAA5IDRP7OVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTOOJQHA2TCMZWGM . You are receiving this because you commented.Message ID: <vercel/next. @.***>

@shawnmclean @morellodev I had to also use the latest next, react, react-dom. This is working for me now:

    "@types/react": "18.2.29",
    "@types/react-dom": "18.2.13",
    "react": "experimental",
    "react-dom": "experimental",
    "next": "canary",

https://github.com/jmd01/next-forms

Workaround

For now, just pin these versions of the types packages and the { experimental_... } exports will be recognized by TS:

{
  "@types/react": "18.2.23",
  "@types/react-dom": "18.2.8",
}