There are many status codes, but only a handful are meaningful in an SEO context.
This is the default code that will be set when Next.js renders a page successfully.
Indicates that the resource requested has been moved temporarily (307) or permanently (308) to the destination URL.
You can trigger a 307 or 308 redirect in Next.js in next.config.js.
// next.config.js
module.exports = {
async redirects() {
return [
{
source: '/about',
destination: '/',
permanent: true, // triggers 308
},
];
},
};
redirects
is an async
function that expects an array to be returned holding objects with source
, destination
, and permanent
properties:
source
is the incoming request path pattern.destination
is the path you want to route to.permanent
true
orfalse
- iftrue
will use the308
status code which instructs clients/search engines to cache the redirect forever, if false will use the 30`7 status code which is temporary and is not cached.
Next.js will automatically return a 404 status code for URLs that do not exist in your application. In some cases, you might also want to return a 404 status code from a page (for example with dynamic routes):
import Image from 'next/image';
import { notFound } from 'next/navigation';
import photos from '@/app/photos';
import styles from './page.module.css';
export default function PhotoPage({ params }) {
const photo = photos.find((p) => p.id === params.id);
const width = 600;
// If photo not found, return 404.
// You want to call notFound() here and not just render a 404
// so that the 404 status code gets sent correctly. notFound()
// throws a NEXT_NOT_FOUND error which will then be caught by
// the closest not-found special file.
if (!photo) {
notFound();
}
return (
<main>
<Image
alt=""
src={photo.imageSrc}
height={width * 1.25}
width={width}
className={styles.photo}
/>
</main>
);
}
Next.js will automatically return a 500 status code for an unexpected application error.
It's recommended to return this status code when your website is down and you predict that the website will be down by an extended period of time. This prevents losing rankings on a long-term basis.
The only way I could think to do this is with middleware, by returning JSON:
// This function can be marked `async` if using `await` inside
export function middleware(request) {
console.log('middleware running');
if (request.nextUrl.pathname.startsWith('/')) {
return new Response(JSON.stringify('Service temporarily unavailable!'), {
status: 503,
headers: {
'Content-Type': 'application/json',
'Retry-After': 'Wed, 21 Oct 2023 07:28:00 GMT'
},
});
}
It would be great if I could return a custom 503 page, but not sure that's possible at this time. See also github issue #52378 and github issue #50383.
There are two ways to define Metadata:
- Config-based Metadata: Export a static metadata object or a dynamic generateMetadata function in a layout.js or page.js file.
- File-based Metadata: Add static or dynamically generated special files to route segments (e.g.
favicon.ico
,apple-icon.jpg
, andicon.jpg
,opengraph-image.jpg
andtwitter-image.jpg
,robots.txt
,sitemap.xml
).
There are two default meta tags that are always added even if a route doesn't define metadata:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
// Static metadata object
export const metadata = {
title: '...',
};
// or Dynamic metadata
export async function generateMetadata({ params }) {
// This is useful for dynamic routes
// For example you could await a fetch call here:
// const postid = params.postid;
// const res = await fetch('...');
// const data = await res.json();
return {
title: '...',
};
}
- The
metadata
object andgenerateMetadata
function exports are only supported in Server Components. - You cannot export both the metadata object and generateMetadata function from the same route segment.
Some fields:
export const metadata = {
title: 'Next.js',
description: 'The React Framework for the Web',
generator: 'Next.js',
applicationName: 'Next.js',
referrer: 'origin-when-cross-origin',
keywords: ['Next.js', 'React', 'JavaScript'],
authors: [{ name: 'Jessica' }, { name: 'Scott', url: 'https://nextjs.org' }],
creator: 'Scott Volk',
publisher: 'Jessica Rush',
formatDetection: {
email: false,
address: false,
telephone: false,
},
viewport: {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
},
colorScheme: 'dark',
robots: {
index: true,
follow: true,
noarchive: true,
},
};
There's way more, e.g. robots, icons, etc. See also the HTML standard.
Note that for page titles you can use the title.template
field to define a template for your page titles, for example in my root layout:
import { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | company name',
default: 'company name',
},
};
The %s
in the template will be replaced with the specific page title:
export const metadata: Metadata = {
title: 'About us',
};
These special files are available for metadata:
favicon.ico
,apple-icon.jpg
, andicon.jpg
opengraph-image.jpg
andtwitter-image.jpg
robots.txt
sitemap.xml
See the Metadata Files API Reference
File convention | Supported file types | Valid locations |
---|---|---|
favicon | .ico | app/ |
icon | .ico, .jpg, .jpeg, .png, .svg | app/**/* |
apple-icon | .jpg, .jpeg, .png | app/**/* |
You can set multiple icons by adding a number suffix to the file name. For example, icon1.png
, icon2.png
, etc. Numbered files will sort lexically.
These are useful for setting the images that appear on social networks and messaging apps when a user shares a link to your site. These files can be placed in any segment.
Place a png|jpeg|gif
image called opengraph-image.png
in your app
directory and to any route segment. This will get added as the meta tags like:
<meta property="og:image" content="" />
File convention | Supported file types |
---|---|
opengraph-image | .jpg, .jpeg, .png, .gif |
twitter-image | .jpg, .jpeg, .png, .gif |
opengraph-image.alt | .txt |
twitter-image.alt | .txt |
Note you can also create this as a .js
file: see the open graph protocol.
Add a static robots.txt file to your app/
directory. For example:
User-Agent: *
Allow: /
Disallow: /private/
Sitemap: https://acme.com/sitemap.xml
You can also generate this file with a robots.js
but I don't really see any added benefit.
You can add a static xml file to your app/
directory, however in this case it's easier automatically generate the file with a app/sitemap.js
:
export default function sitemap() {
return [
{
url: 'https://acme.com',
lastModified: new Date(),
},
{
url: 'https://acme.com/about',
lastModified: new Date(),
},
{
url: 'https://acme.com/blog',
lastModified: new Date(),
},
]
}
If you had dynamic routes, you could do:
export default async function sitemap() {
// List regular routes
const pages = ['', 'about', 'login', 'signup'];
// Also fetch any dynamic routes
const res = await fetch(url);
const posts = await res.json()
// Create arrays:
const dynamicRoutes = posts.map((post) => {
return {
url: `http://localhost:3000/post/${post.id}`,
lastModified: new Date()
}
});
const routes = pages.map((route) => {
return {
url: `http://localhost:3000/${route}`,
lastModified: new Date()
}
});
return [...routes, ...dynamicRoutes];
}