Astro 2.5

By
Matthew Phillips
Ben Holmes
Erika
Bjorn Lu

We just released Astro 2.5, with a big list of features including:

Data collections and references

Content collections are Astro’s first-class solution to managing and authoring content. Astro 2.5 takes that story even further with new data formats and collection references.

First, we’ve introduced a new type: 'data' property to store data formats like JSON and YAML in their own collections. This unlocks using collections for new forms of content including author profiles, reusable image alt text, translation dictionaries, and more.

Create data collections alongside your existing content collections:

src/content/
blog/
week-1.md
week-2.md
authors/
grace-hopper.json
alan-turing.json

Configure them using the new type: 'data' property:

// src/content/config.ts
import { defineCollection, z } from "astro:content"
const authors = defineCollection({
type: "data",
schema: z.object({
name: z.string(),
socialLink: z.string().url(),
}),
})
const blog = defineCollection({
type: "content",
schema: z.object({
/* ... */
}),
})
export const collections = { blog: blog, authors: authors }

You may also want to “reference” related entries. A common example is a blog post that references reusable author profiles stored as JSON in a data collection, or related post URLs stored in the same collection. You can configure these relationships using the new reference() function:

import { defineCollection, reference, z } from "astro:content"
const blog = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
// Reference a single author from the `authors` collection by `id`
author: reference("authors"),
// Reference an array of related posts from the `blog` collection by `slug`
relatedPosts: z.array(reference("blog")),
}),
})
const authors = defineCollection({
type: "data",
schema: z.object({
/** ... */
}),
})
export const collections = { blog, authors }

Now, each blog post can reference related entries with type-safe validation:

# src/content/blog/welcome.md
---
title: "Welcome to my blog"
author: ben-holmes # references `src/content/authors/ben-holmes.json`
relatedPosts:
- about-me # references `src/content/blog/about-me.md`
- my-year-in-review # references `src/content/blog/my-year-in-review.md`
---

Check the updated content collections guide for in-depth examples.

Static by default hybrid rendering (experimental)

In 2.0 we added the ability to pre-render individual pages in SSR apps. Now in 2.5 you can also do the opposite; in a mostly static site you can specify some routes to not be prerendered.

This new 'hybrid' server output option flips the default pre-rendering behaviour of SSR so that you no longer have to mark each and every static routes that you want pre-rendered. This option will be marked as experimental for a short period of time as we stabilize edge cases. You can use it today by enabling the hybridOutput flag in the experimental section of your config and adding an adapter:

astro.config.mjs

import { defineConfig } from "astro/config"
import nodejs from "@astrojs/node"
export default defineConfig({
output: "hybrid",
adapter: nodejs(),
experimental: {
hybridOutput: true,
},
})

Once you’ve done that, your entire site will be pre-rendered by default. You can opt out of pre-rendering by setting the prerender export of any page or endpoint to false:

src/pages/contact.astro

---
export const prerender = false
if (Astro.request.method === "POST") {
// handle form submission
}
---
<form method="POST">
<input type="text" name="name" />
<input type="email" name="email" />
<button type="submit">Submit</button>
</form>

Custom client directives (experimental)

Custom client directives allow integration authors to define new client: directives to gain greater control over when components load.

Integrations can add these through the astro:config:setup hook’s new addClientDirective() API. To use this API, enable customClientDirectives in the experimental section of your config.

astro.config.mjs

import { defineConfig } from "astro/config"
import onClickDirective from "astro-click-directive"
export default defineConfig({
integrations: [onClickDirective()],
experimental: {
customClientDirectives: true,
},
})

astro-click-directive

export default function onClickDirective() {
return {
hooks: {
"astro:config:setup": ({ addClientDirective }) => {
addClientDirective({
name: "click",
entrypoint: "astro-click-directive/click.js",
})
},
},
}
}

Now you can use client:click on any of your framework components with full type support.

<Counter client:click />

See the client directives documentation to learn how to build these types of integrations.

HTML minification

You can now opt-in to minification of the HTML produced by your Astro components.

Using the compressHTML option, Astro will remove all whitespace from your HTML including line breaks.

We wanted to enable minification without sacrificing performance. For server-rendered apps, doing minification on each render could be costly. With compressHTML your components are compressed only once by the Astro compiler, and then during the build.

You can enable this in your config:

import { defineConfig } from "astro/config"
export default defineConfig({
compressHTML: true,
})

Note: compression occurs both in development mode and in the final build.

While this option will not compress HTML produced by framework component you can write a middleware to compress HTML responses.

Parallelized rendering

Astro will now render components in parallel so that data-loading components higher in the tree will no longer block other components that are also loading data, as in the example below:

<Delayed ms={30} />
<Delayed ms={20} />
<Delayed ms={40} />
<Delayed ms={10} />

This <Delayed /> component waits a number of milliseconds. Previously, each component would need to wait for the previous to finish before it started rendering. In 2.5 these will now all render at the same time.

Fun fact: we attempted to add this optimization in the past but were lacking good benchmarks to ensure that this didn’t actually slow down rendering. Recent improvements to our benchmarking CI infrastructure has now allowed us to implement this with confidence.

Polymorphic type helper

Astro now includes a TypeScript helper (Polymorphic) to make it easier to build components that can render as different HTML elements with full type safety. This is useful for components like <Link> that can render as either <a> or <button> depending on the props passed to it.

The example below implements a fully-typed, polymorphic component that can render as any HTML element. The HTMLTag type is used to ensure that the as prop is a valid HTML element.

---
import { HTMLTag, Polymorphic } from "astro/types"
type Props<Tag extends HTMLTag> = Polymorphic<{ as: Tag }>
const { as: Tag, ...props } = Astro.props
---
<Tag {...props} />

More

Additional bug fixes and improvements are included in this release. Check out the release notes to learn more.