An Astro integration for generating screenshots of your pages automatically at build time
Warning
This is currently an experimental project or proof-of-concept. It may contain bugs or incomplete features, and is not intended for production use. Breaking changes may be made at any time. Consider more stable alternatives for critical applications.
Generate screenshots of your Astro pages automatically at build time with Astro Snapshot. Perfect for creating social media images, content previews, and dynamic icons.
This integration was inspired by a similar plugin I wrote for Gatsby called Gatsby Plugin: Component to Image and astro-selfie. Compared to astro-selfie, this integration exposes a lot more configuration options that allow you to completely customize how images are generated.
- π Works with any page : Generate one or more images from any valid Astro page
- Not limited by presets or available integration options like other solutions
- Not limited by types of JSX elements or CSS properties supported by Satori
- Use whatever front-end framework you want
- π· Configurable output filetypes: Generate PNG, JPEG, or WebP images with arbitrary dimensions
- Formats are automatically detected from the file extension
- Pass though options to Puppeteer for precise control of image quality, encoding speed, and more
- π Customizable output paths: Full control over paths of the generated images
- Save images to the
publicdirectory to include them in the build, unprocessed - Save images to the
distdirectory if you don't want to include theme in source control - Save them in the
srcdir for further compression or importing into components* - Or save them somewhere else, your choice
- Save images to the
- ποΈ Default options: Reuse the same options for multiple images
- Provide defaults for all options and override them on a per-image basis
- π§ TypeScript support: Full type safety for all options and functions
- No need to worry about typos or incorrect config values
- ποΈ Social images: Use your existing front-end components to generate Open Graph images and/or Twitter cards for your blog posts or other content
- π° Content previews: Generate screenshots of your website for use in documentation, marketing materials
- πΌοΈ Favicons: Dynamically generate favicons for your website
Important
Note that, because this plugin runs after the build completes, you will not be able to import the generated images into your components or perform any further operations with them in the same build cycle.
You can, however, use them in the next build, provided they are not overwritten. If you do this, make sure to account
for the images not existing the first time you perform a build (i.e. use a placeholder image or catch errors from
import statements).
After the Astro build completes, this plugin uses Puppeteer to render the pages in a headless browser and save screenshots of the rendered content as images
Tip
If you see any warnings like Cannot find package 'puppeteer' after adding the integration, your package manager may
not have installed peer dependencies for you. If this happens, install Puppeteer manually like so:
npm install puppeteerThis package is available on both JSR and
npm. It's also support the astro add command to update
your astro.config.js automatically.
Note
This grabs the package from NPM. If you want to use the JSR version, you will need to install it manually.
We can use the Astro CLI to install the integration automatically using your preferred package manager:
π¦ Deno
deno run -A astro add astro-snapshotπ₯ Bun
bunx astro add astro-snapshotπ’ npm
npx astro add astro-snapshotπ§ pnpm
pnpm astro add astro-snapshotπ§Ά yarn
yarn astro add astro-snapshotπ vlt
vlt astro add astro-snapshotIf you run into any issues, try the manual installation steps below.
Tip
JSR has some advantages if you're using TypeScript or Deno:
- It ships typed, modern ESM code by default
- No need for separate type declarations
- Faster, leaner installs without extraneous files
You can use JSR with your favorite package manager.
First, install it using your preferred package manager:
π¦ Deno
deno add jsr:@twocaretcat/astro-snapshot # JSR (recommended)deno add npm:@twocaretcat/astro-snapshot # npmπ₯ Bun
bunx jsr add @twocaretcat/astro-snapshot # JSRbun add @twocaretcat/astro-snapshot # npmπ’ npm
npx jsr add @twocaretcat/astro-snapshot # JSRnpm install @twocaretcat/astro-snapshot # npmπ§ pnpm
pnpm i jsr:@twocaretcat/astro-snapshot # JSRpnpm add @twocaretcat/astro-snapshot # npmπ§Ά yarn
yarn add jsr:@twocaretcat/astro-snapshot # JSRyarn add @twocaretcat/astro-snapshot # npmπ vlt
vlt install jsr:@twocaretcat/astro-snapshot # JSRvlt install @twocaretcat/astro-snapshot # npmThen, apply the integration to your astro.config.* file using the integrations property:
// astro.config.mjs
import { defineConfig } from 'astro/config';
+import snapshot from 'astro-snapshot';
export default defineConfig({
// ...
- integrations: [],
+ integrations: [snapshot()],
});Add the integration to your astro.config.mjs or astro.config.ts file and configure it like so:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import snapshot from 'astro-snapshot';
export default defineConfig({
integrations: [
snapshot({
pages: {
// Single screenshot for homepage
'/': [
{
outputPath: 'public/og/home.png',
},
],
// Multiple screenshots for about page (different sizes)
'/about': [
{
outputPath: 'public/og/about-og.png',
width: 1200,
height: 630,
},
{
outputPath: 'public/og/about-square.jpg',
width: 1080,
height: 1080,
},
{
outputPath: 'public/og/about-twitter.png',
width: 1200,
height: 675,
},
],
},
}),
],
});// astro.config.mjs
import { defineConfig } from 'astro/config';
import snapshot from 'astro-snapshot';
export default defineConfig({
integrations: [
snapshot({
// Pages to screenshot (required)
pages: {
'/': [
{
outputPath: 'public/og/home.png',
width: 1200, // Viewport width (default: 1200)
height: 630, // Viewport height (default: 630)
// Puppeteer page.goto() options
gotoOptions: {
waitUntil: 'networkidle0',
timeout: 30000,
},
// Puppeteer page.screenshot() options
screenshotOptions: {
quality: 95, // For jpeg only
fullPage: false,
clip: { // Capture specific region
x: 0,
y: 0,
width: 1200,
height: 630,
},
},
},
],
},
// Default config for all screenshots (optional)
defaults: {
width: 1200,
height: 630,
gotoOptions: {
waitUntil: 'networkidle2',
},
},
// Port for preview server (default: 4322)
port: 4322,
// Puppeteer launch options
launchOptions: {
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
},
}),
],
});Tip
If you need to reference output paths in your pages, define the config in a common place and import it into your pages as needed. The integration doesn't provide any methods for getting the output paths, so you'll need to manage that yourself.
Generate optimized images for different platforms:
// astro.config.mjs
const socialMediaSizes = {
og: { width: 1200, height: 630 }, // OpenGraph
twitter: { width: 1200, height: 600 }, // Twitter
linkedin: { width: 1200, height: 627 }, // LinkedIn
instagram: { width: 1080, height: 1080 }, // Instagram
};
export default defineConfig({
integrations: [
snapshot({
pages: {
'/': Object.entries(socialMediaSizes).map(([platform, dims]) => ({
outputPath: `public/social/${platform}.png`,
...dims,
})),
},
}),
],
});Generate screenshots for all blog posts:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import snapshot from 'astro-snapshot';
// Get all blog post slugs (implement based on your setup)
const blogPosts = await getBlogPostSlugs();
// Create config for each blog post
const blogPages = Object.fromEntries(
blogPosts.map((slug) => [
`/blog/${slug}`,
[
{
outputPath: `public/og/blog/${slug}.png`,
width: 1200,
height: 630,
},
{
outputPath: `public/og/blog/${slug}-square.jpg`,
width: 1080,
height: 1080,
},
],
]),
);
export default defineConfig({
integrations: [
snapshot({
pages: {
'/': [{ outputPath: 'public/og/home.png' }],
'/about': [{ outputPath: 'public/og/about.png' }],
...blogPages,
},
}),
],
});Tip
Optimize build performance by conditionally generating screenshots based on environment variables or build mode.
Control when screenshots are generated:
// astro.config.mjs
const isDevelopment = process.env.NODE_ENV === 'development';
const shouldGenerateScreenshots = process.env.GENERATE_SCREENSHOTS === 'true';
export default defineConfig({
integrations: [
// Only add integration when needed
...(shouldGenerateScreenshots || !isDevelopment
? [
snapshot({
pages: {
'/': [{ outputPath: 'public/og/home.png' }],
},
}),
]
: []),
],
});Different viewports for different purposes:
// astro.config.mjs
const viewports = {
desktop: { width: 1920, height: 1080 },
tablet: { width: 768, height: 1024 },
mobile: { width: 375, height: 667 },
};
export default defineConfig({
integrations: [
snapshot({
pages: {
'/': Object.entries(viewports).map(([device, dims]) => ({
outputPath: `public/previews/${device}.png`,
...dims,
})),
},
}),
],
});Handle pages with animations or lazy-loaded content:
// astro.config.mjs
export default defineConfig({
integrations: [
snapshot({
pages: {
'/dashboard': [
{
outputPath: 'public/og/dashboard.png',
gotoOptions: {
waitUntil: 'networkidle0', // Wait for all network requests
timeout: 60000, // Increase timeout
},
},
],
},
}),
],
});| Property | Type | Required | Default | Description |
|---|---|---|---|---|
pages |
Record<string, ScreenshotConfig[]> |
β | - | Map of page paths to screenshot configs |
defaults |
Partial<ScreenshotConfig> |
β | {} |
Default config for all screenshots |
launchOptions |
PuppeteerLaunchOptions |
β | { headless: true } |
Puppeteer launch options |
port |
number |
β | 4322 |
Preview server port |
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
outputPath |
string |
β | - | Output path with format extension |
width |
number |
β | 1200 |
Viewport width in pixels |
height |
number |
β | 630 |
Viewport height in pixels |
gotoOptions |
GoToOptions |
β | { waitUntil: 'networkidle2' } |
Puppeteer goto options |
screenshotOptions |
ScreenshotOptions |
β | {} |
Puppeteer screenshot options |
The format is automatically detected from the file extension in outputPath:
.png- PNG format.jpg/.jpeg- JPEG format.webp- WebP format
- Check that pages are correctly specified in config
- Ensure Puppeteer dependencies are installed
- Verify the build completes without errors
- Check console output for screenshot generation logs
On some systems, Puppeteer may need additional configuration:
launchOptions: {
args: ['--no-sandbox', '--disable-setuid-sandbox'];
}Process pages in batches or increase Node memory:
NODE_OPTIONS="--max-old-space-size=4096" npm run buildAdd these args for containerized environments:
launchOptions: {
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
];
}Pull requests, bug reports, feature requests, and other kinds of contributions are greatly appreciated. See the contribution guide for more details.
Copyright Β© 2025 John Goodliff.
This project is licensed under the MIT License. See LICENSE for details.
We are not affiliated with or endorsed by Astro.
- π€ Gatsby Plugin: Component to Image: A similar image generation plugin for the Gatsby framework.
- π€ Tally: A free online tool to count the number of characters, words, paragraphs, and lines in your text. Tally uses this integration to generate social images.
- π astro-selfie: A similar integration that automatically generates images for every page.
Find this project useful? Sponsoring me will help me cover costs and commit more time to open-source.
If you can't donate but still want to contribute, don't worry. There are many other ways to help out, like:
- π’ reporting (submitting feature requests & bug reports)
- π¨βπ» coding (implementing features & fixing bugs)
- π writing (documenting & translating)
- π¬ spreading the word
- β starring the project
I appreciate the support!