Self-updating screenshots in Mintlify docs
Mintlify keeps your text in Git and rebuilds it on every change. The screenshots stay as they were the day someone took them. This guide puts an image URL in your MDX that your own tests refresh on every merge, inside Mintlify's Frame component, in light and dark.
The upkeep problem
Mintlify's own media guide says it directly: "Every screenshot you publish is a commitment to update it when the UI changes."
Its advice for living with that is sound. Crop tightly, skip screenshots of pages that change often, keep the source files, and note what each image shows so you can find the stale ones. For a redesign, it suggests treating the screenshot updates as a docs sprint. All of that makes recapturing cheaper. A person still has to notice, recapture and commit.
If your product has end-to-end tests, you can skip that step. A test reaches the screen, saves a screenshot, and CI publishes it at a URL your MDX points to. When the UI changes and the change merges, the next build replaces the image.
How Mintlify shows images
From Images and embeds and Frames in Mintlify's docs, checked October 2026:
- Markdown image syntax works, with root-relative paths from the docs repository, such as /images/screenshot.png. Files in the repository must be under 20 MB.
- An <img> tag takes Tailwind classes in className for size, such as w-[450px].
- <Frame> wraps an image with a border and takes optional caption and hint props.
- Light and dark images are two <img> tags, one with block dark:hidden and one with hidden dark:block.
- Mintlify optimizes images at a root-relative path. It serves AVIF or WebP, makes resized versions up to 2500px wide and adds width and height. It serves an image at an absolute URL as it is.
An absolute URL works wherever a path does. Mintlify's own examples load images from a CDN. So nothing changes on the Mintlify side, and nothing needs installing there. You swap a path for a URL. Docusaurus and Starlight can build that URL from a short reference with a docs plugin, but Mintlify does not run remark plugins, so here you paste the full URL.
The trade-off is that Mintlify doesn't optimize an image at a URL. Keep each capture to the size the page shows. Set the test's viewport, or screenshot one element instead of the whole page.
1. Capture the screenshots in your app's tests
The screenshots come from the repository that holds your app, whether your Mintlify content lives in that repository or its own. Take a light and a dark version of each screen:
// tests/docs.spec.ts
import { test, expect } from '@playwright/test';
for (const theme of ['light', 'dark'] as const) {
test(`billing settings (${theme})`, async ({ page }) => {
await page.emulateMedia({ colorScheme: theme });
await page.goto('/settings/billing');
await expect(page.getByRole('heading', { name: 'Billing' })).toBeVisible();
const suffix = theme === 'dark' ? '.dark' : '';
await page.screenshot({
path: `screenshots/settings/billing${suffix}.png`,
animations: 'disabled',
});
});
}
billing.png is published as settings/billing. billing.dark.png is not a second screenshot. It is the dark version of the same one, served when a page asks for ?theme=dark. Push both in the same push and save both in the same format. If your app has its own theme switch instead of following the system setting, set what the switch sets before the page loads. See Dark mode and Light, dark and other themes.
Sign-in, fixed data, fonts and viewports are covered in Screenshot automation. The aim is a test whose picture changes only when the UI does.
2. Publish from CI
This assumes a public Screenery project, acme/dashboard, created with the Screenery GitHub App on the app's repository. Getting started covers that. Add this workflow to the app's repository, not the docs one:
name: screenshots
on:
push:
branches: [main]
pull_request:
jobs:
screenshots:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: lts/*
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: screenery/push@v1
with:
project: acme/dashboard
path: screenshots
id-token: write lets the job prove to Screenery which repository it runs for, so there is no secret to store. pull-requests: write lets the job post a preview comment on pull requests. Every input is on the GitHub Action reference.
3. Embed the URL in MDX
The simplest form is a Markdown image:

Inside a Frame, with a caption:
<Frame caption="Billing settings, from the latest build">
<img
src="https://cdn.screenery.dev/acme/dashboard/settings/billing@latest.png"
alt="Billing settings with the current plan and payment method"
/>
</Frame>
With a light and a dark variant, using Mintlify's dark-mode classes:
<Frame caption="Billing settings">
<img
className="block dark:hidden"
src="https://cdn.screenery.dev/acme/dashboard/settings/billing@latest.png"
alt="Billing settings with the current plan and payment method"
/>
<img
className="hidden dark:block"
src="https://cdn.screenery.dev/acme/dashboard/settings/billing@latest.png?theme=dark"
alt="Billing settings with the current plan and payment method"
/>
</Frame>
Both images use the same @latest URL, and the dark one adds ?theme=dark. A missing theme is never a 404. If a screen has no dark capture yet, the dark URL serves the light image, so you can write every embed this way from the start.
The push log prints a ready-made Markdown snippet for every public screenshot, so you rarely type a URL by hand. The extension is the format you saved. Screenery doesn't convert formats, so a PNG stays .png.
Replacing images you already have
- Search your MDX for /images/ and list the screenshots of your product.
- Write one test per screen that reaches the right state and saves the file under a stable name.
- Merge, then swap each path for its @latest URL.
- Delete the old PNGs from the docs repository once the new embeds show up.
Keep repository images for things that rarely change, such as logos and diagrams. The URLs are for screens of a UI that keeps moving.
What happens after a merge
- A pull request leaves your docs alone. Its screenshots land on @pr-12, and the preview comment shows what changed against @latest.
- Merging moves @latest. The build on the default branch replaces the image behind every embed. The MDX doesn't change, so there's no docs commit and no Mintlify redeploy. A browser may keep the old image for up to five minutes.
- The first push creates @latest from whatever branch it came from, so the URLs work while the setup pull request is still open. That happens once per project.
- Renaming a test breaks its URL. There are no redirects. Treat a screenshot name like a route.
- A changelog entry can pin one image. A version URL ending in @v_… never changes.
The full rules are in How @latest moves. To fail a pull request when a screenshot changes by accident, see Visual regression testing in CI.
Mintlify's agent
Mintlify's agent docs say it can look at the images in your docs and check whether a screenshot still matches the content. They don't say it takes a new screenshot. So the agent can tell you an image is out of date, and a person retakes it.
With Screenery URLs in your MDX, the retaking happens in CI instead. The agent checks the words and the images, and your tests replace the images. Checked October 2026 against Mintlify's docs.
FAQ
- Can Mintlify show an image from an external URL?
- Yes. Put the full URL in Markdown image syntax or in an img tag's src. Mintlify's own examples load images from a CDN. Root-relative paths are for images stored in the docs repository, and those files must be under 20 MB.
- How do I show a different screenshot in Mintlify's dark mode?
- Use two img tags. Give the light one className="block dark:hidden" and the dark one className="hidden dark:block", which is Mintlify's documented pattern. With Screenery, both use the same @latest URL and the dark one adds ?theme=dark. If no dark screenshot was captured, that URL serves the light image instead of a 404.
- Do I have to redeploy my Mintlify docs when a screenshot changes?
- No. The MDX keeps the same URL, and Screenery changes the image behind it when your default branch builds. A browser may keep the old image for up to five minutes.
- Will an open pull request change the screenshots in my Mintlify docs?
- No. A pull request publishes to its own channel, such as @pr-12. Only a push on the default branch, or a promotion, moves @latest. The one exception is a new project's first push, which creates @latest from whatever branch it came from.
- Does Mintlify's agent retake screenshots?
- Mintlify's agent docs say it can look at the images in your docs and check whether a screenshot still matches the content. They don't say it takes a new screenshot. Screenery does the retaking, from your tests, so the two can work side by side.
Related guides
- Screenshot automation for docs and READMEs
- How to keep documentation screenshots up to date
- README screenshots that update themselves
- Markdown image syntax
Mintlify details come from Mintlify's own docs, checked October 2026. Follow the links for their current behaviour.