コンテンツにスキップ
まだあたたかい

Featured images & media: heroes, cards, and a photo-essay layout

Pick the right kind of image source, control where it shows up, write good alt text, and turn off the TOC for prose-light photo essays.

Authoring 4 分で読めます
A long exposure of waves washing across a rocky coastline at dusk
A long exposure of waves washing across a rocky coastline at dusk

This post is a small photo essay that demonstrates the theme’s image features end-to-end. Notice the things this page is doing that others aren’t:

  • The hero is a local asset from src/assets/images/posts/featured-images-and-media/coastline.jpg, routed through Astro’s image pipeline (responsive srcset, WebP).
  • toc: false  Eno right-hand table of contents. Photo essays read better without one.
  • showFeaturedImage: true is set explicitly, even though that’s the site default. Authors can turn it off per post if they prefer.

You have three storage options. Hero images and card thumbnails are routed through a small SmartImage wrapper that picks the right strategy automatically:

Drop the file into src/assets/images/posts/<post-identifier>/ and reference it from the post’s frontmatter as a path relative to the markdown file:

# src/content/posts/en/coast-walk.md ↁEsrc/assets/images/posts/coast-walk/coastline.jpg
heroImage: ../../assets/images/posts/coast-walk/coastline.jpg
heroImageAlt: A long exposure of waves on a rocky coastline

The content collection schema uses Astro’s image() helper, so the file is validated, dimensions are inferred at build time, and the image flows through the optimizer (modern formats + responsive srcset). This is the option you should pick by default.

In MDX you can also import assets explicitly and feed them to <Image> for inline use:

src/content/posts/en/example.mdx
import { Image } from 'astro:assets';
import coastline from '../../assets/images/posts/featured-images-and-media/coastline.jpg';
<Image src={coastline} alt="A coastline at dusk" widths={[400, 800, 1600]} />

Drop the file into public/ and reference it by absolute path:

heroImage: /images/coastline.jpg
heroImageAlt: A long exposure of waves on a rocky coastline

Pros: zero ceremony. Cons: no automatic optimisation  EAstro deliberately does not process anything in public/. The browser receives the file exactly as it sits on disk. Use this for assets that must keep a stable URL (favicons, OG defaults, web manifest).

Remote URLs are first-class. The hero and card components run them through Astro’s image service, which fetches the source at build time, transcodes it to WebP, and emits a responsive srcset:

heroImage: ../../assets/images/posts/typography-and-markdown/light-through-window.png
heroImageAlt: Soft daylight on a wooden desk

The host must be allow-listed in image.remotePatterns inside astro.config.mjs. The defaults cover Unsplash, GitHub user content, jsDelivr, Cloudinary and Cloudflare Images; add your own CDN if you use one. Any host not on the list will fail the build with a clear error.

The hero shows up in two places:

  1. As a thumbnail on cards (home page, listings, archives, tag and category pages).
  2. As a large hero on the post page itself.

Both are controlled by:

  • SITE.showFeaturedImages in src/config.ts  Ethe site-wide default.
  • SITE.dynamicPostCardHeight in src/config.ts  E controls whether listing cards with hero thumbnails keep a fixed desktop height (false) or can expand for longer excerpts (true).
  • showFeaturedImage per-post  Eoverrides the site default.
  • heroImage per-post  Eprovides the actual image source.

If heroImage is missing, no hero is rendered regardless of the flags. If heroImage is set but showFeaturedImage: false, the post page is text-only  Ebut the card on listings can still use the hero, depending on your layout customisations.

If your listing pages feel too rigid (or too uneven), this is the knob to tune globally without editing component markup.

Inline Markdown images render edge-to-edge inside the prose column:

Coastline rocks photographed under overcast skies

A captioned figure with a <figcaption>:

Sunlight breaking through clouds over the ocean
Sunlight on the horizon. Photo by Sean O. on Unsplash.

Side-by-side images? Drop them inside an HTML <div> with Tailwind classes (this is MDX, after all):

A sandy beachA coastal cliff

The theme ships a <VideoEmbed> component for responsive, privacy-friendly video embeds. It renders a 16:9 iframe with rounded corners and an optional caption  Econsistent with how the theme styles images.

Use the shorthand platform + id props  Ethe component builds the embed URL automatically (using youtube-nocookie.com for privacy):

A YouTube embed using the shorthand platform + id props.

Works the same way:

A Vimeo embed.

For any other provider, pass the full embed URL via src:

<VideoEmbed
src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ"
title="Custom embed URL"
caption="Optional caption text"
/>

heroImageAlt is technically optional. Don’t treat it that way.

  • Decorative images (a coloured stripe at the top of a post): use empty alt  EheroImageAlt: "".
  • Content images (a screenshot, a photograph, a chart): describe what the image conveys, not the file.
  • Don’t start with “Image of…”  Escreen readers already announce it as an image.

The theme uses heroImageAlt for the post hero and for the card thumbnail on listings, so a well-written alt benefits both contexts.

  • Hero & card images are run through SmartImage and get a responsive srcset in WebP automatically  Eno extra work needed.
  • Inline images authored with raw <img> get loading="lazy" only if you set it; Markdown’s ![]() syntax does not. Prefer the <img loading="lazy"> form (or Astro’s <Image>) for below-the-fold imagery.
  • For inline images you control, use <Image> from astro:assets with an imported src/assets/images/posts/<post-identifier>/... file to get the same modern-format, responsive output the hero gets.
  • For locally-stored hero images, prefer src/assets/images/posts/<post-identifier>/ over public/ if you can  Eonly the former goes through the optimizer.

Photo essays read best without distractions. That’s why this post:

  • Hides the TOC (toc: false).
  • Uses one large hero plus a few inline figures.
  • Stays in MDX so you can drop in side-by-side grids without leaving Markdown.

Happy shooting.