VOOZH about

URL: https://dev.to/junhee916/escaping-the-nextjs-14-sitemap-image-bug-a-guide-to-implementing-dynamic-mode-manually-2026-5ghp

⇱ Escaping the Next.js 14 sitemap-image Bug! A Guide to Implementing Dynamic Mode Manually (2026) - DEV Community


Escaping the Next.js 14 Sitemap-Image Bug! A Guide to Implementing Dynamic Mode Manually (2026)

I wanted to share an experience I had recently while working on a project with Next.js 14, where I got stuck with the sitemap-image extension. I was using the MetadataRoute.Sitemap feature in dynamic mode, but no matter how much I filled out the images field, sitemap-image just wouldn't work correctly.

Attempts and Pitfalls

At first, I tried to use Next.js 14's MetadataRoute.Sitemap feature as is. I added the images field in the src/app/sitemap.ts file and checked it with curl. Even though I clearly included images: [...], not a single <image:image> tag was picked up.

// src/app/sitemap.ts (Initial Attempt)
import { MetadataRoute } from 'next';

export default function sitemap(): MetadataRoute.Sitemap {
 return [
 {
 url: 'https://example.com/posts/1',
 lastModified: new Date('2026-05-17'),
 images: [
 {
 url: 'https://example.com/images/post1.jpg',
 alt: 'Post 1 Image',
 width: 1200,
 height: 630,
 },
 ],
 },
 // ... other URLs
 ];
}

When I checked with the command curl -I https://example.com/sitemap.xml, it seemed like there was a compatibility issue within Next.js between dynamic mode and the images field of MetadataRoute.Sitemap, as the images field wasn't being reflected properly. I reached this conclusion after about 3 hours of debugging.

The Cause

In conclusion, it was a bug where Next.js 14's dynamic mode couldn't properly parse the images field in MetadataRoute.Sitemap and reflect it in the sitemap-image extension. It seemed to be an issue with Next.js itself, so instead of trying to fix it directly, I had to find a workaround.

The Solution

So, I switched to generating the sitemap XML manually, similar to how I would create rss.xml/route.ts. I created a src/app/sitemap.xml/route.ts file and implemented the sitemap protocol and sitemap-image extension myself.

// src/app/sitemap.xml/route.ts
import { MetadataRoute } from 'next';

export async function GET() {
 const sitemapEntries: MetadataRoute.Sitemap = [
 {
 url: 'https://example.com/posts/1',
 lastModified: new Date('2026-05-17'),
 changefreq: 'weekly',
 priority: 0.9,
 images: [
 {
 url: 'https://example.com/images/post1.jpg',
 alt: 'Post 1 Image',
 width: 1200,
 height: 630,
 },
 ],
 },
 {
 url: 'https://example.com/about',
 lastModified: new Date('2026-05-17'),
 changefreq: 'monthly',
 priority: 0.7,
 },
 ];

 const 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">
${sitemapEntries.map(entry => `
 <url>
 <loc>${entry.url}</loc>
 <lastmod>${entry.lastModified.toISOString().split('T')[0]}</lastmod>
 <changefreq>${entry.changefreq || 'daily'}</changefreq>
 <priority>${entry.priority || 0.5}</priority>
 ${entry.images?.map(image => `
 <image:image>
 <image:loc>${image.url}</image:loc>
 <image:caption>${image.alt || ''}</image:caption>
 </image:image>`).join('') || ''}
 </url>
`).join('')}
</urlset>`;

 return new Response(xml, {
 headers: {
 'Content-Type': 'application/xml',
 },
 });
}

By doing this, I explicitly added the xmlns:image namespace and could directly control the <image:image> and <image:loc> tags for each URL. Attributes like lastmod, changefreq, and priority were also set directly, making it much more stable.

Results

  • The sitemap-image extension now works correctly, allowing search engines to index images.
  • SEO optimization has been strengthened, increasing the chances of visibility in search results.
  • Stability has been secured by reducing direct dependency on Next.js bugs.

In Summary — To Avoid the Same Pitfall

  • [ ] Always keep in mind that unexpected bugs can occur when using built-in Next.js features, especially in dynamic mode.
  • [ ] When using MetadataRoute, make it a habit to verify directly with curl or other tools whether specific features like the images field are working correctly.
  • [ ] For SEO-related features, implementing them directly can be more stable and beneficial for optimization in the long run.
  • [ ] The approach of generating XML directly, like with sitemap.xml/route.ts, offers high flexibility, so consider it if necessary.