Server Actions with Toast (useActionState) - Robin Wieruch

Server Actions with Toast (useActionState) - Robin Wieruch

In this tutorial we want to explore how to show toast notifications for Server Actions in React when using React’s useActionState Hook.

Note: This tutorial has been created following an insightful discussion with Sebastien on X. He suggested an improved solution to my original approach , which we’ll explore together. A big thanks to Sebastien for his valuable input.

Feel free to check out the whole implementation in the GitHub repository .

It’s important to note that we will implement all the heavy lifting ourselves next. At the end, you have reusable primitives which will allow you to implement toast notifications with Server Actions when using React’s useActionState Hook. However, if you don’t want to implement the utilities yourself, check out next-safe-action .

Let’s start with the initial implementation which should already work but which is not showing any toast notifications yet. We will implement the toast notifications afterward:

To show toast notifications when using React’s useActionState Hook, we literally hook into the server action’s functionality. So let’s wrap the Server Action with a higher-order function (HoF) that will allow us to add callback functions to the Server Action:

The initial implementation of the higher-order function does not enhance the passed function in any way. It just calls the function and returns the result. We already know that this result is a promise, because the Server Action is an asynchronous function:

Without the type definitions, the higher-order function looks simpler. Essentially, it forwards the arguments to the Server Action and returns the promise:

Feel free to move this utility to another file to make it reusable across files.

Now comes the actual enhancement in the higher-order function. First, we want to provide as a second argument callback functions for the success and error case. These callback functions will be called when the Server Action is completed:

Second, we define the toastCallbacks object with two functions: one for the success case and one for the error case. Later we will add more callbacks in this object. Furthermore, this object could be defined for reusability in a separate file as well:

And third, we need to add the second argument to the higher-order function as type definition and implementation detail. The latter will call these functions when the Server Action completes and passes the result for success and error cases to the callbacks:

While we are able to hook into the the Server Action with the withCallbacks , we decoupled the actual implementation logic into the callbacks. This way we can reuse the withCallbacks for other Server Actions and may provide different callback functions for different actions.

The toast feedback should now work as expected when upvoting users. The implementation to get here may be a bit more complex, but it provides a more flexible and reusable solution for adding callback functions to Server Actions.

Next we want to show a toast notification for a pending Server Action. This is useful to inform the user that the action is being processed. We’ll add a loading toast message when the Server Action is triggered and remove it when the action is completed.

First, we add a loading toast message to the withCallbacks higher-order function:

Second, to make the toast callbacks reusable for other pending states, you can pass in the text for the loading toast message as a parameter:

Third, we add the new callbacks to the type definition with TypeScript generics :

Last but not least, we adjust the withCallbacks higher-order function to call the onStart and onEnd callbacks when the Server Action is triggered and completed:

And finally use the new function in the withCallbacks higher-order function:

This way you can show a loading toast when the Server Action is triggered and remove it when the action is completed. In the next section, we will give an overview of what we have achieved by putting all reusable parts into separate files.

To recap, we want to extract the withCallbacks and toastCallbacks to separate files to make them reusable across files. This way we can use the same toast feedback for other Server Actions in our application.

In addition, this allows us to see all the files in their entirety without the need to scroll up and down. First, we extract the withCallbacks HoF to a separate file:

And second, we extract the createToastCallbacks function to a separate file:

Finally all utilities can be used in a lightweight way in the User component:

Although the initial implementation may seem complex, it provides a look into the underlying mechanics, similar to what you would find in a library like next-safe-action.

Using it now in your application though is straightforward and provides a reusable solution for adding toast notifications to Server Actions. You had a first experience in the last code snippet and now we can reuse the same utilities for other Server Actions.

To demonstrate how to handle errors for failed Server Actions with toast notifications, we’ll add a downvote Server Action that always returns an error message. This will allow us to test how our app handles failed Server Actions and displays toast notifications.

Next, we add a new button in the User component that calls the new Server Action. When the action fails, it should trigger a toast notification informing the user:

Do not forget the import for the new Server Action:

And last, we add the new button to the User component:

Now, when you click the new button, you should see a toast message displaying the failed Server Action message. The error handling with a toast notification is now in place, and the user receives feedback when a Server Action fails.

In the last section, we want prove that the toast notifications are also working for unmounting components, because this was a flaw in the alternative implementation where we used React’s useEffect Hook for the toast notifications.

First, we are going to implement a delete Server Action that removes a user:

Second, we create a custom hook for the delete Server Action which reuses the withCallbacks and createToastCallbacks utilities:

And third, we add a new button to the User component that calls the delete action:

The toast notification should show up, even though the component unmounts, because the utility is not tied to the component lifecycle.

The last examples have shown you how straightforward it is to add toast notifications for Server Actions in React. The initial implementation for the utility functions is on a library level, but the usage in your application is lightweight and reusable.

In this tutorial, we learned how to implement React Server Actions with Toasts by:

If you enjoyed this tutorial and want to dig deeper into this topic, feel free to check out the alternative approach which has been referenced in this tutorial.

Recommended articles