Banner image for the post.

Toasts, Redirects & 404s in Next.js

nextjsuxtutorial
25 November 2025

This article is about 404 errors. A four-oh-four error occurs when a resource cannot be found by the server. End users will typically run into this issue when attempting to access a resource that can't be found, such as a deleted page or unavailable product.

Almost every site has a 404 page that displays a message such as "Not Found" to advise the user when they've strayed from the path. Hell, even this site has just a default 404 page. But, is this the best option?

The Problem with 404 Pages

IMO, 404 pages are bad UX.

First, it changes the tone of the user's experience on the site. 9 times out of 10, the developer is to blame for the 404 error, not the user. The reasons for a 404 are most likely:

  • The resource was moved and not redirected properly
  • The user saved a link to a now deleted or unavailable resource
  • The server was misconfigured

I'd posit the only situation you can truly blame the user for is when they enter the address of the resource wrong typing it in themselves; at that point, it's on them. A 404 page doesn't convey this however. It presents the user with a message akin to "You're here now - it's your mess to fix". Even where the 404 page is phrased in terms of an apology it still acts as a jarring experience for the user.

Second, it forces the user to take the next step. A user's journey through a web application is important. As developers, we need to be aware the user can click away or leave the site at any time if they choose. The more time spent on the website and spent navigating through the pages can be considered a success metric. Especially in the case of online stores - the whole site should act as a sales funnel. One wrong move, and the user clicks away and the conversion is lost.

If your 404 page has a button to take the user back home, that's great - it still needs the user to click and take an action though. That being said, a lot of 404 pages don't even have this functionality; the user is left alone to find their way back to a meaningful page.

Third, it reduces trust in your application. A 404 return code is an error; and in general you don't want your users to experience non-user-related errors for the key reason it detracts from the trust in your application. If the user isn't confident the site is working correctly, they may not be confident to continue using it.

Now, I'm not saying you shouldn't handle 404 errors. Without a catchall handler for not found routes, you leave the result to either the browser or the underlying framework for your application, and neither have a particularly elegant way of managing the scenario. Most of the time this exposes developer (but not consumer) friendly error messages and/or an unstyled UI. Yuck.

But, consider instead redirecting on 404s. This takes away the action that the user would normally have to take, and instead swiftly guides them back to a meaningful page. An even better option is to combine a redirect with a toast message that abstracts the "error" away, and gently advises why they are now where they are. In my opinion, this is the best solution for UX.

How to Redirect on 404 in Next.js

So how do we go about this in Next.js? Well, Next.js has file-based routing and one of the conventions is a not-found.js file you can house at the root of your app/ directory to display a UI page, or in our case, redirect to a particular page.

not-found.tsx
tsx
1"use client";
2
3import { useEffect } from "react";
4import { useRouter } from "next/navigation";
5
6export default function NotFound() {
7 const router = useRouter();
8
9 useEffect(() => {
10 router.replace("/?lost=true");
11 }, [router]);
12
13 return null;
14}

The not-found.js component needs to technically mount and render so we need to use the client side routing functioanlity. This component will now redirect home whenever the user enters an incorrect url, or when the notFound() function from next/navigation is called.

But we also want to be able to display a toast message to advise the user of why they were redirected and why they're not in the location they're supposed to be. That's where the query param comes in. As the toast component will likely unmount on the NotFound page, we need to have a component render on the redirected destination page that can render the toast message. The way I do this is by having a component such as the below to handle creating the toast message when the required query param is present:

not-found-toaster.tsx
typescript
1"use client";
2
3import { useSearchParams, useRouter } from "next/navigation";
4import { useEffect } from "react";
5import { toast } from "sonner";
6
7export default function NotFoundToaster() {
8 const searchParams = useSearchParams();
9 const router = useRouter();
10 const lost = searchParams.get("lost");
11
12 useEffect(() => {
13 if (lost && lost === "true") {
14 toast.error("We're sorry - that page isn't available right now. It may have been moved or deleted.");
15 }
16
17 if (lost) {
18 // Small timeout to ensure toast is shown before URL change
19 setTimeout(() => {
20 router.replace("/", { scroll: false });
21 }, 1000);
22 }
23 }, [logout, oauth, router]);
24
25 return null;
26}
27

This is then nested in the page.tsx component. This can also be further extended - in some of my applications for a particular page I might have several different messages that can be displayed based on what the query param is called and what the state is.

Try this in your next application instead of using a basic 404 page.

Banner by Maggie Ziegler on Unsplash