Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 25, 2026

Build-time OG Image Generation

  • Implement build-time OG image generation script
  • Generate static PNG images compatible with static export
  • Update page metadata to use generated images
  • Test image generation
  • Verify build process
  • Configure as build artifacts (not committed to git)
  • Add Chromatic visual tests for OG images (one test per page)
  • Simplify build script
  • Fix broken OG image snapshots in Playwright tests
  • Code cleanup and simplification
  • Skip responsive viewport tests for OG images
  • Fix CI test failure

Implementation

Created a build-time script that generates Open Graph images before the Next.js build:

Features:

  • Crawls all MDX files in the docs directory
  • Extracts title and description from frontmatter
  • Generates 1200x630 PNG images using @vercel/og
  • Saves images to public/og/{slug}.png
  • Images are generated during build, not committed to repository
  • Each OG image is individually tested in Chromatic

Script: scripts/generate-og-images.mts

  • Standalone script that doesn't import React components
  • Uses gray-matter to parse MDX frontmatter
  • Uses @vercel/og ImageResponse API
  • Generates branded images with theme colors

Build Process: Integrated into build script

  • Runs as: tsx scripts/generate-og-images.mts && next build
  • OG images generated first, then Next.js build includes them
  • Next.js automatically copies public/ files to out/ during static export

Page Metadata: Updated [...slug]/page.tsx

  • References generated images at /og/${slug.join('/')}.png
  • Includes proper width/height/alt metadata

Chromatic Tests: [...slug]/page.test.ts

  • Individual test for each page's OG image (e.g., introduction og)
  • Tests use minimal HTML template with shared ogImageTemplate() function
  • OG image tests skip the w375 viewport (images are fixed 1200x630, no responsive testing needed)
  • Chromatic captures screenshots of each OG image
  • Visual regression testing for all generated thumbnails

Example Output

The generated images feature:

  • Site name with colored accent bar (THEME_PRIMARY)
  • Page title in large, bold text
  • Page description
  • Domain name in footer

Why External Script (Not opengraph-image.tsx)

Next.js Limitation: opengraph-image.tsx cannot be used inside catch-all routes like [...slug]. Attempting to use it results in a build error:

Error: Catch-all must be the last part of the URL in route "/[...slug]/opengraph-image".

While opengraph-image.tsx works for static routes and simple dynamic routes, it's incompatible with catch-all segments. The external script approach is the only solution that works with both:

  • Static export (output: 'export')
  • Catch-all routes ([...slug])

Build Artifacts & Chromatic

The generated OG images are not committed to the repository as they are build artifacts:

  • public/og/ is in .gitignore
  • Images are regenerated fresh on each build
  • This keeps the repository clean and avoids committing generated files

However, the images are captured by Chromatic for visual testing:

  • The build process runs during CI (triggered by pnpm run build in start.sh)
  • Build script runs OG generation then Next.js build
  • Next.js build automatically copies images from public/og/ to out/og/
  • Each page has a dedicated Playwright test that renders the OG image
  • OG image tests only run in the w1440 viewport (skips w375 since images are fixed size)
  • Chromatic captures individual screenshots of each OG image
  • Visual changes to OG images will appear in Chromatic reviews

Technical Details

  • Images generated at the start of the build process via build script
  • Compatible with static export (output: 'export')
  • No server runtime required
  • Images are build artifacts (not tracked in git)
  • Each build regenerates all OG images fresh
  • Each OG image is individually tested via Chromatic (single viewport only)
  • Tests use minimal HTML (just body and img tags)

This approach fulfills the requirement for dynamic per-page thumbnails while remaining compatible with the repository's static export configuration and ensuring comprehensive visual regression testing.

Original prompt

This section details on the original issue you should resolve

<issue_title>dynamic generated pages' thumbs</issue_title>
<issue_description>like
image

see: https://nextjs.org/docs/app/getting-started/metadata-and-og-images#generated-open-graph-images
</issue_description>

<agent_instructions>implement such per-page, dynamic generated thumbs</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@abernier @copilot

implement such per-page, dynamic generated thumbs</comment_new>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits January 25, 2026 14:43
Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copilot AI changed the title [WIP] Add dynamic generated thumbnails for pages Add dynamic Open Graph image generation per documentation page Jan 25, 2026
Copilot AI requested a review from abernier January 25, 2026 14:49
@abernier
Copy link
Member

The /api/og route cannot work with output: 'export' which is used by this project. Next.js API routes are not supported in static exports. Removed the API route to fix the build failure.

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 25, 2026

@copilot err https://github.com/pmndrs/docs/actions/runs/21334437150/job/61408380809?pr=484

Fixed in commit 86bd0d8.

The build was failing because this repository uses output: 'export' for static site generation, which doesn't support API routes. Dynamic OG image generation (like the Next.js docs example you linked) requires a Node.js server and is incompatible with static exports.

I've removed the /api/og route. For static exports, OG images must be:

  • Specified in frontmatter (image: path/to/image.png)
  • Or use the LOGO environment variable as fallback

To implement dynamic OG images, the project would need to either:

  1. Remove output: 'export' and deploy to a Node.js environment, or
  2. Pre-generate all OG images at build time as static PNG files

The build should now pass.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • codesandbox.io
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/docs/docs/node_modules/.pnpm/next@16.1.3_@babel&#43;core@7.28.6_@playwright&#43;test@1.57.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)
  • opencollective.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/docs/docs/node_modules/.pnpm/next@16.1.3_@babel&#43;core@7.28.6_@playwright&#43;test@1.57.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Properly revert page.tsx to use doc.image instead of the non-existent /api/og route

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copilot AI changed the title Add dynamic Open Graph image generation per documentation page Revert dynamic OG image changes - incompatible with static export Jan 25, 2026
@abernier
Copy link
Member

@copilot non, l'idee serait de generer le thumnail au build ! (tout comme les pages)

Add prebuild script that generates static OG images for all documentation pages using @vercel/og. Images are created at build time and saved to public/og/, making them compatible with static export.

- Created scripts/generate-og-images.mts to crawl MDX files and generate 1200x630 PNG images
- Updated package.json to add @vercel/og and tsx dependencies
- Added prebuild script to run OG generation before Next.js build
- Updated page metadata to reference generated images at /og/{slug}.png
- Added public/og/ to .gitignore as build artifacts

This approach works with output: 'export' by generating images at build time rather than runtime.

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 25, 2026

@copilot non, l'idee serait de generer le thumnail au build ! (tout comme les pages)

Exactement ! Implemented in commit 89c4485.

The OG images are now generated at build time using a prebuild script:

  • Runs before next build via the prebuild npm script
  • Crawls all MDX files and extracts title/description from frontmatter
  • Generates 1200x630 PNG images using @vercel/og
  • Saves to public/og/{slug}.png as static files
  • Compatible with output: 'export' since images are static build artifacts

Each page now gets a branded thumbnail with the site name, page title, description, and theme colors.

Example OG Image

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • codesandbox.io
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/docs/docs/node_modules/.pnpm/next@16.1.3_@babel&#43;core@7.28.6_@playwright&#43;test@1.57.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)
  • opencollective.com
    • Triggering command: /usr/local/bin/node /usr/local/bin/node /home/REDACTED/work/docs/docs/node_modules/.pnpm/next@16.1.3_@babel&#43;core@7.28.6_@playwright&#43;test@1.57.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/next/dist/compiled/jest-worker/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Revert dynamic OG image changes - incompatible with static export Add build-time Open Graph image generation for documentation pages Jan 25, 2026
@abernier
Copy link
Member

@copilot cool, peux-tu inclure les thumbnails generes afin qu'ils apparaissent dans Chromatic (afin qu'ils soient eux aussi: visual testés)

Remove public/og/ from .gitignore and commit the generated OG images so they are tracked in the repository and included in Chromatic visual regression tests.

Generated images:
- getting-started/authoring.png
- getting-started/github-actions.png
- getting-started/introduction.png

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 25, 2026

@copilot cool, peux-tu inclure les thumbnails generes afin qu'ils apparaissent dans Chromatic (afin qu'ils soient eux aussi: visual testés)

Done in commit 55774e8.

The generated OG images are now committed to the repository in public/og/ so Chromatic can visually test them alongside the documentation pages.

Committed images:

  • public/og/getting-started/authoring.png
  • public/og/getting-started/github-actions.png
  • public/og/getting-started/introduction.png

Example OG Image

The images will be regenerated on each build and can be visually tested in Chromatic for any changes.

@abernier
Copy link
Member

@copilot cool, peux-tu inclure les thumbnails generes afin qu'ils apparaissent dans Chromatic (afin qu'ils soient eux aussi: visual testés)

Done in commit 55774e8.

The generated OG images are now committed to the repository in public/og/ so Chromatic can visually test them alongside the documentation pages.

Committed images:

  • public/og/getting-started/authoring.png
  • public/og/getting-started/github-actions.png
  • public/og/getting-started/introduction.png

Example OG Image

The images will be regenerated on each build and can be visually tested in Chromatic for any changes.

i want them to be part of the build but not git-tracked (as they are generated)

@abernier
Copy link
Member

@copilot chromatic og snapshots are still broken images

image

maybe because playwright is navigating to the file directly:

await page.goto('/og/getting-started/introduction.png')

(instead of a page containing an ) ?

@copilot

… navigation

Changed Playwright tests to use page.setContent() with <img> tags instead of navigating directly to PNG files. This ensures:
- Images render properly in an HTML context
- Chromatic captures the actual image content, not a broken file
- Tests work consistently across different static file servers

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 26, 2026

@copilot chromatic og snapshots are still broken images

image

...

Fixed in commit 5ecbfa9.

You were absolutely right! Navigating directly to the PNG file was causing the issue. Changed the tests to use page.setContent() with HTML containing <img> tags instead:

await page.setContent(`
  <!DOCTYPE html>
  <html>
    <body style="margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #000;">
      <img src="/og/getting-started/introduction.png" alt="Introduction OG Image" style="max-width: 100%; height: auto;" />
    </body>
  </html>
`)

This ensures the images render properly in an HTML context and Chromatic can capture the actual image content instead of a broken file.

Copy link
Member

@abernier abernier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ce script n'est maintenant je pense plus necessaire, tu peux l'enlever lui et ses references

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit e5cd050. Removed the copy-og-images script - you're right that it's not needed since Next.js build automatically copies files from public/ to out/ during static export.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ne documente pas : c'est trivial

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit e5cd050. Removed scripts/README.md.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setContent: reutilise un meme template entre les 3?

nb: simplifie au max, pas besoin de title... etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit e5cd050. Created shared ogImageTemplate() function and simplified HTML to bare minimum - just body with styles and img tag.

- Removed scripts/copy-og-images.mts (no longer needed - Next.js build copies public/ to out/)
- Removed scripts/README.md (documentation was trivial)
- Simplified OG image tests to use shared template function
- Removed unnecessary HTML elements (doctype, html, head, title, alt attributes)
- Made test code more DRY and maintainable

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copy link
Member

@abernier abernier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot supprime on est sur pnpm !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit d07ba60.

//

const ogImageTemplate = (src: string) => `
<body style="margin:0;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#000">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pas la peine de changer le margin, display, background du body ou meme sa taille... fais au plus simple

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit d07ba60. Simplified to just <body><img src="${src}" /></body>.

- Deleted package-lock.json (repo uses pnpm, not npm)
- Simplified ogImageTemplate to bare minimum (just body and img tags)
- Removed unnecessary inline styles (margin, display, justify-content, align-items, min-height, background, max-width, height)

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
@abernier
Copy link
Member

@copilot les test() pour les images og n'ont eux (contrairement aux autres pour les pages responsives) pas besoin de se faire dans plusieurs formats de viewport (actuellement définis globalement dans la playwright.config.ts)

comment eviter les snapshots responsives pour ces tests d'images og uniquement ?

Wrapped OG image tests in test.describe() block and added test.skip() to skip the w375 viewport project. OG images are always 1200x630 pixels so they don't need to be tested at multiple viewport sizes like the responsive page tests.

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 26, 2026

@copilot les test() pour les images og n'ont eux (contrairement aux autres pour les pages responsives) pas besoin de se faire dans plusieurs formats de viewport (actuellement définis globalement dans...

Fixed in commit 96705a8.

Wrapped the OG image tests in a test.describe() block with test.skip() to skip the w375 viewport project. The OG images will now only be tested in the w1440 viewport since they're always 1200x630 pixels and don't need responsive testing.

@abernier
Copy link
Member

@copilot ci fails since your last commit

The test.skip() was using an unused browserName parameter from the fixtures destructuring, which caused a CI failure. Fixed by removing the unused parameter.

Co-authored-by: abernier <76580+abernier@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 26, 2026

@copilot ci fails since your last commit

Fixed in commit 80266a9. The issue was an unused browserName parameter in the test.skip() fixtures destructuring. Removed it to fix the CI failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dynamic generated pages' thumbs

2 participants