I'm using Next.js latest version (App Router) and I want to show a toast (flash) message after a server-side redirect. Here's my use case:
I have a server component (e.g., /login/page.tsx
) that checks if a user is already logged in.
If they are, I want to:
/
"You are already logged in"
I'm using react-hot-toast
, which only works in client components, so I can't trigger the toast directly from the server.
What I tried: I've tried setting a cookie and remove it once read it from a client component. But that doesn't work:
cookieStore.set()
throws and error:
Cookies can only be modified in a Server Action or Route Handler.
// /login/page.tsx
import styles from "@/app/styles/auth.module.css";
import LoginComponent from "@/app/components/Auth/LoginComponent";
import { redirect } from "next/navigation";
import { getUserSession } from "@/lib/session/session.server";
import { setToastMessage } from "@/lib/toast-message.server";
async function LoginPage() {
const session = await getUserSession();
if (session) {
await setToastMessage("You are already logged in.", "error");
return redirect("/");
}
return (
<div className={styles.container}>
<div className={styles.content}>
<div className={styles.header}>
<span>Login</span>
</div>
<div className={styles.main}>
<LoginComponent />
</div>
</div>
</div>
);
}
export default LoginPage;
// @/lib/session/session.server.ts
import { cookies } from "next/headers";
export async function setToastMessage(message: string, type: string) {
const cookieStore = await cookies();
cookieStore.set("_toast", message);
cookieStore.set("_toast_type", type);
}
// Here `cookieStore.set()` throws and error:
Cookies can only be modified in a Server Action or Route Handler.
// /page.tsx
import Dashboard from "./components/Dashboard/DashBoard";
import ToastServerMessage from "./components/ToastServerMessage";
export default function Home() {
return (
<>
<Dashboard />
<ToastServerMessage />
</>
);
}
// ToastServerMessage.tsx
"use client";
import { useEffect } from "react";
import cookies from "js-cookie";
import toast from "react-hot-toast";
function ToastServerMessage() {
useEffect(() => {
const toastMessage = cookies.get("_toast");
const toastType = cookies.get("_toast_type");
if (toastMessage && toastType) {
if (toastType === "error") {
toast.error(toastMessage);
}
}
// delete cookie
// ...
}, []);
return <></>;
}
export default ToastServerMessage;
What I need:
How can I safely:
Thanks in advance!
Best Thing is to Use a Route Handler to Set the Cookie, Then Redirect
Create a Route Handler:
import { NextResponse } from 'next/server';
export async function GET() {
const response = NextResponse.redirect(new URL('/', process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'));
response.cookies.set('_toast', 'You are already logged in', {
path: '/',
httpOnly: false,
secure: true,
sameSite: 'strict',
});
response.cookies.set('_toast_type', 'error', {
path: '/',
httpOnly: false,
secure: true,
sameSite: 'strict',
});
return response;
}
Update your /login/page.tsx
to redirect to this API route:
import { redirect } from 'next/navigation';
import { getUserSession } from '@/lib/session/session.server';
export default async function LoginPage() {
const session = await getUserSession();
if (session) {
return redirect('/api/redirect-login');
}
return (
<div className={styles.container}>
<div className={styles.content}>
<div className={styles.header}>
<span>Login</span>
</div>
<div className={styles.main}>
<LoginComponent />
</div>
</div>
</div>
);
}
Client Component to Show Toast on Redirected Page.
'use client';
import { useEffect } from 'react';
import toast from 'react-hot-toast';
import cookies from 'js-cookie';
export default function ToastServerMessage() {
useEffect(() => {
const toastMessage = cookies.get('_toast');
const toastType = cookies.get('_toast_type');
if (toastMessage && toastType) {
if (toastType === 'error') toast.error(toastMessage);
if (toastType === 'success') toast.success(toastMessage);
}
cookies.remove('_toast');
cookies.remove('_toast_type');
}, []);
return null;
}
Include <ToastServerMessage />
in your main layout or home page where needed
NextResponse
lets you set cookies and redirect at once, js-cookie
can read and clear those cookies and react-hot-toast
shows the message once the client loads.
Why this is best:
Works With Next.js Architecture (Server components (like page.tsx
) cannot modify cookies with this is by design in Next.js)
Route Handlers (app/api/…/route.ts
) can set cookies and perform redirects using NextResponse
Avoids Client-Side Hacks
Decouples Logic Cleanly
Fully Compatible with React-Hot-Toast
You're not misusing server-only functions in client environments