Assets (Experimental)

Built-in optimized asset support is enabled in Astro behind an experimental flag. This built-in feature will eventually replace the optional @astrojs/image integration.

The new assets experience currently features:

  • A new built-in <Image /> component
  • Relative images with automatic optimization in Markdown, MDX, and Markdoc
  • Integration with content collections
  • Improved error messages and types

Enabling Assets in your Project

Section titled Enabling Assets in your Project

Enabling assets may cause some breaking changes in your project. It may also require some manual changes to take advantage of new features.

Please check every section below to avoid errors and to get the most out of using the experimental assets option!

To enable built-in asset support, add the following lines to your astro.config.mjs configuration file:

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

export default defineConfig({
  experimental: {
   assets: true
  }
});

Copied!

When you next run Astro, it will update your src/env.d.ts file to configure types for you. To do this manually, replace the astro/client reference with astro/client-image:

src/env.d.ts
/// <reference types="astro/client" />
/// <reference types="astro/client-image" />

Copied!

Move your images to src/assets/

Section titled Move your images to src/assets/

Create src/assets/, which Astro will recognize as your new assets folder.

We recommend storing all images to be optimized in the src/assets/ directory, although this location is optional.

Your images stored in src/assets/ can be used by components (.astro, .mdx, and other UI frameworks) and in Markdown files.

Previously, importing an image would return a simple string with the path of the image. With the new image features enabled, imported image assets now match the following signature:

interface ImageMetadata {
  src: string;
  width: number;
  height: number;
  format: string;
}

Copied!

You must update the src attribute of any existing <img> tags, and you may also update other attributes that are now available to you from the imported image.

src/components/MyComponent.astro
---
import rocket from '../images/rocket.svg';
import rocket from '../assets/images/rocket.svg'
---
<img src={rocket} width="250" height="250" alt="A rocketship in space." />
<img src={rocket.src} width={rocket.width} height={rocket.height} alt="A rocketship in space." />

Copied!

Update your Markdown, MDX, and Markdoc files

Section titled Update your Markdown, MDX, and Markdoc files

Relative images can now be referenced in Markdown, MDX, and Markdoc files. These will automatically be optimized and hashed by Astro’s build process.

This allows you to move your images from the public/ directory to the new, reserved src/assets/ directory or move them near your Markdown, MDX, or Markdoc files. (See the URL path of these built images in the example below.)

Your existing images in public/ and remote images are still valid but are not automatically optimized by Astro’s build process.

src/pages/posts/post-1.md
# My Markdown Page

<!-- Local image stored at src/assets/stars.png -->
![A starry night sky.](../../assets/stars.png)
<img src="/_astro/stars.hash.png" alt="A starry night sky.">

<!-- Image stored at public/images/stars.png -->
![A starry night sky.](/images/stars.png)
<img src="/images/stars.png" alt="A starry night sky.">

<!-- Remote image on another server -->
![Astro logo](https://docs.astro.build/assets/logomark-light.png)
<img src="https://docs.astro.build/assets/logomark-light.png" width="25" alt="Astro logo">

Copied!

Built-in asset support is incompatible with the @astrojs/image integration.

To avoid errors in your project, complete the following steps:

  1. Remove the @astrojs/image integration.

    You must remove the integration by uninstalling and then removing it from your astro.config.mjs file.

  2. Migrate any existing <Image /> components.

    Change all import statements from @astrojs/image/components to astro:assets to use the new built-in <Image /> component.

    Remove any component attributes that are not currently supported image asset properties.

    For example aspectRatio is no longer supported, as it is now automatically inferred from the width and height attributes.

    src/components/MyComponent.astro
    ---
    import { Image } from '@astrojs/image/components';
    import { Image } from 'astro:assets'
    import localImage from "../assets/logo.png";
    const localAlt = "The Astro Logo";
    ---
    
    <Image
      src={localImage}
      width={300}
      aspectRatio="16:9"
      alt={localAlt}
    />

    Copied!

  3. Remove any existing <Picture /> components

    Currently, the built-in assets feature does not include a <Picture /> component.

    Instead, you can use the HTML image attributes srcset and sizes or the <picture> tag for art direction or to create responsive images.

Update content collections schemas

Section titled Update content collections schemas

You can now declare images in your frontmatter as their paths relative to the current folder.

src/content/blog/my-post.md
---
title: "My first blog post"
cover: "./firstpostcover.jpeg" # will resolve to "src/content/blog/firstblogcover.jpeg"
coverAlt: "A photograph of a sunset behind a mountain range"
---

This is a blog post

Copied!

A new image helper for content collections lets you validate the image metadata using Zod.

src/content/config.ts
import { defineCollection, z } from "astro:content";

const blogCollection = defineCollection({
  schema: ({ image }) => z.object({
    title: z.string(),
    cover: image().refine((img) => img.width >= 1080, {
      message: "Cover image must be at least 1080 pixels wide!",
    }),
    coverAlt: z.string(),
  }),
});

export const collections = {
  blog: blogCollection,
};

Copied!

The image will be imported and transformed into metadata that matches the signature of imported images, allowing you to pass it as a src to <Image/> or getImage(). A blog index page might render the cover photo and title of each blog post:

src/pages/blog.astro
---
import { Image } from "astro:assets";
import { getCollection } from "astro:content";
const allBlogPosts = await getCollection("blog");
---

{
  allBlogPosts.map((post) => (
    <div>
      <Image src={post.data.cover} alt={post.data.coverAlt} />
      <h2>
        <a href={"/blog/" + post.slug}>{post.data.title}</a>
      </h2>
    </div>
  ))
}

Copied!

Enabling asset support allows you to access the astro:assets module. This module exposes the following features:

  • <Image /> (available in .astro and .mdx files)
  • getImage() (available in .astro, .mdx, .ts, .js on the server)

The <Image /> component generates an <img> tag.

This component can transform an image’s dimensions, file type, and quality to produce an optimized image. The resulting <img> tag will include the correct width and height attributes to avoid Cumulative Layout Shift (CLS).

Import images from a relative file path or import alias in any component file and then use the import as the <Image /> component’s src attribute.

---
import { Image } from 'astro:assets';
import myImage from "../assets/my_image.png"; // Image is 1600x900
---

<!-- `alt` is mandatory on the Image component -->
<Image src={myImage} alt="A description of my image." />

Copied!

<!-- Output -->
<!-- Image is optimized, proper attributes are enforced -->
<img
  src="/_astro/my_image.hash.webp"
  width="1600"
  height="900"
  decoding="async"
  loading="lazy"
  alt="A description of my image."
/>

Copied!

These properties define the dimensions to use for the image.

When using local images in their original aspect ratio, the width and height can be automatically inferred from the source file and are optional. However, both of these properties are required for remote images.

You can optionally state the image file type to be used. The default file format used is webp.

quality is an optional property that can either be:

  • a preset (low, mid, high, max) that is automatically normalized between formats.
  • a number from 0 to 100 (interpreted differently between formats).

Use the alt attribute to provide descriptive alt text for images.

This attribute is required for the <Image /> component. This component will throw an error if no alt text is provided.

If the image is merely decorative (i.e. doesn’t contribute to the understanding of the page), set alt="" so that screen readers know to ignore the image.

In addition to the properties above, the <Image /> component accepts all properties accepted by the HTML <img> tag.

For example, you can provide a class to the final img element.

---
import { Image } from 'astro:assets';
import myImage from "../assets/my_image.png";
---

<!-- `alt` is mandatory on the Image component -->
<Image src={myImage} alt="" class="my-class" />

Copied!

<!-- Output -->
<img
  src="/_astro/my_image.hash.webp"
  width="1600"
  height="900"
  decoding="async"
  loading="lazy"
  class="my-class"
  alt=""
/>

Copied!

This function is intended for generating images destined to be used somewhere else than directly in HTML, for example in an API Route. It also allows you to create your own custom <Image /> component.

getImage() takes an options object with the same properties as the Image component (except alt).

---
import { getImage } from "astro:assets";
import myBackground from "../background.png"

const optimizedBackground = await getImage({src: myBackground, format: 'avif'})
---

<div style={`background-image: url(${optimizedBackground.src});`}></div>

Copied!

It returns an object with the following properties:

{
  options: {...} // Original parameters passed,
  src: "https//..." // Path to the generated image,
  attributes: {...} // Additional HTML attributes needed to render the image (width, height, style, etc..)
}

Copied!

By default, Astro uses Squoosh to transform your images. This supports all JavaScript environments.

If you’re deploying to a Node environment, you may want to use sharp, which offers faster performance but requires Node. To use sharp, first install it:

Terminal window
npm install sharp

Copied!

Then, enable Astro’s sharp image service in your config:

astro.config.mjs
import { defineConfig, sharpImageService } from "astro/config";

export default defineConfig({
  experimental: {
    assets: true,
  },
  image: {
    service: sharpImageService(),
  },
});

Copied!