Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions cdn/custom-error-pages-app-dir/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# Dependencies
/node_modules
/.pnp
.pnp.js

# Testing
/coverage

# Next.js
/.next/
/out/
next-env.d.ts

# Production
build
dist

# Misc
.DS_Store
*.pem

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Local ENV files
.env.local
.env.development.local
.env.test.local
.env.production.local

# Vercel
.vercel

# Turborepo
.turbo

# typescript
*.tsbuildinfo
.env*.local
70 changes: 70 additions & 0 deletions cdn/custom-error-pages-app-dir/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
name: Custom Error Pages (App Router)
slug: custom-error-pages-app-dir
description: Create custom 5xx error pages using Next.js App Router
framework: Next.js
useCase: Edge Functions
css: Tailwind
deployUrl: https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/cdn/custom-error-pages-app-dir&project-name=custom-error-pages-app-dir&repository-name=custom-error-pages-app-dir
demoUrl: https://custom-error-pages-app-dir.vercel.app
---

# Custom Error Pages (App Router)

This example demonstrates how to create custom error pages for 5xx server errors using Next.js App Router. This feature is available for Enterprise customers.

## Demo

https://custom-error-pages-app-dir.vercel.app

## How to Use

You can choose from one of the following two methods to use this repository:

### One-Click Deploy

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=vercel-examples):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/cdn/custom-error-pages-app-dir&project-name=custom-error-pages-app-dir&repository-name=custom-error-pages-app-dir)

### Clone and Deploy

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [pnpm](https://pnpm.io/installation) to bootstrap the example:

```bash
pnpm create next-app --example https://github.com/vercel/examples/tree/main/cdn/custom-error-pages-app-dir
```

Next, run Next.js in development mode:

```bash
pnpm dev
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=vercel-examples) ([Documentation](https://nextjs.org/docs/deployment)).

## Structure

```
app/
├── 500/
│ └── page.tsx # Generic server error (fallback for all 5xx)
└── 504/
└── page.tsx # Specific gateway timeout page
```

## How it works

When deployed to Vercel, error pages are automatically detected and routes are generated:

| Error | Destination |
|-------|-------------|
| 500 | `/500` (from `app/500/page.tsx`) |
| 502 | `/500` (fallback) |
| 503 | `/500` (fallback) |
| 504 | `/504` (from `app/504/page.tsx`) |
| 508 | `/500` (fallback) |

## Learn more

- [Custom Error Pages Documentation](https://vercel.com/docs/cdn/custom-error-pages)
14 changes: 14 additions & 0 deletions cdn/custom-error-pages-app-dir/app/500/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function ServerError() {
return (
<div className="flex min-h-screen flex-col items-center justify-center">
<h1 className="text-4xl font-bold">500</h1>
<p className="mt-4 text-lg text-gray-600">Internal Server Error</p>
<p className="mt-2 text-sm text-gray-500">
Something went wrong on our end. Please try again later.
</p>
<a href="/" className="mt-6 text-blue-600 hover:underline">
Go back home
</a>
</div>
);
}
14 changes: 14 additions & 0 deletions cdn/custom-error-pages-app-dir/app/504/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function GatewayTimeout() {
return (
<div className="flex min-h-screen flex-col items-center justify-center">
<h1 className="text-4xl font-bold">504</h1>
<p className="mt-4 text-lg text-gray-600">Gateway Timeout</p>
<p className="mt-2 text-sm text-gray-500">
The server took too long to respond. Please try again later.
</p>
<a href="/" className="mt-6 text-blue-600 hover:underline">
Go back home
</a>
</div>
);
}
Binary file added cdn/custom-error-pages-app-dir/app/favicon.ico
Binary file not shown.
26 changes: 26 additions & 0 deletions cdn/custom-error-pages-app-dir/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@import "tailwindcss";

:root {
--background: #ffffff;
--foreground: #171717;
}

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}

body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
34 changes: 34 additions & 0 deletions cdn/custom-error-pages-app-dir/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
65 changes: 65 additions & 0 deletions cdn/custom-error-pages-app-dir/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Image from "next/image";

export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</div>
</main>
</div>
);
}
18 changes: 18 additions & 0 deletions cdn/custom-error-pages-app-dir/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
7 changes: 7 additions & 0 deletions cdn/custom-error-pages-app-dir/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
};

export default nextConfig;
Loading