Images and Fonts

By Syncfusion®, Inc. · Last updated

Images in public/ and src/, the Image and Picture components, local and remote images, authorized domains, image sizes, responsive layouts, fit and position, images in Markdown and collections, getImage(), SVG components, image services and the Fonts API.

Images in Astro

  • Images are often the heaviest part of a page. A large photo can be bigger than all the HTML, CSS and JavaScript together.
  • Astro has built-in tools that make images smaller and load them at the right time.
  • Image optimisation = making an image file smaller (resize, compress, modern format) without visible loss.
  • In this lesson, you will add optimised images to Asha's site, and load a custom font.

Simple lesson: use Astro's image tools, and your images get smaller and safer to load.

A Real-World Example

  • Asha adds a 4000 × 3000 photo from her phone as a post cover. The file is 4 MB.
  • On the page, the photo is only shown 800 pixels wide.
  • With a plain <img>, every reader downloads all 4 MB.
  • With Astro's <Image />, the build makes an 800-pixel-wide version in a modern format, often under 100 KB.

Press ▶ Run. Then open HTML sent to browser and look at the <img> tag.

What happened:

  • import cover from "../assets/cover.svg" gave Astro the image's file and its size.
  • width={300} was given. Astro worked out height="200", to keep the shape.
  • Astro added loading="lazy" and decoding="async" for you.

What You Will Learn

In this lesson, you will learn:

  1. Where to keep images: public/ or src/.
  2. How <Image /> and <Picture /> work.
  3. How to show remote images, and allow their domains.
  4. How image sizes and responsive layouts work.
  5. How to use images in Markdown and collections.
  6. What getImage(), SVG components and image services are.
  7. How to load fonts with the Fonts API.

Images in public/ vs src/

src/ Images

  • Keep most images in src/, such as src/assets/.
  • Import them: import cover from "../assets/cover.jpg";.
  • Astro knows their size and optimises them at build time.
  • The file name gets a hash, such as cover.B2x9a.webp, so browsers can cache it for a long time.

public/ Images

  • Images in public/ are copied as they are. They are not optimised.
  • Use them with a plain path: src="/photo.jpg".
  • Use public/ only when a file must keep its exact name, such as favicon.svg or a social image.

Default to src/assets/ for images. Only move an image to public/ when something outside your code needs its exact address.

Simple lesson: images in src/ are optimised. Images in public/ are copied as they are.

The Image and Picture Components

The Image Component

  • <Image /> comes from astro:assets.
  • It needs src and alt.
  • alt = a short text that describes the image, for screen readers and search engines. Use alt="" for a purely decorative image.
  • Astro stops the build if alt is missing. This protects your readers.

Image Dimensions

  • width and height set the size of the image Astro makes.
  • For a local image, give one of them and Astro works out the other. Give none, and Astro uses the file's own size.
  • The browser uses both values to keep space for the image before it loads. The page does not jump.

The Picture Component

  • <Picture /> makes a <picture> element with several formats.
  • formats={["avif", "webp"]} lists the formats to offer, best first.
  • Each browser picks the first format it supports. Older browsers fall back to the <img> inside.

Animation: a 4 MB photo goes through astro build and comes out as a 90 KB WebP and a 60 KB AVIF.

photo.jpg 4000 × 3000 · 4 MB

astro build resize + compress

photo.webp 800 × 600 · 90 KB

photo.avif 800 × 600 · 60 KB

Example sizes. Real savings depend on the photo.

The build makes small, modern copies of each image.

Press ▶ Run. Compare the <img> from <Image /> with the <picture> from <Picture />.

On your computer, Astro uses the Sharp library to turn photos into small WebP and AVIF files. This lab runs in the browser, so it keeps the original file and only writes the same HTML attributes. SVG files are already small, so the labs use them.

Simple lesson: <Image /> for one optimised image, <Picture /> to offer several formats.

Local and Remote Images

Remote Images

  • Remote image = an image from another website, used with a full URL.
  • Astro cannot read its size from your project, so give width and height.
  • Or add inferSize and Astro downloads the image once to find its size.

Authorizing Remote Image Domains

  • By default, Astro does not optimise images from other websites. It shows them as they are.
  • To allow it, list the trusted domains in the config:
export default defineConfig({
  image: {
    domains: ["images.unsplash.com"],
  },
});
  • image.remotePatterns allows wider rules, such as every sub-domain of a site.
  • This protects your build from being used to process images from anywhere.

Simple lesson: remote images need a size, and their domain must be allowed before Astro optimises them.

Responsive Images

The layout Property

  • Responsive image = an image that adapts to the screen size, so phones download small files and big screens get sharp ones.
  • Add layout to <Image /> or <Picture />:

    • "constrained" = shrinks on small screens, never grows past its width. Good for most images.
    • "full-width" = always fills the width of its container. Good for banners.
    • "fixed" = keeps the exact size. Good for avatars and icons.
    • "none" = no responsive behaviour.
  • Astro then adds srcset and sizes. The browser picks the best file for the screen.
  • Set a default for the whole site with image: { layout: "constrained" } in the config.

fit and position

  • fit = how the image fills its box: "cover" (crop to fill), "contain" (show it all) and others, like CSS object-fit.
  • position = which part to keep when it is cropped: "top", "center", "left bottom" and so on.

Responsive Image Styles

  • With a layout, Astro adds a small set of styles that make the image behave correctly.
  • image.responsiveStyles in the config turns them on or off.

Press ▶ Run. Make the preview narrower by dragging the bar. The constrained image shrinks. The fixed one does not.

Simple lesson: pick a layout, and Astro makes the image fit every screen.

Images in Content

Images in Markdown and MDX

  • In Markdown, write ![Alt text](./photo.jpg).
  • A path starting with ./ or ../ is a local image. Astro optimises it like an <Image />.
  • A path starting with / is from public/ and is not optimised.

The image() Helper in Collection Schemas

  • A post's cover image can be a frontmatter field: cover: ./cover.jpg.
  • In the schema, use the image() helper. The schema becomes a function that receives it:
schema: ({ image }) =>
  z.object({
    title: z.string(),
    cover: image(),
  }),
  • image() checks that the file exists and turns the path into image data you can pass to <Image />.
  • The path is relative to the Markdown file.

[TS] ImageMetadata

  • The type of an imported image is ImageMetadata: src, width, height and format.
  • Use it for a component prop: interface Props { cover: ImageMetadata }.
  • [JS] You do not need the type. The object is the same.

Simple lesson: use image() in a schema for cover images. Pass the result straight to <Image />.

Advanced Image Handling

getImage()

  • getImage() makes an optimised image without an <img> tag.
  • Use it when you need just the URL, such as for a CSS background or an Open Graph image.
  • const og = await getImage({ src: cover, width: 1200 }); then use og.src.

Importing SVG Files as Components

  • An imported .svg file can also be used as a component: <Logo width={48} />.
  • Astro puts the SVG code straight into the HTML. No extra file is downloaded.
  • Props you pass, such as width, height and class, are added to the <svg> tag.

Image Services

  • Image service = the tool that does the optimising.
  • The default is Sharp, which runs on your computer at build time.
  • passthroughImageService() turns optimisation off. Use it on hosts where Sharp cannot run.

Press ▶ Run. The logo is drawn inline. The banner URL comes from getImage().

Simple lesson: getImage() gives you just the optimised URL. SVG imports can be inline components.

Fonts

The Fonts API Overview

  • Web font = a font file the browser downloads, so your text looks the same on every device.
  • Astro's Fonts API downloads font files at build time and serves them from your own site.
  • It is faster and more private than loading fonts from another website on every visit.

Configuring Fonts

  • List fonts in astro.config.mjs under fonts:
import { defineConfig, fontProviders } from "astro/config";

export default defineConfig({
  fonts: [
    {
      provider: fontProviders.google(),
      name: "Poppins",
      cssVariable: "--font-poppins",
      weights: [400, 700],
      fallbacks: ["sans-serif"],
    },
  ],
});

Font Providers

  • Font provider = where the font files come from.
  • Built-in providers include Google Fonts, Fontsource, Bunny and Fontshare.
  • fontProviders.local() uses font files you keep in your project.

Using Fonts in Layouts

  1. Add <Font cssVariable="--font-poppins" /> from astro:assets inside your layout's <head>.
  2. Use the variable in CSS: font-family: var(--font-poppins);.

Font Fallbacks and Preloading

  • fallbacks = fonts to show while the web font loads, or if it fails.
  • Astro adjusts the fallback's size to match the web font, so text does not jump when it loads.
  • <Font cssVariable="--font-poppins" preload /> adds preload links: the browser starts downloading the font early.
  • Preload only the fonts used at the top of the page. Each preload competes with other downloads.

Press ▶ Run. The <Font /> component defines the CSS variable. The body uses it.

In this lab, no font file is downloaded. <Font /> only sets the variable, so the text shows the fallback (Georgia). On your computer, the real Poppins files are downloaded at build time and served from your site.

Simple lesson: list fonts in the config, add <Font /> to the layout, and use the CSS variable.

TL;DR

  1. src/ images = optimised. public/ images = copied as they are.
  2. <Image /> = needs src and alt. Adds width, height, lazy loading.
  3. <Picture /> = several formats with formats={["avif", "webp"]}.
  4. Remote images = need width and height (or inferSize). Allow domains with image.domains.
  5. layout = constrained, full-width, fixed or none. fit and position control cropping.
  6. image() = image fields in collection schemas, relative to the Markdown file.
  7. getImage() = an optimised URL without a tag.
  8. SVG import = can be a component: <Logo width={48} />.
  9. Fonts API = fonts in the config, <Font cssVariable="..." /> in the head.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
No altalt is optionalAlways add alt, or alt="" for decorationThe build stops
Remote image with no sizeAstro measures every imageGive width and height, or inferSizeThe build stops
src="../assets/x.jpg" as a stringStrings work like importsImport the image, then pass the importThe image is not found or not optimised
Big photos in public/Every image is optimisedKeep photos in src/Readers download huge files
Forgetting image.domainsRemote images are always optimisedList the domain in the configThe image is shown at full size
Preloading every font weightMore preloads load fasterPreload only what is used firstSlower first paint

Check Yourself

Exercises

What to Remember

  • Keep images in src/ and import them. public/ images are not optimised.
  • <Image /> needs alt. <Picture /> offers several formats.
  • Remote images need a size and an allowed domain.
  • layout makes images responsive. image() adds images to collections.
  • The Fonts API serves fonts from your own site, with smart fallbacks.

Next: External Data and Environment Variables, where Asha's site reads data from an API.