How can i send like a notification with redirect from a server action
Unanswered
Mawhadmd posted this in #help-forum
MawhadmdOP
I'm reading the documentation and in the ''mutating data" section i had an idea of showing toast notification to user when they're done editing (redirected back), but the only way i can think of is through searchparams, but this will leave unwanted residue in the url. is there another way to help me achieve this?
23 Replies
@Mawhadmd I'm reading the documentation and in the ''mutating data" section i had an idea of showing toast notification to user when they're done editing (redirected back), but the only way i can think of is through searchparams, but this will leave unwanted residue in the url. is there another way to help me achieve this?
American Chinchilla
Using router.push
For navigation on the client side
Or if you want in the server action you would use the redirect() function i believe
@American Chinchilla Or if you want in the server action you would use the redirect() function i believe
MawhadmdOP
i'm using this, but AFAIK i can only add ?params= to the url, this will distort the url
@Mawhadmd i'm using this, but AFAIK i can only add ?params= to the url, this will distort the url
American Chinchilla
Im a bit confused
You can pass a relative URL
Like in their example doc redirect(“/posts”)
So there wont any URL query params
@American Chinchilla So there wont any URL query params
MawhadmdOP
My english is confused, What i was saying is that i want to inform the page that i'm redirecting users to that they've done the action successfully, hence display the notification.
I used query params, but i'm seeking another way
American Chinchilla
Then you would use client side navigation
Using next.js router from next/navigation
@American Chinchilla Then you would use client side navigation
MawhadmdOP
how do i know if the server actions has succeeded?
@Mawhadmd how do i know if the server actions has succeeded?
American Chinchilla
The server actions can return data
Or use the actionFormStatus hook
Either or works
If you’re using React 19 (comes with Next 15) Check the docs for the [
This lets you access to the return value of your action for you to do whatever you want with it.
useActionState
hook](https://react.dev/reference/react/useActionState) (previously named useFormState
). This lets you access to the return value of your action for you to do whatever you want with it.
@Mawhadmd how do i know if the server actions has succeeded?
You can return an object that indicates if the server action succeeded or failed:
Now in your component get this object back, and if you use
return {success : true, data: … }
// or
return {success : false, error: … }
Now in your component get this object back, and if you use
useActionState
you have a nice loading state too, and you can display your toast or do whatever you need.adding up to what wolf and luis said, this is how i've done it in the past, if it can help. i used the old useFormState back then, but here i tested it with the new useActionState which has the isPending status which is super nice. i check the result of the action and then show the toast and redirect client side with router.push depending on the result:
The action is just:
"use client";
import { toast } from "sonner";
import { editName } from "../action";
import { useRouter } from "next/navigation";
import { useActionState } from "react";
type State = Awaited<ReturnType<typeof editName>>;
const EditSomeThing = () => {
const router = useRouter();
const [state, action, isPending] = useActionState(
async (_previousState: State, formData: FormData) => {
const name = formData.get("name");
if (typeof name !== "string" || !name.trim()) {
// we can do any client side validation we want here, without contacting the server
return {
error: "Name is required",
};
}
// server action is called here
const result = await editName(name);
if (result?.error) {
return result;
}
toast("Name edited successfully!");
router.push("/redirection-testing/users");
},
undefined,
);
return (
<form
action={action}
className="w-md border-teal-500 bg-teal-50 border-2 p-4 rounded-md fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
>
{state?.error && (
<div className="text-red-500 mb-2">
<strong>Error:</strong> {state.error}
</div>
)}
<div className="flex gap-2 items-center">
<label htmlFor="name">Name</label>
<input
id="name"
name="name"
disabled={isPending}
className="border-teal-500 border bg-white rounded-md p-1 flex-1"
/>
</div>
<button
type="submit"
disabled={isPending}
className="w-full p-2 mt-4 bg-teal-500 text-white font-semibold rounded-md"
>
{isPending ? "Loading..." : "Submit"}
</button>
</form>
);
};
export default EditSomeThing;
The action is just:
"use server";
import { setTimeout } from "timers/promises";
export const editName = async (name: string) => {
await setTimeout(3000);
// lets randomly fail or success
if (Math.random() > 0.5) {
return {
error: "Failed to edit name",
};
}
console.log("edited", name);
};
@Andres Eloy adding up to what wolf and luis said, this is how i've done it in the past, if it can help. i used the old useFormState back then, but here i tested it with the new useActionState which has the isPending status which is super nice. i check the result of the action and then show the toast and redirect client side with router.push depending on the result:
tsx
"use client";
import { toast } from "sonner";
import { editName } from "../action";
import { useRouter } from "next/navigation";
import { useActionState } from "react";
type State = Awaited<ReturnType<typeof editName>>;
const EditSomeThing = () => {
const router = useRouter();
const [state, action, isPending] = useActionState(
async (_previousState: State, formData: FormData) => {
const name = formData.get("name");
if (typeof name !== "string" || !name.trim()) {
// we can do any client side validation we want here, without contacting the server
return {
error: "Name is required",
};
}
// server action is called here
const result = await editName(name);
if (result?.error) {
return result;
}
toast("Name edited successfully!");
router.push("/redirection-testing/users");
},
undefined,
);
return (
<form
action={action}
className="w-md border-teal-500 bg-teal-50 border-2 p-4 rounded-md fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
>
{state?.error && (
<div className="text-red-500 mb-2">
<strong>Error:</strong> {state.error}
</div>
)}
<div className="flex gap-2 items-center">
<label htmlFor="name">Name</label>
<input
id="name"
name="name"
disabled={isPending}
className="border-teal-500 border bg-white rounded-md p-1 flex-1"
/>
</div>
<button
type="submit"
disabled={isPending}
className="w-full p-2 mt-4 bg-teal-500 text-white font-semibold rounded-md"
>
{isPending ? "Loading..." : "Submit"}
</button>
</form>
);
};
export default EditSomeThing;
The action is just:
tsx
"use server";
import { setTimeout } from "timers/promises";
export const editName = async (name: string) => {
await setTimeout(3000);
// lets randomly fail or success
if (Math.random() > 0.5) {
return {
error: "Failed to edit name",
};
}
console.log("edited", name);
};
MawhadmdOP
I can't seem to find UseActionState (React 19 beta), Module '"react"' has no exported member 'useActionState'
Thanks for your help btw
version 19 of react should have the useActionState unless you're in an old canary version, if that's your case you can replace it with useFormState importing it from react-dom and it will work similar to the useActionState in the example above (just tested it)
in newer versions you will get the deprecation error i got in the picture
import { useFormState } from "react-dom";
in newer versions you will get the deprecation error i got in the picture
@Andres Eloy version 19 of react should have the useActionState unless you're in an old canary version, if that's your case you can replace it with useFormState importing it from react-dom and it will work similar to the useActionState in the example above (just tested it)
tsx
import { useFormState } from "react-dom";
in newer versions you will get the deprecation error i got in the picture
MawhadmdOP
yeah i got the deprecation error, but it appears that i had to update react and @types/react seperately to v19 to get the useActionState 👍🏻