Slobodan M.
Slobodan M.

Reputation: 81

Is there a way to use not-found.tsx inside of [lang] folder in Next.js 14?

I have a problem with Next.js 14 project, which uses app router. I use [lang] folder to localize my app. So, I have all my pages, including page.tsx and layout.tsx inside of [lang] folder. This way, I get locale passed to all of my pages like this:

export default function Home({ params }: { params: { lang: string } }) {
  const s = dictionary[params.lang];

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <p>{s.home.title}</p>
    </main>
  );
}

This is my middleware.ts file:

let locales = ["en", "de"];
export let defaultLocale = "en";

function getLocale(request: Request): string {
  const headers = new Headers(request.headers);
  const acceptLanguage = headers.get("accept-language");
  if (acceptLanguage) {
    headers.set("accept-language", acceptLanguage.replaceAll("_", "-"));
  }
  const headersObject = Object.fromEntries(headers.entries());
  const languages = new Negotiator({ headers: headersObject }).languages();

  return match(languages, locales, defaultLocale);
}

// Without /locale in path
export function middleware(request: NextRequest) {
  let locale = getLocale(request) ?? defaultLocale;
  const pathname = request.nextUrl.pathname;

  const newUrl = new URL(`/${locale}${pathname}`, request.nextUrl);
  return NextResponse.rewrite(newUrl);
}

export const config = {
  matcher: [
    // Skip all internal paths (_next)
    "/((?!_next|api|favicon.ico|robots.txt|sitemap.xml).*)",
    // Optional: only run on root (/) URL
    // '/'
  ],
};

My folder structure is:

my-app/
├─ ...
├─ public/
│  ├─ favicon.ico
│  ├─ robots.txt
├─ src/
│  ├─ app/
│  │  ├─ [lang]/
│  │  │  ├─ page.tsx
│  │  │  ├─ layout.tsx
│  │  │  ├─ not-found.tsx
│  ├─ api/
├─ ...

The problem is, when I put not-found.tsx inside of a [lang] folder, it does not work. On unknown route I get default 404 page. But, when I put it outside of [lang], in app folder directly, it does work, but I do not get locale, and I get the page without layout. The workaround would be to create yet another layout.tsx in app directory, but again without the possibility to localize it, which is just bad user experience.

I tried to copy not-found.tsx into [lang] folder to be able to get app locale to localize my content. According to Next.js documentation, this should work out of the box.

Has anyone encountered a problem like this?

Upvotes: 2

Views: 1042

Answers (2)

Serhii Dmitriiev
Serhii Dmitriiev

Reputation: 1

There's a way to render your custom not-found.tsx. Except the not-found.tsx file itself, you have to catch unknown routes too. You can define a catch-all route that explicitly calls the notFound function.

import {notFound} from 'next/navigation';
 
export default function CatchAllPage() {
  notFound();
}

After this change, all requests that are matched within the [locale] segment will render the not-found page when an unknown route is encountered (e.g. /en/unknown).

Upvotes: 0

Ahmed Abdelbaset
Ahmed Abdelbaset

Reputation: 4956

This answer documents my solution for the same issue, you might find it suitable or not.

  1. First of all, put your main not-found.tsx localized at app/[lang]/not-found.tsx.

  2. At the root, create a app/layout.tsx that doesn't do anything except returning its children prop:

import { type ReactNode } from "react";

export default function GlobalLayout({ children }: { children: ReactNode }) {
  return children;
}
  1. Create a root not-found.tsx:

Here you have two options:

  • Create a generic page telling your app is not localized to this language. or
  • redirect to the default language:
"use client";

import { redirect, usePathname } from "next/navigation";

import { defaultLocale } from "@/config/constants";

export default function NotFound() {
  const pathname = usePathname();

  redirect(`/${defaultLocale}${pathname}`);
}

Upvotes: 1

Related Questions