Images

A landing page can carry up to nine images: a hero, up to six feature icons, a CTA background, and a dedicated OG share card. You can AI-generate them or upload your own.

Image slots

SlotAspectSizeSource
Hero16:92K (~1920×1080)AI or upload
Feature icons (×2–6)1:11K (~1024×1024)AI-generated during initial flow
CTA background16:92KAI or upload
OG share card16:92KAI-generated during initial flow

Gemini Nano Banana

We use Google's Gemini 2.5 Flash Image (codename "Nano Banana") for every AI image. The SDK's imageConfig lets us pin the aspect ratio and render size per slot, and we reinforce the same intent in the prompt text. Style-consistent across multiple images thanks to Nano Banana's strength at preserving accent color and composition.

Batch generation

During the signup / new-page wizard, once the AI text finishes, we kick off all missing images in parallel via /api/landing-pages/[id]/enrich-images. Hero, feature icons, CTA background, and OG card all run concurrently so total image generation takes roughly the slowest single call, not the sum. Partial failures don't block - whatever succeeds gets written.

WebP transcoding

Whatever the AI returns (PNG today) gets re-encoded as WebP at quality 82 before storage. User uploads pass through the same pipeline. The result is dramatically smaller files with no visible quality loss - better LCP and lower bandwidth.

Uploading your own image

Every image slot that supports AI generation also supports upload:

  1. Open the landing page editor.
  2. Find the hero or CTA section.
  3. Click ⬆ Upload.
  4. Pick a JPEG, PNG, WebP, or GIF from your computer.
  5. It gets compressed to WebP (max 1920px, ≤450 KB) before upload.
  6. Hit Save to persist and recompile.

Dedupe + limits

Every AI-generation prompt is hashed - identical prompts under the same page reuse the cached WebP object. Hard cap: 30 images per user per day across all AI paths; upload doesn't count.

Fallback

If the Gemini call errors or times out (>10 s), the slot falls back to an Unsplash image matched to the prompt keywords so your page still has a visual. Worth re-generating once later if you want a brand-matched image.

How images ship to the visitor

  • Stored in R2 at landing-images/{userId}/{pageId}/{slot}-{hash}.webp.
  • Served by /api/images/[...key] with Cache-Control: max-age=31536000, immutable.
  • Hero is preloaded via <link rel="preload"> before the HTML parse finishes - huge LCP win.
  • Below-the-fold images are loading="lazy".
  • Intrinsic width/height attrs keep cumulative layout shift at zero.