Fonts and Images

Astro 6 introduced a Fonts API and has built-in image optimization. Both replace Next.js-specific features.

Fonts API (Astro 6)

The Fonts API downloads fonts at build time and serves them from your own domain. No client-side requests to Google Fonts or other providers.

Setup

// astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';

export default defineConfig({
  fonts: {
    providers: [fontProviders.google()],
    families: [
      {
        name: 'Inter',
        provider: 'google',
        weights: [400, 500, 600, 700],
        styles: ['normal', 'italic'],
      },
      {
        name: 'JetBrains Mono',
        provider: 'google',
        weights: [400, 700],
      },
    ],
  },
});

Usage in CSS

The Fonts API generates CSS custom properties:

body {
  font-family: var(--font-inter), sans-serif;
}

code {
  font-family: var(--font-jetbrains-mono), monospace;
}

Comparison with next/font

Featurenext/font/googleAstro Fonts API
Self-hostingYesYes
Build-time downloadYesYes
CSS variableManual setupAuto-generated --font-*
ConfigurationPer-file importCentral config
No CLSYesYes

The Astro approach is simpler: configure once in astro.config.mjs, use CSS variables everywhere.

Images

The <Image /> component

Astro optimizes images at build time via astro:assets:

---
import { Image } from 'astro:assets';
import heroImage from '../assets/hero.jpg';
---
<Image src={heroImage} alt="Hero" width={800} height={400} />

This:

  • Converts to WebP/AVIF
  • Generates proper width/height attributes (no layout shift)
  • Lazy loads by default

Local images

Import images in the frontmatter:

---
import { Image } from 'astro:assets';
import photo from '../assets/photo.jpg';
---
<Image src={photo} alt="A photo" />

Remote images

Configure allowed domains in astro.config.mjs:

export default defineConfig({
  image: {
    domains: ['cdn.example.com', 'images.unsplash.com'],
    // Or use patterns for more flexibility:
    remotePatterns: [
      { protocol: 'https', hostname: '**.example.com' },
    ],
  },
});

Then use remote URLs:

---
import { Image } from 'astro:assets';
---
<Image
  src="https://cdn.example.com/photo.jpg"
  alt="Remote photo"
  width={600}
  height={400}
/>

The <Picture /> component

For art direction with multiple formats/sizes:

---
import { Picture } from 'astro:assets';
import hero from '../assets/hero.jpg';
---
<Picture
  src={hero}
  formats={['avif', 'webp']}
  alt="Hero image"
  widths={[400, 800, 1200]}
  sizes="(max-width: 800px) 100vw, 800px"
/>

Images in content collections

Reference images from collection schemas:

// content.config.ts
const blog = defineCollection({
  type: 'content',
  schema: ({ image }) => z.object({
    title: z.string(),
    cover: image(),
  }),
});
---
title: My Post
cover: ./cover.jpg
---

The image is validated at build time and available for optimization.

Public directory images

Images in public/ are not processed:

<img src="/logo.svg" alt="Logo" />

Use this for SVGs, favicons, and assets that do not need optimization.

Comparison with next/image

Featurenext/imageAstro <Image />
Build-time optimizationNo (runtime)Yes
Runtime optimizationYes (on-demand)Only with SSR adapter
Lazy loadingDefaultDefault
FormatsWebPWebP, AVIF
Layout shift preventionYesYes
Remote imagesnext.config.js domainsastro.config.mjs domains
Import requirednext/imageastro:assets

Key difference: Astro optimizes at build time by default, which means no runtime image processing cost. Next.js optimizes on-demand, which is more flexible but adds server load.