Astro 2.1

Matthew Phillips

We just released Astro 2.1, featuring:

Astro 2.1 is available on npm today. Run npm i astro@latest to upgrade an existing project or run npm create astro in the terminal to start something new.

Built-in Image Support

Astro 2.1 brings experimental support for built-in image optimization to Astro. To enable it in your project, run Astro with the --experimental-assets flag or set the experimental.assets boolean flag in your Astro config file.

If you’re already familiar with our original @astrojs/image integration, Astro’s new built-in <Image> component should feel familiar but with some notable improvements. You can now adjust the size of the image using the width and height props without changing its aspect ratio and control the level of image optimization using the new quality prop.

import { Image } from "astro:assets"
import penguin from "~/assets/penguin.png"

<Image src={penguin} quality="high" alt="a high-quality penguin" />

The resulting image will be automatically optimized for you in a modern web format, for maximum performance with minimum Cumulative Layout Shift (CLS).

Additionally the built-in image support can be swapped out for another provider, allowing you to connect this to an production image CDN like Cloudinary or a host like Vercel to leverage their image optimization services instead.

Astro 2.1 also answers one of our most requested features coming out of the original 2.0 launch: image handling inside of content collections. A new image() schema helper is now available from the Content Collections API that lets you define images in your frontmatter schema.

import { image, defineCollection, z } from "astro:content"

const blogCollection = defineCollection({
	schema: z.object({
		title: z.string(),
		cover: image(),

export const collections = {
	blog: blogCollection,

If the image that you reference in your frontmatter doesn’t exist, Astro will let you know. You can refine the schema even further to check for things like image dimensions and file extension:

cover: image().refine((img) => img.width === 800 && img.height === 600, {
	message: "Cover needs to be 800x600 pixels!",

To try it for yourself, check out the migration instructions in our new Assets Guide. We’ll continue to revise and improve support for images until the feature is stable and the flag dropped in a future major release. You can contribute your feedback by commenting on the Image RFC.

Markdoc integration

The logo for the Markdoc project

A new experimental Markdoc integration has also been published alongside Astro 2.1. You can add @astrojs/markdoc to your project today by running npx astro add markdoc in your project directory.

Markdoc is a promising new content format developed by Stripe when they needed something that could match their scale. While we still love MDX for Astro, it’s not uncommon for our users to see a noticable slowdown when processing a large number of MDX files.

As part of our own investigation, we built a performance benchmark to compare Markdoc build performance inside of Astro against our existing content formats: Markdown and MDX. These tests show exciting improvements, with up to 3x faster builds over MDX rendering static content and islands of interactivity.

Once the @astrojs/markdoc integration has been added to your project, Markdoc becomes available exclusively inside of Content Collections using the .mdoc file extension. You can mix Markdoc files alongside your existing Markdown and MDX files for a smooth, gradual migration process.

Support for MDX is not going away. We are experimenting with both formats as we feel out what the future of content looks like in Astro. We’d love your feedback as we try to find the right balance between flexible syntax and scaling to larger sites. Especially if this is your first time trying Markdoc, give us your thoughts in the ongoing Markdoc RFC.

astro check —watch

CLI output of the astro check --watch command, showing no errors and that it is waiting on file changes.

astro check is a built-in Astro CLI command that runs TypeScript type checking on your project .astro files. It is often used to catch bugs and type errors in CI before merging changes or deploying your site.

We heard from some users that they wanted this same feature for development, similar to tsc --watch in TypeScript. For some, getting errors in your editor is easy to miss, and it can be easy to forget to run astro check manually before pushing a change.

In Astro 2.1, a new --watch flag has been added to the astro check command. This will spawn a long-running process that listens for file changes and reruns diagnostics on changed files. You can run this in parallel with your dev server with by adding a script to your package.json:

	"scripts": {
		"dev": "astro check --watch & astro dev"

Inferred types for dynamic routes

The getStaticPaths() export in your Astro pages is used to define which pages should be built for a dynamic route in SSG mode. For each page you return a params and props property.

In Astro 2.1, we’ve added two new helpers to infer these types and provide type checking when using these values in your template.

Given the following getStaticPaths() function:

// Example: src/pages/blog/[slug].astro
export async function getStaticPaths() {
	const posts = await getCollection("blog")
	return => {
		return {
			params: { slug: post.slug },
			props: { draft:, title: },

You could now use the new InferGetStaticParamsType and InferGetStaticPropsType helper types to get type definitions for your params and props values without needing to define them manually:

import { InferGetStaticParamsType, InferGetStaticPropsType } from "astro"
export async function getStaticPaths() {
	/* ... */

type Params = InferGetStaticParamsType<typeof getStaticPaths>
type Props = InferGetStaticPropsType<typeof getStaticPaths>
const { slug } = Astro.params
//               ^? { slug: string; }
const { title } = Astro.props
//                ^? { draft: boolean; title: string; }

Try Astro 2.1

Thank you to everyone who contributed to this outstanding release. In addition to these new features, there were also a bunch of bug fixes throughout the core packages and integrations. See the complete release notes to learn more.