| name | r2-image-upload |
| description | Upload images and assets to ZenML's Cloudflare R2 bucket. Use when adding new images to blog posts, content pages, marketing data files, or any content that needs an R2-hosted URL. Handles uploading, key generation, and prints paste-ready frontmatter. Triggers: "upload image", "add image to R2", "new blog image", "upload asset", "R2 upload". |
R2 Image Upload
Upload images to the ZenML R2 bucket (zenml-assets) and get back absolute URLs for use in content frontmatter or src/lib/*.ts data files.
Two-Tier Image Decision
Before uploading to R2, decide which tier the image belongs to:
| Tier | Where | When to use | Reference pattern |
|---|
| A: public/ | public/images/ | Small site-wide UI images: logos, icons, favicons, backgrounds | "/images/filename.svg" (root-relative) |
| B: R2 | zenml-assets bucket | Content images: blog heroes, screenshots, team photos, OG images | "https://assets.zenml.io/content/uploads/..." (absolute URL) |
Rule of thumb: If it appears in src/content/*.md frontmatter, it goes to R2 (schemas require z.string().url()). If it's site furniture used across many pages, it goes in public/images/.
Format note: AVIF vs JPEG for Open Graph
Default to AVIF for everything — best compression, browsers render it fine.
Exception: images referenced from seo.ogImage need a JPEG sibling alongside the AVIF. Social platforms (LinkedIn, Twitter/X, Slack, Facebook, Discord) do NOT support AVIF in Open Graph cards. Using AVIF for silently renders previews without an image.