Image Optimization
How PhantomWP downloads, optimizes, and serves WordPress images with responsive sizes and blur placeholders.
Image Optimization
PhantomWP automatically downloads and optimizes WordPress images for your Astro site. Images are served as responsive WebP with blur-up placeholders for fast, smooth loading.
How It Works
PhantomWP uses a two-stage pipeline to handle WordPress images:
| Stage | When | Purpose |
|---|---|---|
| IDE Download | On-demand (you click Download) | Instant preview during development |
| Build-Time Sync | Automatic before every build | Full optimization with responsive sizes |
Stage 1: IDE Download
When you click Download in the WordPress Settings media section, PhantomWP:
- Fetches images from your WordPress media library
- Saves originals to
src/media/cms/(for Astro's Image component) - Converts to WebP in
public/media/cms/(for content images) - Updates the media URL map so local paths are used instead of remote WordPress URLs
This gives you working images in the dev preview immediately. The images are committed to your repository so that CI builds don't need to re-download them.
Stage 2: Build-Time Sync
Before every production build, the sync-media script runs automatically and:
- Fetches all published posts and pages from WordPress
- Finds every image actually used in content (featured images + images in post/page body)
- Downloads any new or changed images (skips unchanged ones)
- Generates responsive WebP variants at multiple sizes
- Generates blur placeholders for progressive loading
- Updates all mapping files
Images that were already downloaded via the IDE are skipped -- the build only processes what's new or changed.
Responsive Images
Content images (set:html content from WordPress) are automatically enhanced with responsive srcset attributes.
What Gets Generated
For each image, the build creates WebP variants at these breakpoints:
| Width | Filename | Use Case |
|---|---|---|
| 320px | image-320w.webp | Mobile phones |
| 640px | image-640w.webp | Small tablets |
| 960px | image-960w.webp | Tablets / small desktops |
| 1200px | image-1200w.webp | Large desktops |
| Full size | image.webp | High-res displays |
Only variants smaller than the original image are created. A 500px-wide image won't get a 960w variant.
How It Looks in HTML
WordPress content images are rewritten from:
<img src="https://yoursite.com/wp-content/uploads/photo-1024x768.jpg"
srcset="...wordpress sizes..."
sizes="...">To:
<img src="/media/cms/photo.webp"
srcset="/media/cms/photo-320w.webp 320w,
/media/cms/photo-640w.webp 640w,
/media/cms/photo-960w.webp 960w,
/media/cms/photo.webp 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 100vw"
width="1200" height="800"
loading="lazy" decoding="async">The browser automatically picks the best size for the user's screen.
Featured Images
Featured images use Astro's built-in <Image> component, which handles responsive sizing and format conversion at build time. The FeaturedImage component automatically resolves local images from src/media/cms/.
Blur Placeholders (LQIP)
For a smooth loading experience, featured images show a tiny blurred preview while the full image loads.
How It Works
- The build generates a 20px-wide WebP thumbnail for each image (about 200-500 bytes)
- This is stored as a base64 data URL in
image-placeholders.json - The
FeaturedImagecomponent renders it as a blurred background - When the full image loads, it fades in over the placeholder
When Placeholders Show
- Lazy-loaded images (default) -- Shows blur placeholder, then fades in
- Priority images (
priority={true}) -- No placeholder, loads eagerly for above-the-fold content
<!-- Shows blur placeholder while loading -->
<FeaturedImage src={imageUrl} alt="Post thumbnail" />
<!-- No placeholder, loads immediately -->
<FeaturedImage src={imageUrl} alt="Hero image" priority={true} />Incremental Sync
The build-time sync is smart about avoiding redundant work:
- Content hashing -- Each image's SHA-256 hash is tracked. If the content hasn't changed, the download is skipped
- Modification time checks -- Responsive variants are only regenerated when the source is newer
- Existing placeholder reuse -- Blur placeholders aren't regenerated if they already exist
This means the first build may take longer (downloading all images), but subsequent builds are fast.
File Structure
After images are downloaded and optimized:
src/
media/
cms/
photo.jpg # Original from WordPress
banner.png # Original from WordPress
lib/
media-map.json # WordPress URL -> local path mapping
responsive-map.json # Image -> responsive variant mapping
image-placeholders.json # Base64 blur placeholder data
.sync-manifest.json # Incremental sync cache (gitignored)
public/
media/
cms/
photo.webp # Full-size WebP
photo-320w.webp # 320px responsive variant
photo-640w.webp # 640px responsive variant
photo-960w.webp # 960px responsive variant
banner.webp # Full-size WebP
banner-640w.webp # Responsive variant
Running the Sync Manually
The sync runs automatically as part of npm run build. To run it manually:
node scripts/sync-media.mjsThis is useful if you want to generate responsive variants and placeholders without doing a full build.
Troubleshooting
Images not loading in dev preview
Download media from WordPress Settings. The build-time sync only runs during npm run build, not during development.
Blur placeholders not showing
- Run
node scripts/sync-media.mjsto generate placeholders - Check that
src/lib/image-placeholders.jsonexists and has entries - Placeholders only appear on non-priority images
Build is slow (first time)
The first build downloads all images from WordPress. Subsequent builds are much faster thanks to incremental sync. To speed up the first build, download images via the IDE first -- they'll be committed and the build will skip them.
Images look blurry
If content images appear low quality, check that responsive variants were generated. Run node scripts/sync-media.mjs and look for the "Images with responsive variants" count in the output.
Next Steps
- Connecting WordPress -- Set up your WordPress connection
- Media Manager -- Upload and manage custom images
- Deploying to Vercel -- Deploy your optimized site