Skip to content

Commit 93ad2f2

Browse files
archanaagivale30huozhidelbaoliveira
authored
Sitemap image tag support (#68034)
Image Tag should be available in sitemap.xml, which is not currently support in sitemap. In this pr, I solved issue #68033 which I faced in my project. cc @leerob @delbaoliveira @samcx x-ref: https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps --------- Co-authored-by: Jiachi Liu <inbox@huozhi.im> Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com>
1 parent b931a83 commit 93ad2f2

File tree

7 files changed

+110
-0
lines changed

7 files changed

+110
-0
lines changed

docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,46 @@ Output:
120120
</urlset>
121121
```
122122

123+
### Image Sitemaps
124+
125+
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).
126+
127+
```ts filename="app/sitemap.ts" switcher
128+
import type { MetadataRoute } from 'next'
129+
130+
export default function sitemap(): MetadataRoute.Sitemap {
131+
return [
132+
{
133+
url: 'https://example.com',
134+
lastModified: '2021-01-01',
135+
changeFrequency: 'weekly',
136+
priority: 0.5,
137+
images: ['https://example.com/image.jpg'],
138+
},
139+
]
140+
}
141+
```
142+
143+
Output:
144+
145+
```xml filename="acme.com/sitemap.xml"
146+
<?xml version="1.0" encoding="UTF-8"?>
147+
<urlset
148+
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
149+
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
150+
>
151+
<url>
152+
<loc>https://example.com</loc>
153+
<image:image>
154+
<image:loc>https://example.com/image.jpg</image:loc>
155+
</image:image>
156+
<lastmod>2021-01-01</lastmod>
157+
<changefreq>weekly</changefreq>
158+
<priority>0.5</priority>
159+
</url>
160+
</urlset>
161+
```
162+
123163
### Generate a localized Sitemap
124164

125165
```ts filename="app/sitemap.ts" switcher

packages/next/src/build/webpack/loaders/metadata/resolve-route-data.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,32 @@ describe('resolveRouteData', () => {
126126
"
127127
`)
128128
})
129+
it('should resolve sitemap.xml with images', () => {
130+
expect(
131+
resolveSitemap([
132+
{
133+
url: 'https://example.com',
134+
lastModified: '2021-01-01',
135+
changeFrequency: 'weekly',
136+
priority: 0.5,
137+
images: ['https://example.com/image.jpg'],
138+
},
139+
])
140+
).toMatchInlineSnapshot(`
141+
"<?xml version="1.0" encoding="UTF-8"?>
142+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
143+
<url>
144+
<loc>https://example.com</loc>
145+
<image:image>
146+
<image:loc>https://example.com/image.jpg</image:loc>
147+
</image:image>
148+
<lastmod>2021-01-01</lastmod>
149+
<changefreq>weekly</changefreq>
150+
<priority>0.5</priority>
151+
</url>
152+
</urlset>
153+
"
154+
`)
155+
})
129156
})
130157
})

packages/next/src/build/webpack/loaders/metadata/resolve-route-data.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,14 @@ export function resolveSitemap(data: MetadataRoute.Sitemap): string {
4747
const hasAlternates = data.some(
4848
(item) => Object.keys(item.alternates ?? {}).length > 0
4949
)
50+
const hasImages = data.some((item) => Boolean(item.images?.length))
5051

5152
let content = ''
5253
content += '<?xml version="1.0" encoding="UTF-8"?>\n'
5354
content += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"'
55+
if (hasImages) {
56+
content += ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"'
57+
}
5458
if (hasAlternates) {
5559
content += ' xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
5660
} else {
@@ -70,6 +74,11 @@ export function resolveSitemap(data: MetadataRoute.Sitemap): string {
7074
}" />\n`
7175
}
7276
}
77+
if (item.images?.length) {
78+
for (const image of item.images) {
79+
content += `<image:image>\n<image:loc>${image}</image:loc>\n</image:image>\n`
80+
}
81+
}
7382
if (item.lastModified) {
7483
const serializedDate =
7584
item.lastModified instanceof Date

packages/next/src/lib/metadata/types/metadata-interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ type SitemapFile = Array<{
607607
alternates?: {
608608
languages?: Languages<string>
609609
}
610+
images?: string[]
610611
}>
611612

612613
type ResolvingMetadata = Promise<ResolvedMetadata>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { MetadataRoute } from 'next'
2+
3+
export default function sitemap(): MetadataRoute.Sitemap {
4+
return [
5+
{
6+
url: 'https://example.com',
7+
lastModified: '2024-01-01',
8+
changeFrequency: 'daily',
9+
priority: 0.5,
10+
},
11+
{
12+
url: 'https://example.com/about',
13+
lastModified: '2024-01-01',
14+
images: [
15+
'https://example.com/image1.jpg',
16+
'https://example.com/image2.jpg',
17+
],
18+
},
19+
]
20+
}

test/e2e/app-dir/metadata-dynamic-routes/index.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ describe('app dir - metadata dynamic routes', () => {
103103
`<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/about" />`
104104
)
105105
})
106+
107+
it('should support images in sitemap', async () => {
108+
const xml = await (await next.fetch('/sitemap-image/sitemap.xml')).text()
109+
110+
expect(xml).toContain(
111+
`<image:image>\n<image:loc>https://example.com/image1.jpg</image:loc>\n</image:image>`
112+
)
113+
expect(xml).toContain(
114+
`<image:image>\n<image:loc>https://example.com/image2.jpg</image:loc>\n</image:image>`
115+
)
116+
})
117+
106118
if (isNextStart) {
107119
it('should optimize routes without multiple generation API as static routes', async () => {
108120
const appPathsManifest = JSON.parse(

test/turbopack-build-tests-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2063,6 +2063,7 @@
20632063
"app dir - metadata dynamic routes sitemap should not throw if client components are imported but not used in sitemap",
20642064
"app dir - metadata dynamic routes sitemap should optimize routes without multiple generation API as static routes",
20652065
"app dir - metadata dynamic routes sitemap should support alternate.languages in sitemap",
2066+
"app dir - metadata dynamic routes sitemap should support images in sitemap",
20662067
"app dir - metadata dynamic routes sitemap should support generate multi sitemaps with generateSitemaps",
20672068
"app dir - metadata dynamic routes social image routes should fill params into dynamic routes url of metadata images",
20682069
"app dir - metadata dynamic routes social image routes should fill params into routes groups url of static images",

0 commit comments

Comments
 (0)