Not Found (404)

Custom 404 UI with not-found.tsx - special file, no URL, nearest-wins.

Overview

Bini.js ships a built-in 404. Add not-found.tsx in any folder for custom UI when no route matches. It is a special file - it does not create a URL. Nearest-wins applies.

FileCreates URL?Purpose
app/page.tsxYes - /Home page
app/not-found.tsxNo - special fileGlobal 404 fallback
app/blog/not-found.tsxNo - special fileBlog-specific 404

Global 404 Page

Create not-found.tsx at the root of app/ to handle all unmatched routes.

app
layout.tsx
page.tsx
not-found.tsx
/
app/not-found.tsx
export default function NotFound() {
  return (
    <div className="flex min-h-screen flex-col items-center justify-center">
      <h1 className="text-4xl font-bold">404</h1>
      <p className="mb-6">The page you&apos;re looking for doesn&apos;t exist.</p>
      <a href="/">Return Home</a>
    </div>
  )
}
Shown for unmatched paths like /non-existent or /blog/invalid-post when no closer not-found exists.

Nested 404 Pages

Place not-found.tsx in subdirectories for segment-specific 404 UI. The closest file to the unmatched path wins.

app
not-found.tsx
blog
not-found.tsx
page.tsx
[slug]
page.tsx
admin
not-found.tsx
page.tsx
/blog
/blog/:slug
/admin
app/blog/not-found.tsx
export default function BlogNotFound() {
  return (
    <div className="py-12 text-center">
      <h1 className="text-3xl font-bold">Post Not Found</h1>
      <p className="mb-6">The blog post you&apos;re looking for doesn&apos;t exist.</p>
      <a href="/blog">View all posts</a>
    </div>
  )
}
URL404 Page Used
/blog/non-existentapp/blog/not-found.tsx
/admin/invalidapp/admin/not-found.tsx
/completely/wrongapp/not-found.tsx (global)

Programmatic 404

When a route matches but data is missing, return your own not-found UI from the page component.

app/blog/[slug]/page.tsx
export default function BlogPost({
  params,
}: {
  params: { slug: string }
}) {
  const post = getPost(params.slug)

  if (!post) {
    return (
      <div className="py-12 text-center">
        <h1>Post Not Found</h1>
        <p>The post &quot;{params.slug}&quot; doesn&apos;t exist.</p>
        <a href="/blog">View all posts</a>
      </div>
    )
  }

  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  )
}

404 with Layout

not-found.tsx is rendered inside the layout chain of the segment it belongs to. Headers and sidebars stay visible.

app
layout.tsx
not-found.tsx
blog
layout.tsx
not-found.tsx
page.tsx
/blog
app/blog/layout.tsx
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div>
      <header className="mb-8">
        <h1 className="text-2xl font-bold">Blog</h1>
      </header>
      <main>{children}</main>
    </div>
  )
}
blog/not-found still shows the Blog header from blog/layout.

Styling 404 Pages

Build any UI you want - copy, links, and actions are all client-side.

app/not-found.tsx
export default function NotFound() {
  return (
    <div className="flex min-h-[60vh] flex-col items-center justify-center px-4 text-center">
      <h1 className="text-9xl font-bold">404</h1>
      <h2 className="mb-3 text-3xl font-bold">Page Not Found</h2>
      <p className="mb-8 max-w-md text-neutral-500">
        The page you&apos;re looking for might have been removed or doesn&apos;t exist.
      </p>
      <div className="flex gap-4">
        <a
          href="/"
          className="rounded-lg bg-black px-6 py-3 text-white dark:bg-white dark:text-black"
        >
          Go Home
        </a>
        <button
          onClick={() => window.history.back()}
          className="rounded-lg border px-6 py-3"
        >
          Go Back
        </button>
      </div>
    </div>
  )
}

Complete Example

app
layout.tsx
page.tsx
not-found.tsx
blog
layout.tsx
not-found.tsx
page.tsx
[slug]
page.tsx
dashboard
layout.tsx
not-found.tsx
page.tsx
settings
page.tsx
/
/blog
/blog/:slug
/dashboard
/dashboard/settings
FileCreates URL?Purpose
app/page.tsxYes - /Home page
app/not-found.tsxNoGlobal 404
app/blog/not-found.tsxNoBlog 404
app/dashboard/not-found.tsxNoDashboard 404
Was this helpful?