Skip to content

Commit

Permalink
Sitemap image tag support (#68034)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
3 people committed Aug 7, 2024
1 parent b931a83 commit 93ad2f2
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,46 @@ Output:
</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>
```

### Generate a localized Sitemap

```ts filename="app/sitemap.ts" switcher
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,32 @@ describe('resolveRouteData', () => {
"
`)
})
it('should resolve sitemap.xml with images', () => {
expect(
resolveSitemap([
{
url: 'https://example.com',
lastModified: '2021-01-01',
changeFrequency: 'weekly',
priority: 0.5,
images: ['https://example.com/image.jpg'],
},
])
).toMatchInlineSnapshot(`
"<?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>
"
`)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ export function resolveSitemap(data: MetadataRoute.Sitemap): string {
const hasAlternates = data.some(
(item) => Object.keys(item.alternates ?? {}).length > 0
)
const hasImages = data.some((item) => Boolean(item.images?.length))

let content = ''
content += '<?xml version="1.0" encoding="UTF-8"?>\n'
content += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"'
if (hasImages) {
content += ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"'
}
if (hasAlternates) {
content += ' xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
} else {
Expand All @@ -70,6 +74,11 @@ export function resolveSitemap(data: MetadataRoute.Sitemap): string {
}" />\n`
}
}
if (item.images?.length) {
for (const image of item.images) {
content += `<image:image>\n<image:loc>${image}</image:loc>\n</image:image>\n`
}
}
if (item.lastModified) {
const serializedDate =
item.lastModified instanceof Date
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/lib/metadata/types/metadata-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ type SitemapFile = Array<{
alternates?: {
languages?: Languages<string>
}
images?: string[]
}>

type ResolvingMetadata = Promise<ResolvedMetadata>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://example.com',
lastModified: '2024-01-01',
changeFrequency: 'daily',
priority: 0.5,
},
{
url: 'https://example.com/about',
lastModified: '2024-01-01',
images: [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
],
},
]
}
12 changes: 12 additions & 0 deletions test/e2e/app-dir/metadata-dynamic-routes/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ describe('app dir - metadata dynamic routes', () => {
`<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/about" />`
)
})

it('should support images in sitemap', async () => {
const xml = await (await next.fetch('/sitemap-image/sitemap.xml')).text()

expect(xml).toContain(
`<image:image>\n<image:loc>https://example.com/image1.jpg</image:loc>\n</image:image>`
)
expect(xml).toContain(
`<image:image>\n<image:loc>https://example.com/image2.jpg</image:loc>\n</image:image>`
)
})

if (isNextStart) {
it('should optimize routes without multiple generation API as static routes', async () => {
const appPathsManifest = JSON.parse(
Expand Down
1 change: 1 addition & 0 deletions test/turbopack-build-tests-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,7 @@
"app dir - metadata dynamic routes sitemap should not throw if client components are imported but not used in sitemap",
"app dir - metadata dynamic routes sitemap should optimize routes without multiple generation API as static routes",
"app dir - metadata dynamic routes sitemap should support alternate.languages in sitemap",
"app dir - metadata dynamic routes sitemap should support images in sitemap",
"app dir - metadata dynamic routes sitemap should support generate multi sitemaps with generateSitemaps",
"app dir - metadata dynamic routes social image routes should fill params into dynamic routes url of metadata images",
"app dir - metadata dynamic routes social image routes should fill params into routes groups url of static images",
Expand Down

0 comments on commit 93ad2f2

Please sign in to comment.