Keeping noindex, the sitemap and the image sitemap in agreement for programmatic pages A developer maintaining LandscapeCraft, a Next.js App Router site with 40 programmatically generated design-style pages, documented how a single getStyleContent predicate keeps noindex tags, sitemap.xml and image-sitemap.xml in agreement for thin pages lacking long-form MDX content. Testing on an archived copy built with Next.js 16.3.4 on Node 22.12.0 reproduced the live site's 249 sitemap URLs and 199 image-sitemap entries, and commit history showed the noindex branch had never actually fired because every style shipped with a write-up. Google's documentation for sitemaps says to "include the URLs in your sitemap that you want to see in Google's search results". Its documentation for the robots meta tag says the noindex rule means: "Do not show this page, media, or resource in search results." A URL that carries both is giving two answers, and when the pages are generated from one array it is easy for them to drift apart without anyone noticing. LandscapeCraft landscapecraft.ai is a Next.js App Router site with 40 design-style pages generated from one TypeScript array. A style can have a long MDX write-up in content/styles/ . A style without one falls back to a short generic template, and the site treats that page as thin: it renders noindex, follow , and its URL stays out of both sitemap.xml and image-sitemap.xml . This post is about those three places: how the code keeps them in step, how I checked that it does, and where it can still drift. The code is from the repository as of 2026-11-02, and the live numbers are from the same day. Code comments below are translated from Chinese. The helper is small: js export function getStyleContent slug: string : StyleContent | null { const filePath = path.join STYLES CONTENT DIR, ${slug}.mdx ; if fs.existsSync filePath return null; const raw = fs.readFileSync filePath, "utf-8" ; const { content } = matter raw ; return { slug, content }; } The style page asks it for its metadata: // app/styles/ slug /page.tsx, inside generateMetadata // Style pages without long-form content they fall back to the generic template stay out of the index noIndex: getStyleContent slug , buildMetadata turns that flag into metadata.robots = { index: false, follow: true } , which renders as