Escaping the Next.js 14 sitemap-image Bug! A Guide to Implementing Dynamic Mode Manually (2026)
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 withcurlor other tools whether specific features like theimagesfield 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.