toast.loading() not working with nextjs 14

Author: MauricioKruijerCreated Nov 1, 2023Updated Apr 19, 2026

Hi there, thanks for releasing this amazing product!

I would like to use it in my nextjs project and I'm running into a few issues with toast.promise() and toast.loading(). I am trying to disable a submit button -> show loading toast -> await my action -> show success (or error). Which would be the expected behaviour, however, I only get the loading toast only the second time I click the button. The success toast is more consistent.

Please have look at https://github.com/MauricioKruijer/nextjs-react-hot-toast/commit/fb8db5a7126b3b7b84c4f8651c6349394b72c6fb for more information. You can run this project to experience what I'm talking about.

There is an extract of my form that is trying to render the toast by using useFormState()

typescript
'use client'

import Button from "@/components/Button";
import {useFormState} from "react-dom";
import toast from "react-hot-toast";

function doStuff(formData: FormData) {
  // if (Math.floor((Math.random() * 4) + 1) === 1) {
  //   throw new Error('This is a planned error')
  // }
  return new Promise(resolve => setTimeout(resolve, 1500))
}

async function someAction(state: any, formData: FormData) {
  const toastId = toast.loading('Loading') // This toast is not not shown, sometimes on third or second time of hitting the button

  try {
    await doStuff(formData)
  } catch (error) {
    toast.dismiss(toastId);
    toast.error((error as Error).message)
    return {
      errors: 'something went wrong'
    }
  }

  toast.success('Success!', {id: toastId})

  return {
    message: 'ok'
  }
}

const Form = () => {
  const initialState = { message: null, error: null };
  const [state, dispatch] = useFormState(someAction, initialState);

  return (
    <form action={dispatch}>
      <Button />
    </form>
  )
}

export default Form

Source: timolins/react-hot-toast