mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
c2b4c0815c
This PRs unifies the caching story across the docs, making Cache Components the happy path, while still providing guidance to users in the old model. However, instead of explaining the old model and its caching layers, we've created a new guide focusing on what APIs to use and when. This follow-up PR aligns terminology across the docs: https://github.com/vercel/next.js/pull/90589 ## IA updates Getting Started section: - Improves Getting Started progression: - **Before:** CC → Fetching Data → Updating Data → Caching and Revalidating (old and new model mixed) - **After:** Fetching Data (Dynamic) → Mutating Data (Dynamic) → Caching with CC (Prerendering) → Revalidating with CC. - New: `caching.mdx` (CC-first) - Structure: - Enabling Cache Components - Data vs UI-level caching - Working with request time APIs - Passing request values to cached functions - Working with non-deterministic operations - Working with synchronous operations - How rendering works (PPR and static shell story) - New: `revalidating.mdx` (CC-first) - Explains how to use `cacheLife` and `cacheTag` Guides Section: - New: `caching-and-revalidating.mdx` (Previous Model) - For users who are not using CC, includes `fetch` options and route segment config - Moves route segment config options that don't apply to CC from API reference to this guide (for easy archiving in the future). - New: `migrating-to-cache-components.mdx` (WIP) - Del: `caching.mdx` 😌 ## Terminology We should remove caching layers from the docs. Users only needed to be exposed to them when they were configured independently, but the new CC APIs work across layers. To make it easier to review this PR, I'm consolidating terminology and fixing broken links in a new PR: https://github.com/vercel/next.js/pull/90589 --------- Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: Joseph <[email protected]>
427 lines
11 KiB
Plaintext
427 lines
11 KiB
Plaintext
---
|
|
title: sitemap.xml
|
|
description: API Reference for the sitemap.xml file.
|
|
related:
|
|
title: Next Steps
|
|
description: Learn how to use the generateSitemaps function.
|
|
links:
|
|
- app/api-reference/functions/generate-sitemaps
|
|
---
|
|
|
|
`sitemap.(xml|js|ts)` is a special file that matches the [Sitemaps XML format](https://www.sitemaps.org/protocol.html) to help search engine crawlers index your site more efficiently.
|
|
|
|
### Sitemap files (.xml)
|
|
|
|
For smaller applications, you can create a `sitemap.xml` file and place it in the root of your `app` directory.
|
|
|
|
```xml filename="app/sitemap.xml"
|
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
<url>
|
|
<loc>https://acme.com</loc>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
<changefreq>yearly</changefreq>
|
|
<priority>1</priority>
|
|
</url>
|
|
<url>
|
|
<loc>https://acme.com/about</loc>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
<changefreq>monthly</changefreq>
|
|
<priority>0.8</priority>
|
|
</url>
|
|
<url>
|
|
<loc>https://acme.com/blog</loc>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
<changefreq>weekly</changefreq>
|
|
<priority>0.5</priority>
|
|
</url>
|
|
</urlset>
|
|
```
|
|
|
|
### Generating a sitemap using code (.js, .ts)
|
|
|
|
You can use the `sitemap.(js|ts)` file convention to programmatically **generate** a sitemap by exporting a default function that returns an array of URLs. If using TypeScript, a [`Sitemap`](#returns) type is available.
|
|
|
|
> **Good to know**: `sitemap.js` is a special Route Handler that is cached by default unless it uses a [Request-time API](/docs/app/glossary#request-time-apis) or [dynamic config](/docs/app/guides/caching-without-cache-components#dynamic) option.
|
|
|
|
```ts filename="app/sitemap.ts" switcher
|
|
import type { MetadataRoute } from 'next'
|
|
|
|
export default function sitemap(): MetadataRoute.Sitemap {
|
|
return [
|
|
{
|
|
url: 'https://acme.com',
|
|
lastModified: new Date(),
|
|
changeFrequency: 'yearly',
|
|
priority: 1,
|
|
},
|
|
{
|
|
url: 'https://acme.com/about',
|
|
lastModified: new Date(),
|
|
changeFrequency: 'monthly',
|
|
priority: 0.8,
|
|
},
|
|
{
|
|
url: 'https://acme.com/blog',
|
|
lastModified: new Date(),
|
|
changeFrequency: 'weekly',
|
|
priority: 0.5,
|
|
},
|
|
]
|
|
}
|
|
```
|
|
|
|
```js filename="app/sitemap.js" switcher
|
|
export default function sitemap() {
|
|
return [
|
|
{
|
|
url: 'https://acme.com',
|
|
lastModified: new Date(),
|
|
changeFrequency: 'yearly',
|
|
priority: 1,
|
|
},
|
|
{
|
|
url: 'https://acme.com/about',
|
|
lastModified: new Date(),
|
|
changeFrequency: 'monthly',
|
|
priority: 0.8,
|
|
},
|
|
{
|
|
url: 'https://acme.com/blog',
|
|
lastModified: new Date(),
|
|
changeFrequency: 'weekly',
|
|
priority: 0.5,
|
|
},
|
|
]
|
|
}
|
|
```
|
|
|
|
Output:
|
|
|
|
```xml filename="acme.com/sitemap.xml"
|
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
<url>
|
|
<loc>https://acme.com</loc>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
<changefreq>yearly</changefreq>
|
|
<priority>1</priority>
|
|
</url>
|
|
<url>
|
|
<loc>https://acme.com/about</loc>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
<changefreq>monthly</changefreq>
|
|
<priority>0.8</priority>
|
|
</url>
|
|
<url>
|
|
<loc>https://acme.com/blog</loc>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
<changefreq>weekly</changefreq>
|
|
<priority>0.5</priority>
|
|
</url>
|
|
</urlset>
|
|
```
|
|
|
|
### Image Sitemaps
|
|
|
|
You can use `images` property to create image sitemaps. Learn more details in the [Google Developer Docs](https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps).
|
|
|
|
```ts filename="app/sitemap.ts" switcher
|
|
import type { MetadataRoute } from 'next'
|
|
|
|
export default function sitemap(): MetadataRoute.Sitemap {
|
|
return [
|
|
{
|
|
url: 'https://example.com',
|
|
lastModified: '2021-01-01',
|
|
changeFrequency: 'weekly',
|
|
priority: 0.5,
|
|
images: ['https://example.com/image.jpg'],
|
|
},
|
|
]
|
|
}
|
|
```
|
|
|
|
Output:
|
|
|
|
```xml filename="acme.com/sitemap.xml"
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<urlset
|
|
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
|
|
>
|
|
<url>
|
|
<loc>https://example.com</loc>
|
|
<image:image>
|
|
<image:loc>https://example.com/image.jpg</image:loc>
|
|
</image:image>
|
|
<lastmod>2021-01-01</lastmod>
|
|
<changefreq>weekly</changefreq>
|
|
<priority>0.5</priority>
|
|
</url>
|
|
</urlset>
|
|
```
|
|
|
|
### Video Sitemaps
|
|
|
|
You can use `videos` property to create video sitemaps. Learn more details in the [Google Developer Docs](https://developers.google.com/search/docs/crawling-indexing/sitemaps/video-sitemaps).
|
|
|
|
```ts filename="app/sitemap.ts" switcher
|
|
import type { MetadataRoute } from 'next'
|
|
|
|
export default function sitemap(): MetadataRoute.Sitemap {
|
|
return [
|
|
{
|
|
url: 'https://example.com',
|
|
lastModified: '2021-01-01',
|
|
changeFrequency: 'weekly',
|
|
priority: 0.5,
|
|
videos: [
|
|
{
|
|
title: 'example',
|
|
thumbnail_loc: 'https://example.com/image.jpg',
|
|
description: 'this is the description',
|
|
},
|
|
],
|
|
},
|
|
]
|
|
}
|
|
```
|
|
|
|
Output:
|
|
|
|
```xml filename="acme.com/sitemap.xml"
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<urlset
|
|
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
|
|
>
|
|
<url>
|
|
<loc>https://example.com</loc>
|
|
<video:video>
|
|
<video:title>example</video:title>
|
|
<video:thumbnail_loc>https://example.com/image.jpg</video:thumbnail_loc>
|
|
<video:description>this is the description</video:description>
|
|
</video:video>
|
|
<lastmod>2021-01-01</lastmod>
|
|
<changefreq>weekly</changefreq>
|
|
<priority>0.5</priority>
|
|
</url>
|
|
</urlset>
|
|
```
|
|
|
|
### Generate a localized Sitemap
|
|
|
|
```ts filename="app/sitemap.ts" switcher
|
|
import type { MetadataRoute } from 'next'
|
|
|
|
export default function sitemap(): MetadataRoute.Sitemap {
|
|
return [
|
|
{
|
|
url: 'https://acme.com',
|
|
lastModified: new Date(),
|
|
alternates: {
|
|
languages: {
|
|
es: 'https://acme.com/es',
|
|
de: 'https://acme.com/de',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
url: 'https://acme.com/about',
|
|
lastModified: new Date(),
|
|
alternates: {
|
|
languages: {
|
|
es: 'https://acme.com/es/about',
|
|
de: 'https://acme.com/de/about',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
url: 'https://acme.com/blog',
|
|
lastModified: new Date(),
|
|
alternates: {
|
|
languages: {
|
|
es: 'https://acme.com/es/blog',
|
|
de: 'https://acme.com/de/blog',
|
|
},
|
|
},
|
|
},
|
|
]
|
|
}
|
|
```
|
|
|
|
```js filename="app/sitemap.js" switcher
|
|
export default function sitemap() {
|
|
return [
|
|
{
|
|
url: 'https://acme.com',
|
|
lastModified: new Date(),
|
|
alternates: {
|
|
languages: {
|
|
es: 'https://acme.com/es',
|
|
de: 'https://acme.com/de',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
url: 'https://acme.com/about',
|
|
lastModified: new Date(),
|
|
alternates: {
|
|
languages: {
|
|
es: 'https://acme.com/es/about',
|
|
de: 'https://acme.com/de/about',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
url: 'https://acme.com/blog',
|
|
lastModified: new Date(),
|
|
alternates: {
|
|
languages: {
|
|
es: 'https://acme.com/es/blog',
|
|
de: 'https://acme.com/de/blog',
|
|
},
|
|
},
|
|
},
|
|
]
|
|
}
|
|
```
|
|
|
|
Output:
|
|
|
|
```xml filename="acme.com/sitemap.xml"
|
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
|
<url>
|
|
<loc>https://acme.com</loc>
|
|
<xhtml:link
|
|
rel="alternate"
|
|
hreflang="es"
|
|
href="https://acme.com/es"/>
|
|
<xhtml:link
|
|
rel="alternate"
|
|
hreflang="de"
|
|
href="https://acme.com/de"/>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
</url>
|
|
<url>
|
|
<loc>https://acme.com/about</loc>
|
|
<xhtml:link
|
|
rel="alternate"
|
|
hreflang="es"
|
|
href="https://acme.com/es/about"/>
|
|
<xhtml:link
|
|
rel="alternate"
|
|
hreflang="de"
|
|
href="https://acme.com/de/about"/>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
</url>
|
|
<url>
|
|
<loc>https://acme.com/blog</loc>
|
|
<xhtml:link
|
|
rel="alternate"
|
|
hreflang="es"
|
|
href="https://acme.com/es/blog"/>
|
|
<xhtml:link
|
|
rel="alternate"
|
|
hreflang="de"
|
|
href="https://acme.com/de/blog"/>
|
|
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
|
|
</url>
|
|
</urlset>
|
|
```
|
|
|
|
### Generating multiple sitemaps
|
|
|
|
While a single sitemap will work for most applications. For large web applications, you may need to split a sitemap into multiple files.
|
|
|
|
There are two ways you can create multiple sitemaps:
|
|
|
|
- By nesting `sitemap.(xml|js|ts)` inside multiple route segments e.g. `app/sitemap.xml` and `app/products/sitemap.xml`.
|
|
- By using the [`generateSitemaps`](/docs/app/api-reference/functions/generate-sitemaps) function.
|
|
|
|
For example, to split a sitemap using `generateSitemaps`, return an array of objects with the sitemap `id`. Then, use the `id` to generate the unique sitemaps.
|
|
|
|
```ts filename="app/product/sitemap.ts" switcher
|
|
import type { MetadataRoute } from 'next'
|
|
import { BASE_URL } from '@/app/lib/constants'
|
|
|
|
export async function generateSitemaps() {
|
|
// Fetch the total number of products and calculate the number of sitemaps needed
|
|
return [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]
|
|
}
|
|
|
|
export default async function sitemap(props: {
|
|
id: Promise<string>
|
|
}): Promise<MetadataRoute.Sitemap> {
|
|
const id = await props.id
|
|
// Google's limit is 50,000 URLs per sitemap
|
|
const start = id * 50000
|
|
const end = start + 50000
|
|
const products = await getProducts(
|
|
`SELECT id, date FROM products WHERE id BETWEEN ${start} AND ${end}`
|
|
)
|
|
return products.map((product) => ({
|
|
url: `${BASE_URL}/product/${product.id}`,
|
|
lastModified: product.date,
|
|
}))
|
|
}
|
|
```
|
|
|
|
```js filename="app/product/sitemap.js" switcher
|
|
import { BASE_URL } from '@/app/lib/constants'
|
|
|
|
export async function generateSitemaps() {
|
|
// Fetch the total number of products and calculate the number of sitemaps needed
|
|
return [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]
|
|
}
|
|
|
|
export default async function sitemap(props) {
|
|
const id = await props.id
|
|
// Google's limit is 50,000 URLs per sitemap
|
|
const start = id * 50000
|
|
const end = start + 50000
|
|
const products = await getProducts(
|
|
`SELECT id, date FROM products WHERE id BETWEEN ${start} AND ${end}`
|
|
)
|
|
return products.map((product) => ({
|
|
url: `${BASE_URL}/product/${product.id}`,
|
|
lastModified: product.date,
|
|
}))
|
|
}
|
|
```
|
|
|
|
Your generated sitemaps will be available at `/.../sitemap/[id]`. For example, `/product/sitemap/1.xml`.
|
|
|
|
See the [`generateSitemaps` API reference](/docs/app/api-reference/functions/generate-sitemaps) for more information.
|
|
|
|
## Returns
|
|
|
|
The default function exported from `sitemap.(xml|ts|js)` should return an array of objects with the following properties:
|
|
|
|
```tsx
|
|
type Sitemap = Array<{
|
|
url: string
|
|
lastModified?: string | Date
|
|
changeFrequency?:
|
|
| 'always'
|
|
| 'hourly'
|
|
| 'daily'
|
|
| 'weekly'
|
|
| 'monthly'
|
|
| 'yearly'
|
|
| 'never'
|
|
priority?: number
|
|
alternates?: {
|
|
languages?: Languages<string>
|
|
}
|
|
}>
|
|
```
|
|
|
|
## Version History
|
|
|
|
| Version | Changes |
|
|
| ---------- | ------------------------------------------------------------ |
|
|
| `v16.0.0` | `id` is now a promise that resolves to a `string`. |
|
|
| `v14.2.0` | Add localizations support. |
|
|
| `v13.4.14` | Add `changeFrequency` and `priority` attributes to sitemaps. |
|
|
| `v13.3.0` | `sitemap` introduced. |
|