@twocaretcat/astro-snapshot
v2.1.0
Published
An Astro integration for generating screenshots of your pages automatically at build time. Perfect for creating social images, content previews, dynamic icons, and more!
Maintainers
Readme
👋 About
Generate screenshots of your Astro pages automatically at build time with Astro Snapshot. Perfect for creating social images, content previews, dynamic icons, and more!.
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.
Features
- 🚀 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
Use cases
- 🏞️ 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
How it works
[!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
importstatements).
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
📦 Installation
[!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 puppeteer
This package is available on both JSR and
npm. It's also support the astro add command to update
your astro.config.js automatically.
Automatic (w/ astro add)
[!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 run -A astro add astro-snapshotbunx astro add astro-snapshotnpx astro add astro-snapshotpnpm astro add astro-snapshotyarn astro add astro-snapshotvlt astro add astro-snapshotIf you run into any issues, try the manual installation steps below.
Manual
[!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 add jsr:@twocaretcat/astro-snapshot # JSR (recommended)deno add npm:@twocaretcat/astro-snapshot # npmbunx jsr add @twocaretcat/astro-snapshot # JSRbun add @twocaretcat/astro-snapshot # npmnpx jsr add @twocaretcat/astro-snapshot # JSRnpm install @twocaretcat/astro-snapshot # npmpnpm i jsr:@twocaretcat/astro-snapshot # JSRpnpm add @twocaretcat/astro-snapshot # npmyarn add jsr:@twocaretcat/astro-snapshot # JSRyarn add @twocaretcat/astro-snapshot # npmvlt 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()],
});🕹️ Usage
Configure the Integration
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,
},
],
},
}),
],
});🤖 Advanced Usage
Kitchen Sink
Here's an example with all available configuration options:
// 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)
overwrite: true, // Overwrite existing screenshots (default: false)
// 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,
},
},
// Puppeteer page.setViewport() options
setViewportOptions: {
deviceScaleFactor: 2, // Higher resolution screenshots
isMobile: false,
hasTouch: false,
isLandscape: true,
},
},
],
},
// Default config for all screenshots (optional)
defaults: {
width: 1200,
height: 630,
overwrite: true,
gotoOptions: {
waitUntil: 'networkidle2',
},
setViewportOptions: {
deviceScaleFactor: 2,
},
},
// Port for preview server (default: 4322)
port: 4322,
// Puppeteer launch options
launchOptions: {
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
},
}),
],
});Multiple Formats for Social Media
[!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,
})),
},
}),
],
});Dynamic Blog Post Screenshots
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,
},
}),
],
});Conditional Screenshot Generation
[!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' }],
},
}),
]
: []),
],
});Custom Viewport Configurations
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,
})),
},
}),
],
});Waiting for Dynamic Content
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
},
},
],
},
}),
],
});📚 API Reference
SnapshotIntegrationConfig
| 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 |
ScreenshotConfig
| 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 |
Supported Formats
The format is automatically detected from the file extension in outputPath:
.png- PNG format.jpg/.jpeg- JPEG format.webp- WebP format
❓ FAQ
Screenshots not generating
- 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
Permission errors
On some systems, Puppeteer may need additional configuration:
launchOptions: {
args: ['--no-sandbox', '--disable-setuid-sandbox'];
}Memory issues with many screenshots
Process pages in batches or increase Node memory:
NODE_OPTIONS="--max-old-space-size=4096" npm run buildDocker deployment
Add these args for containerized environments:
launchOptions: {
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
];
}🤝 Contributing
Pull requests, bug reports, feature requests, and other kinds of contributions are welcome. See the contribution guide for more details.
🧾 License
Copyright © 2025 John Goodliff (@twocaretcat).
This project is licensed under the MIT license. See the license for more details.
We are not affiliated with or endorsed by Astro.
🖇️ Related
Recommended
Other projects you might like:
- 👤 Gatsby Plugin: Component to Image: A similar image generation plugin for the Gatsby framework.
Used By
Notable projects that depend on this one:
- 👤 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.
Alternatives
Similar projects you might want to use instead:
- 🌐 astro-selfie: A similar integration that automatically generates images for every page.
💕 Funding
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!
