diff --git a/astro.sidebar.ts b/astro.sidebar.ts
index ed4990f336ba0..f7f5230bc2953 100644
--- a/astro.sidebar.ts
+++ b/astro.sidebar.ts
@@ -90,6 +90,7 @@ export const sidebar = [
group('guides.upgrade.major', {
collapsed: true,
items: [
+ 'guides/upgrade-to/v6',
'guides/upgrade-to/v5',
'guides/upgrade-to/v4',
'guides/upgrade-to/v3',
@@ -119,12 +120,14 @@ export const sidebar = [
items: [
'reference/api-reference',
'reference/modules/astro-actions',
+ 'reference/modules/astro-app',
'reference/modules/astro-assets',
'reference/modules/astro-config',
'reference/modules/astro-content',
'reference/modules/astro-env',
'reference/modules/astro-i18n',
'reference/modules/astro-middleware',
+ 'reference/modules/astro-static-paths',
'reference/modules/astro-transitions',
'reference/modules/astro-zod',
],
@@ -136,6 +139,8 @@ export const sidebar = [
'reference/content-loader-reference',
'reference/image-service-reference',
'reference/dev-toolbar-app-reference',
+ 'reference/session-driver-reference',
+ 'reference/font-provider-reference',
'reference/container-reference',
'reference/programmatic-reference',
],
@@ -143,17 +148,13 @@ export const sidebar = [
group('reference.experimental', {
items: [
'reference/experimental-flags',
- 'reference/experimental-flags/csp',
- 'reference/experimental-flags/fonts',
- 'reference/experimental-flags/live-content-collections',
+ 'reference/experimental-flags/route-caching',
'reference/experimental-flags/client-prerender',
'reference/experimental-flags/content-intellisense',
- 'reference/experimental-flags/preserve-scripts-order',
- 'reference/experimental-flags/heading-id-compat',
- 'reference/experimental-flags/static-import-meta-env',
'reference/experimental-flags/chrome-devtools-workspace',
- 'reference/experimental-flags/fail-on-prerender-conflict',
'reference/experimental-flags/svg-optimization',
+ 'reference/experimental-flags/queued-rendering',
+ 'reference/experimental-flags/rust-compiler',
],
}),
'reference/legacy-flags',
diff --git a/public/_headers b/public/_headers
index b505fec8b57ad..9859e1d65fa4c 100644
--- a/public/_headers
+++ b/public/_headers
@@ -5,3 +5,7 @@
cache-control: max-age=31536000
cache-control: immutable
cache-control: public
+
+# TODO: Remove this before merging to main
+/*
+ x-robots-tag: no-index
diff --git a/src/content/docs/de/basics/project-structure.mdx b/src/content/docs/de/basics/project-structure.mdx
index 5027ff897bb6e..83a6698c66032 100644
--- a/src/content/docs/de/basics/project-structure.mdx
+++ b/src/content/docs/de/basics/project-structure.mdx
@@ -125,7 +125,7 @@ Eine Anleitung zur Erstellung einer neuen `package.json`-Datei für dein Projekt
Diese Datei wird von jeder Starter-Vorlage generiert und enthält Konfigurationsoptionen für dein Astro-Projekt. Hier kannst du die zu verwendenden Integrationen, Build-Optionen, Serveroptionen und mehr angeben.
-Astro unterstützt mehrere Dateiformate für seine JavaScript-Konfigurationsdatei: `astro.config.js`, `astro.config.mjs`, `astro.config.cjs` und `astro.config.ts`. Wir empfehlen, in den meisten Fällen `.mjs` oder `.ts` zu verwenden, wenn du TypeScript in deiner Konfigurationsdatei schreiben willst.
+Astro unterstützt mehrere Dateiformate für seine JavaScript-Konfigurationsdatei: `astro.config.js`, `astro.config.mjs` und `astro.config.ts`. Wir empfehlen, in den meisten Fällen `.mjs` oder `.ts` zu verwenden, wenn du TypeScript in deiner Konfigurationsdatei schreiben willst.
Das Laden von TypeScript-Konfigurationsdateien wird mit [`tsm`](https://github.com/lukeed/tsm) gehandhabt und berücksichtigt die `tsconfig`-Optionen deines Projekts.
diff --git a/src/content/docs/de/tutorial/6-islands/4.mdx b/src/content/docs/de/tutorial/6-islands/4.mdx
index 544c050e7d5bf..7c60d35154716 100644
--- a/src/content/docs/de/tutorial/6-islands/4.mdx
+++ b/src/content/docs/de/tutorial/6-islands/4.mdx
@@ -120,7 +120,6 @@ Aktualisiere Astro und alle Integrationen auf die neuesten Versionen, indem du i
```ts title="src/content.config.ts"
import { glob } from "astro/loaders";
import { defineCollection } from "astro:content";
- // Importiere Zod
import { z } from "astro/zod";
const blog = defineCollection({
@@ -150,7 +149,7 @@ Aktualisiere Astro und alle Integrationen auf die neuesten Versionen, indem du i
1. Erstelle eine neue Seitendatei `src/pages/posts/[...slug].astro`.
Markdown- und MDX-Dateien werden innerhalb einer Sammlung nicht mehr automatisch zu Seiten. Du musst daher eine Seite erstellen, die für die Ausgabe jedes einzelnen Blogbeitrags verantwortlich ist.
-2. Füge den folgenden Code ein, um deine Sammlung [abzufragen](/de/guides/content-collections/#querying-collections) und für jede generierte Seite die `slug` und den Seiteninhalt bereitzustellen:
+2. Füge den folgenden Code ein, um deine Sammlung [abzufragen](/de/guides/content-collections/#querying-build-time-collections) und für jede generierte Seite die `slug` und den Seiteninhalt bereitzustellen:
```astro title="src/pages/posts/[...slug].astro"
---
diff --git a/src/content/docs/en/astro-courses.mdx b/src/content/docs/en/astro-courses.mdx
index 8f48e35c5c69a..d2a698de4a613 100644
--- a/src/content/docs/en/astro-courses.mdx
+++ b/src/content/docs/en/astro-courses.mdx
@@ -48,10 +48,7 @@ Learn from your fellow astronauts with curated collections of guides, articles,
-
-
-
diff --git a/src/content/docs/en/basics/project-structure.mdx b/src/content/docs/en/basics/project-structure.mdx
index 8b2a0a00bcc4b..0963238cce2cd 100644
--- a/src/content/docs/en/basics/project-structure.mdx
+++ b/src/content/docs/en/basics/project-structure.mdx
@@ -124,7 +124,7 @@ For help creating a new `package.json` file for your project, check out the [man
This file is generated in every starter template and includes configuration options for your Astro project. Here you can specify integrations to use, build options, server options, and more.
-Astro supports several file formats for its JavaScript configuration file: `astro.config.js`, `astro.config.mjs`, `astro.config.cjs` and `astro.config.ts`. We recommend using `.mjs` in most cases or `.ts` if you want to write TypeScript in your config file.
+Astro supports several file formats for its JavaScript configuration file: `astro.config.js`, `astro.config.mjs` and `astro.config.ts`. We recommend using `.mjs` in most cases or `.ts` if you want to write TypeScript in your config file.
TypeScript config file loading is handled using [`tsm`](https://github.com/lukeed/tsm) and will respect your project's `tsconfig` options.
diff --git a/src/content/docs/en/getting-started.mdx b/src/content/docs/en/getting-started.mdx
index ec9c01835d6d2..9b70bb453fd03 100644
--- a/src/content/docs/en/getting-started.mdx
+++ b/src/content/docs/en/getting-started.mdx
@@ -5,6 +5,9 @@ i18nReady: true
tableOfContents: false
editUrl: false
next: false
+banner:
+ content: |
+ Astro v6 is here! Learn how to upgrade your site
hero:
title: Astro Docs
tagline: Guides, resources, and API references to help you build with Astro.
diff --git a/src/content/docs/en/guides/actions.mdx b/src/content/docs/en/guides/actions.mdx
index cfe4e8622fdab..1b930e30e2cf1 100644
--- a/src/content/docs/en/guides/actions.mdx
+++ b/src/content/docs/en/guides/actions.mdx
@@ -329,14 +329,14 @@ export const server = {
// Matches when the `type` field has the value `create`
type: z.literal('create'),
name: z.string(),
- email: z.string().email(),
+ email: z.email(),
}),
z.object({
// Matches when the `type` field has the value `update`
type: z.literal('update'),
id: z.number(),
name: z.string(),
- email: z.string().email(),
+ email: z.email(),
}),
]),
async handler(input) {
@@ -374,7 +374,7 @@ The following example shows a validated newsletter registration form that accept
```
-2. Define a `newsletter` action to handle the submitted form. Validate the `email` field using the `z.string().email()` validator, and the `terms` checkbox using `z.boolean()`:
+2. Define a `newsletter` action to handle the submitted form. Validate the `email` field using the `z.email()` validator, and the `terms` checkbox using `z.boolean()`:
```ts title="src/actions/index.ts" ins={5-12}
import { defineAction } from 'astro:actions';
@@ -384,7 +384,7 @@ The following example shows a validated newsletter registration form that accept
newsletter: defineAction({
accept: 'form',
input: z.object({
- email: z.string().email(),
+ email: z.email(),
terms: z.boolean(),
}),
handler: async ({ email, terms }) => { /* ... */ },
diff --git a/src/content/docs/en/guides/cms/builderio.mdx b/src/content/docs/en/guides/cms/builderio.mdx
index f105c872d9275..fe1c8fe317e2f 100644
--- a/src/content/docs/en/guides/cms/builderio.mdx
+++ b/src/content/docs/en/guides/cms/builderio.mdx
@@ -174,7 +174,7 @@ Things can sometimes go wrong when setting up the preview. If something's not ri
* Make sure the site is live - for example, your dev server is running.
* Make sure that the URLs match exactly - the one in your Astro project and the one set in the Builder app.
* Make sure it's the full URL including the protocol, for example `https://`.
-* If you're working in a virtual environment like [IDX](https://idx.dev), [StackBlitz](https://stackblitz.com/), or [Gitpod](https://www.gitpod.io/), you might have to copy and paste the URL again when you restart your workspace, since this usually generates a new URL for your project.
+* If you're working in a virtual environment like [Firebase Studio](https://studio.firebase.google.com/) or [StackBlitz](https://stackblitz.com/), you might have to copy and paste the URL again when you restart your workspace, since this usually generates a new URL for your project.
For more ideas, read [Builder's troubleshooting guide](https://www.builder.io/c/docs/guides/preview-url-working).
:::
diff --git a/src/content/docs/en/guides/cms/cloudcannon.mdx b/src/content/docs/en/guides/cms/cloudcannon.mdx
index 26bb1ba162f69..4ee2a2ec75187 100644
--- a/src/content/docs/en/guides/cms/cloudcannon.mdx
+++ b/src/content/docs/en/guides/cms/cloudcannon.mdx
@@ -144,7 +144,7 @@ The following example will create a new blog post from the CloudCannon Site Dash
## Rendering CloudCannon content
-Use Astro's Content Collections API to [query and display your posts and collections](/en/guides/content-collections/#querying-collections), just as you would in any Astro project.
+Use Astro's Content Collections API to [query and display your posts and collections](/en/guides/content-collections/#querying-build-time-collections), just as you would in any Astro project.
### Displaying a collection list
@@ -159,7 +159,7 @@ const posts = await getCollection('blog');
diff --git a/src/content/docs/en/guides/cms/frontmatter-cms.mdx b/src/content/docs/en/guides/cms/frontmatter-cms.mdx
index 825baa7eb22a6..af3a5c32bafc8 100644
--- a/src/content/docs/en/guides/cms/frontmatter-cms.mdx
+++ b/src/content/docs/en/guides/cms/frontmatter-cms.mdx
@@ -9,7 +9,7 @@ i18nReady: true
---
import { FileTree } from '@astrojs/starlight/components';
-[Front Matter CMS](https://frontmatter.codes/) brings the CMS to your editor, it runs within Visual Studio Code, Gitpod, and many more.
+[Front Matter CMS](https://frontmatter.codes/) brings the CMS to your editor, allowing you to create and preview content in real-time in Visual Studio Code.
## Integration with Astro
diff --git a/src/content/docs/en/guides/cms/keystatic.mdx b/src/content/docs/en/guides/cms/keystatic.mdx
index d8fdc1e12276a..c8fe130303978 100644
--- a/src/content/docs/en/guides/cms/keystatic.mdx
+++ b/src/content/docs/en/guides/cms/keystatic.mdx
@@ -183,7 +183,7 @@ Visit `http://127.0.0.1:4321/keystatic` in the browser to see the Keystatic Admi
## Rendering Keystatic content
-Use Astro's Content Collections API to [query and display your posts and collections](/en/guides/content-collections/#querying-collections), just as you would in any Astro project.
+[Query and display your posts and collections](/en/guides/content-collections/#querying-build-time-collections), just as you would in any Astro project.
### Displaying a collection list
@@ -198,7 +198,7 @@ const posts = await getCollection('posts')
diff --git a/src/content/docs/en/guides/configuring-astro.mdx b/src/content/docs/en/guides/configuring-astro.mdx
index ef4abace880a8..0e2f3b91d18f8 100644
--- a/src/content/docs/en/guides/configuring-astro.mdx
+++ b/src/content/docs/en/guides/configuring-astro.mdx
@@ -26,7 +26,7 @@ export default defineConfig({
It is only required if you have something to configure, but most projects will use this file. The `defineConfig()` helper provides automatic IntelliSense in your IDE and is where you will add all your configuration options to tell Astro how to build and render your project to HTML.
-We recommend using the default file format `.mjs` in most cases, or `.ts` if you want to write TypeScript in your config file. However, `astro.config.js` and `astro.config.cjs` are also supported.
+We recommend using the default file format `.mjs` in most cases, or `.ts` if you want to write TypeScript in your config file. However, `astro.config.js` is also supported.
Read Astro's [configuration reference](/en/reference/configuration-reference/) for a full overview of all supported configuration options.
diff --git a/src/content/docs/en/guides/content-collections.mdx b/src/content/docs/en/guides/content-collections.mdx
index 2d13a4875668e..f93e02a017aab 100644
--- a/src/content/docs/en/guides/content-collections.mdx
+++ b/src/content/docs/en/guides/content-collections.mdx
@@ -3,6 +3,9 @@ title: Content collections
description: >-
Manage your content with type safety.
i18nReady: true
+tableOfContents:
+ minHeadingLevel: 2
+ maxHeadingLevel: 3
---
import { FileTree, CardGrid, LinkCard, Steps } from '@astrojs/starlight/components';
import Since from '~/components/Since.astro'
@@ -12,18 +15,13 @@ import ReadMore from "~/components/ReadMore.astro"
-**Content collections** are the best way to manage sets of content in any Astro project. Collections help to organize and query your documents, enable Intellisense and type checking in your editor, and provide automatic TypeScript type-safety for all of your content.
-Astro v5.0 introduced the Content Layer API for defining and querying content collections. This performant, scalable API provides built-in content loaders for your local collections. For remote content, you can use third-party and community-built loaders or create your own custom loader and pull in your data from any source.
+**Content collections** are the best way to manage sets of content in any Astro project: blog posts, product descriptions, character profiles, recipes, or any structured content. Collections help to organize and query your documents, enable Intellisense and type checking in your editor, and provide automatic TypeScript type-safety for all of your content.
-:::note
-Projects may continue using the legacy Content Collections API introduced in Astro v2.0. However, we encourage you to [update any existing collections](/en/guides/upgrade-to/v5/#legacy-v20-content-collections-api) when you are able.
-:::
+Astro provides performant, scalable APIs to load, query, and render content from anywhere: stored locally in your project, hosted remotely, or fetched live from frequently-updating sources.
## What are Content Collections?
-You can define a **collection** from a set of data that is structurally similar. This can be a directory of blog posts, a JSON file of product items, or any data that represents multiple items of the same shape.
-
-Collections stored locally in your project or on your filesystem can have entries of Markdown, MDX, Markdoc, YAML, TOML, or JSON files:
+A content collection is a set of related, structurally identical data. This data can be stored in one or several files locally (e.g. a folder of individual Markdown files of blog posts, a single JSON file of product descriptions) or fetched from remote sources such as a database, CMS, or API endpoint. Each member of the collection is called an entry.
- src/
@@ -35,32 +33,103 @@ Collections stored locally in your project or on your filesystem can have entrie
- authors.json a single file containing all collection entries
-With an appropriate collection loader, you can fetch remote data from any external source, such as a CMS, database, or headless payment system.
+Collections are defined by the location and shape of its entries and provide a convenient way to query and render your content and associated metadata. You can create a collection any time you have a group of related data or content, stored in the same location, that shares a common structure.
+
+[Two types of content collections](#types-of-collections) are available to allow you to work with data fetched either at build time or at request time. Both build-time collections and live updating collections use:
+
+- A required `loader` to retrieve your content and metadata from wherever it is stored and make it available to your project through content-focused APIs.
+- An optional collection `schema` that allows you to define the expected shape of each entry for type safety, autocomplete, and validation in your editor.
+
+Collections stored locally in your project or on your filesystem can use one of Astro's [provided build-time loaders](#build-time-collection-loaders) to fetch data from Markdown, MDX, Markdoc, YAML, TOML, or JSON files. Point Astro to the location of your content, define your data shape, and you're good to go with a blog or similarly content-heavy, mostly static site in no time!
+
+With [community-built loaders](https://astro.build/integrations/?search=&categories%5B%5D=loaders) or by building a [custom build-time collection loader](#custom-build-time-loaders) or [live loader](#creating-a-live-loader) yourself, you can fetch remote data from any external source, such as a CMS, database, or headless payment system, either at build time or live on demand.
+
+### Types of collections
+
+[Build-time content collections](#defining-build-time-content-collections) are updated at build time, and data is saved to a storage layer. This provides excellent performance for most content, but may not be suitable for frequently updating data sources requiring up-to-the-moment data freshness, such as live stock prices.
+
+For the best performance and scalability, use build-time content collections when one or more of these is true:
+
+- **Performance is critical** and you want to prerender data at build time.
+- **Your data is relatively static** (e.g., blog posts, documentation, product descriptions).
+- **You want to benefit from build-time optimization** and caching.
+- **You need to process MDX** or **perform image optimization**.
+- **Your data can be fetched once and reused** across multiple builds.
+
+:::tip[Quick start]
+See [the official Astro blog starter template](https://github.com/withastro/astro/tree/latest/examples/blog) to get up and running quickly with an example of using the [built-in `glob()` loader](#the-glob-loader) and [defining a schema](#defining-the-collection-schema) for a collection of local Markdown or MDX blog posts.
+:::
+
+[Live content collections](#live-content-collections) fetch their data at runtime rather than build time. This allows you to access frequently updated data from CMSs, APIs, databases, or other sources using a unified API, without needing to rebuild your site when the data changes. However, this can come at a performance cost since data is fetched at each request and returned directly with no data store persistence.
+
+Live content collections are designed for data that changes frequently and needs to be up-to-date when a page is requested. Consider using them when one or more of these is true:
+
+- **You need real-time information** (e.g. user-specific data, current stock levels).
+- **You want to avoid constant rebuilds** for content that changes often.
+- **Your data updates frequently** (e.g. up-to-the-minute product inventory, prices, availability).
+- **You need to pass dynamic filters** to your data source based on user input or request parameters.
+- **You're building preview functionality** for a CMS where editors need to see draft content immediately.
+
+Both kinds of collections can exist in the same project, so you can always choose the best type of collection for each individual data source. For example, a build-time collection can manage product descriptions, while a live collection can manage content inventory.
+
+Both types of collections use similar APIs (e.g. `getCollection()` and `getLiveCollection()`), so that working with collections will feel familiar no matter which one you choose, while still ensuring that you always know which type of collection you are working with.
+
+We suggest using build-time content collections whenever possible, and using live collections when your content needs updating in real time and the performance tradeoffs are acceptable. Additionally, live content collections have some limitations compared to build-time collections:
+
+- **No MDX support**: MDX cannot be rendered at runtime
+- **No image optimization**: Images cannot be processed at runtime
+- **Performance considerations**: Data is fetched on each request (unless cached)
+- **No data store persistence**: Data is not saved to the content layer data store
+
+### When to create a collection
+
+Define your data as a collection when:
+
+- You have multiple files or data to organize that share the same overall structure (e.g. a directory of blog posts written in Markdown which all have the same frontmatter properties).
+- You have existing content stored remotely, such as in a CMS, and want to take advantage of the collections helper functions instead of using `fetch()` or SDKs.
+- You need to fetch (tens of) thousands of related pieces of data at build time, and need a querying and caching method that handles at scale.
+
+Much of the benefit of using collections comes from:
+
+- Defining a common data shape to validate that an individual entry is "correct" or "complete", avoiding errors in production.
+- Content-focused APIs designed to make querying intuitive (e.g. `getCollection()` instead of `import.meta.glob()`) when importing and rendering content on your pages.
+- Access to both built-in loaders and access to the low-level [Content Loader API](/en/reference/content-loader-reference/) for retrieving your content. There are additionally several third-party and community-built loaders available, and you can build your own custom loader to fetch data from anywhere.
+- Performance and scalability. Build-time content collections data can be cached between builds and is suitable for tens of thousands of content entries.
+
+### When not to create a collection
+
+Collections provide excellent structure, safety, and organization when you have multiple pieces of content that must share the same properties.
+
+Collections may not be your solution if:
+
+- You have only one or a small number of different content pages. Consider [making individual page components](/en/basics/astro-pages/) such as `src/pages/about.astro` with your content directly instead.
+- You are displaying files that are not processed by Astro, such as PDFs. Place these static assets in the [`public/` directory](/en/basics/project-structure/#public) of your project instead.
+- Your data source has its own SDK/client library for imports that is incompatible with or does not offer a content loader, and you prefer to use it directly.
## TypeScript configuration for collections
-Content collections rely on TypeScript to provide Zod validation, Intellisense and type checking in your editor. If you are not extending one of Astro's `strict` or `strictest` TypeScript settings, you will need to ensure the following `compilerOptions` are set in your `tsconfig.json`:
+Content collections rely on TypeScript to provide Zod validation, Intellisense, and type checking in your editor. By default, Astro configures a [`strict` TypeScript template](/en/guides/typescript/#tsconfig-templates) when you create a new project using the `create astro` CLI command. Both of Astro's `strict` and `strictest` templates include the TypeScript settings your project needs for content collections.
+
+If you changed this setting to `base` because you are not writing TypeScript in your project, or are not using any of Astro's built-in templates, you will need to also add the following `compilerOptions` in your `tsconfig.json` to use content collections:
-```json title="tsconfig.json" ins={5} {6}
+```json title="tsconfig.json" ins={4-7}
{
- // Included with "astro/tsconfigs/strict" or "astro/tsconfigs/strictest"
"extends": "astro/tsconfigs/base",
+ // not needed for `strict` or `strictest`
"compilerOptions": {
- "strictNullChecks": true, // add if using `base` template
- "allowJs": true // required, and included with all Astro templates
+ "strictNullChecks": true,
+ "allowJs": true
}
}
```
-## Defining Collections
+## Defining build-time content collections
-Individual collections use `defineCollection()` to configure:
-- a `loader` for a data source (required)
-- a `schema` for type safety (optional, but highly recommended!)
+All of your build-time content collections are defined in a special `src/content.config.ts` file (`.js` and `.mjs` extensions are also supported) using `defineCollection()`, and then a single collections object is exported for use in your project.
-### The collection config file
-
-To define collections, you must create a `src/content.config.ts` file in your project (`.js` and `.mjs` extensions are also supported.) This is a special file that Astro will use to configure your content collections based on the following structure:
+Each individual collection configures:
+- [a build-time `loader`](#build-time-collection-loaders) for a data source (required)
+- [a build-time `schema`](#defining-the-collection-schema) for type safety (optional, but highly recommended!)
```ts title="src/content.config.ts"
// 1. Import utilities from `astro:content`
@@ -72,87 +141,151 @@ import { glob, file } from 'astro/loaders';
// 3. Import Zod
import { z } from 'astro/zod';
-// 4. Define your collection(s)
-const blog = defineCollection({ /* ... */ });
-const dogs = defineCollection({ /* ... */ });
+// 4. Define a `loader` and `schema` for each collection
+const blog = defineCollection({
+ loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
+ schema: z.object({
+ title: z.string(),
+ description: z.string(),
+ pubDate: z.coerce.date(),
+ updatedDate: z.coerce.date().optional(),
+ }),
+});
// 5. Export a single `collections` object to register your collection(s)
-export const collections = { blog, dogs };
+export const collections = { blog };
```
-### Defining the collection `loader`
+You can then use the dedicated `getCollection()` and `getEntry()` functions to [query your content collections data](#querying-build-time-collections) and render your content.
-The Content Layer API allows you to fetch your content (whether stored locally in your project or remotely) and uses a `loader` property to retrieve your data.
+You can choose to [generate page routes](#generating-routes-from-content) from your build-time collection entries at build time for an entirely static, prerendered site. Or, you can render your build-time collections on demand, choosing to delay building your page until it is first requested. This is useful when you have a large number of pages (e.g. thousands or tens of thousands) and want to delay building a static page until it is needed.
-#### Built-in loaders
+## Build-time collection loaders
-Astro provides [two built-in loader functions](/en/reference/content-loader-reference/#built-in-loaders) (`glob()` and `file()`) for fetching your local content, as well as access to the API to construct your own loader and fetch remote data.
+Astro provides two built-in loaders (`glob()` and `file()`) for fetching your local content at build time. Pass the location of your data in your project or on your filesystem, and these loaders will automatically handle your data and update the persistent data store content layer.
-The [`glob()` loader](/en/reference/content-loader-reference/#glob-loader) creates entries from directories of Markdown, MDX, Markdoc, JSON, YAML, or TOML files from anywhere on the filesystem. It accepts a `pattern` of entry files to match using glob patterns supported by [micromatch](https://github.com/micromatch/micromatch#matching-features), and a base file path of where your files are located. Each entry's `id` will be automatically generated from its file name. Use this loader when you have one file per entry.
+To fetch remote data at build time, you can [build a custom loader](#custom-build-time-loaders) to retrieve your data and update the data store. Or, you can use any [third-party or community-published loader integration](https://astro.build/integrations/2/?search=&categories%5B%5D=loaders). Several already exist for popular content management systems as well as common data sources such as Obsidian vaults, GitHub repositories, or Bluesky posts.
-The [`file()` loader](/en/reference/content-loader-reference/#file-loader) creates multiple entries from a single local file. Each entry in the file must have a unique `id` key property. It accepts a `base` file path to your file and optionally a [`parser` function](#parser-function) for data files it cannot parse automatically, or to parse data asynchronously. Use this loader when your data file can be parsed as an array of objects.
+### The `glob()` loader
-```ts title="src/content.config.ts" {6,10}
+The [`glob()` loader](/en/reference/content-loader-reference/#glob-loader) fetches entries from directories of Markdown, MDX, Markdoc, JSON, YAML, or TOML files from anywhere on the filesystem. If you store your content entries locally as separate files, such as a directory of blog posts, then the `glob()` loader is all you need to access your content.
+
+This loader requires a `pattern` of entry files to match using glob patterns supported by [micromatch](https://github.com/micromatch/micromatch#matching-features), and a `base` file path of where your files are located. A unique `id` for each entry will be automatically generated from its file name, but you can [define custom IDs](#defining-custom-ids) if needed.
+
+```ts title="src/content.config.ts" {5}
import { defineCollection } from 'astro:content';
-import { glob, file } from 'astro/loaders'; // Not available with legacy API
-import { z } from 'astro/zod';
+import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
- schema: /* ... */
});
+
+export const collections = { blog };
+```
+
+#### Defining custom IDs
+
+When using the [`glob()` loader](#the-glob-loader) with Markdown, MDX, Markdoc, JSON, or TOML files, every content entry [`id`](/en/reference/modules/astro-content/#collectionentryid) is automatically generated in an URL-friendly format based on the content filename. This unique `id` is used to query the entry directly from your collection. It is also useful when [creating new pages and URLs from your content](#generating-routes-from-content).
+
+You can override a single entry’s generated `id` by adding your own `slug` property to the file frontmatter or data object for JSON files. This is similar to the “permalink” feature of other web frameworks.
+
+```md title="src/blog/1.md" {3}
+---
+title: My Blog Post
+slug: my-custom-id/supports/slashes
+---
+Your blog post content here.
+```
+
+```json title="src/categories/1.json" {3}
+{
+ "title": "My Category",
+ "slug": "my-custom-id/supports/slashes",
+ "description": "Your category description here."
+}
+```
+
+You can also pass options to the `glob()` loader's [`generateID()` helper function](/en/reference/content-loader-reference/#generateid) when you define your build-time collection to adjust how `id`s are generated. For example, you may wish to revert the default behavior of converting uppercase letters to lowercase for each collection entry:
+
+```js title="src/content.config.ts"
+const authors = defineCollection({
+ /* Retrieve all JSON files in your authors directory while retaining
+ * uppercase letters in the ID. */
+ loader: glob({
+ pattern: '**/*.json',
+ base: "./src/data/authors",
+ generateId: ({ entry }) => entry.replace(/\.json$/, ''),
+ }),
+});
+```
+
+### The `file()` loader
+
+The [`file()` loader](/en/reference/content-loader-reference/#file-loader) fetches multiple entries from a single local file defined in your collection. The `file()` loader will automatically detect and parse (based on the file extension) a single array of objects from JSON and YAML files, and will treat each top-level table as an independent entry in TOML files.
+
+```ts title="src/content.config.ts" {5}
+import { defineCollection } from 'astro:content';
+import { file } from 'astro/loaders';
+
const dogs = defineCollection({
loader: file("src/data/dogs.json"),
- schema: /* ... */
});
-const probes = defineCollection({
- // `loader` can accept an array of multiple patterns as well as string patterns
- // Load all markdown files in the space-probes directory, except for those that start with "voyager-"
- loader: glob({ pattern: ['*.md', '!voyager-*'], base: 'src/data/space-probes' }),
- schema: z.object({
- name: z.string(),
- type: z.enum(['Space Probe', 'Mars Rover', 'Comet Lander']),
- launch_date: z.date(),
- status: z.enum(['Active', 'Inactive', 'Decommissioned']),
- destination: z.string(),
- operator: z.string(),
- notable_discoveries: z.array(z.string()),
- }),
-});
+export const collections = { dogs };
+```
+
+Each entry object in the file must have a unique `id` key property so that the entry can be identified and queried. Unlike the `glob()` loader, the `file()` loader will not automatically generate IDs for each entry.
-export const collections = { blog, dogs, probes };
+You can provide your entries as an array of objects with an `id` property, or in object form where the unique `id` is the key:
+
+```json title="src/data/dogs.json"
+// Specify an `id` property in each object of an array
+[
+ { "id": "poodle", "coat": "curly", "shedding": "low" },
+ { "id": "afghan", "coat": "short", "shedding": "low" }
+]
```
-##### `parser` function
+```json title="src/data/dogs.json"
+// Each key will be used as the `id`
+{
+ "poodle": { "coat": "curly", "shedding": "low" },
+ "afghan": { "coat": "silky", "shedding": "low" }
+}
+```
-The `file()` loader accepts a second argument that defines a `parser` function. This allows you to specify a custom parser (e.g. `csv-parse`) to create a collection from a file's contents.
+#### Parsing other data formats
-The `file()` loader will automatically detect and parse (based on their file extension) a single array of objects from JSON and YAML files, and will treat each top-level table as an independent entry in TOML files. Support for these file types is built-in, and there is no need for a `parser` unless you have a [nested JSON document](#nested-json-documents). To use other files, such as `.csv`, you will need to create a parser function.
+Support for parsing single JSON, YAML, and TOML files into collection entries withe the `file()` loader is built-in (unless you have a [nested JSON document](#nested-json-documents)). To load your collection from unsupported file types, such as `.csv`, you will need to create a [parser function](/en/reference/content-loader-reference/#parser). This function can be made async if required (e.g. to fetch files from the web, or if your parser is asyncronous).
-The following example shows importing a CSV parser, then loading a `cats` collection into your project by passing both a file path and `parser` function to the `file()` loader:
+The following example shows importing a third-party CSV parser then passing a custom `parser` function to the `file()` loader:
-```typescript title="src/content.config.ts"
+```typescript title="src/content.config.ts" {3} "parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true })"
import { defineCollection } from "astro:content";
import { file } from "astro/loaders";
import { parse as parseCsv } from "csv-parse/sync";
const cats = defineCollection({
- loader: file("src/data/cats.csv", { parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true })})
+ loader: file("src/data/cats.csv", {
+ parser: (text) => parseCsv(text, { columns: true, skipEmptyLines: true }),
+ }),
});
```
-###### Nested `.json` documents
+##### Nested `.json` documents
-The `parser` argument also allows you to load a single collection from a nested JSON document. For example, this JSON file contains multiple collections:
+The `parser()` argument can be used to load a single collection from a nested JSON document. For example, this JSON file contains multiple collections:
```json title="src/data/pets.json"
{"dogs": [{}], "cats": [{}]}
```
-You can separate these collections by passing a custom `parser` to the `file()` loader for each collection:
+You can separate these collections by passing a custom `parser()` function to the `file()` loader for each collection, using Astro's built-in JSON parsing:
```typescript title="src/content.config.ts"
+import { file } from "astro/loaders";
+import { defineCollection } from "astro:content";
+
const dogs = defineCollection({
loader: file("src/data/pets.json", { parser: (text) => JSON.parse(text).dogs })
});
@@ -161,48 +294,35 @@ const cats = defineCollection({
});
```
-#### Building a custom loader
-
-You can build a custom loader to fetch remote content from any data source, such as a CMS, a database, or an API endpoint.
+### Custom build-time loaders
-Using a loader to fetch your data will automatically create a collection from your remote data. This gives you all the benefits of local collections, such as collection-specific API helpers such as `getCollection()` and `render()` to query and display your data, as well as schema validation.
+You can [build a custom loader](/en/reference/content-loader-reference/#building-a-loader) using the Content Loader API to fetch remote content from any data source, such as a CMS, a database, or an API endpoint.
-:::tip
-Find community-built and third-party loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders).
-:::
-
-##### Inline loaders
-
-You can define a loader inline, inside your collection, as an async function that returns an array of entries.
-
-This is useful for loaders that don't need to manually control how the data is loaded and stored. Whenever the loader is called, it will clear the store and reload all the entries.
+Then you can import and define your custom loader in your collections config, passing any required values:
```ts title="src/content.config.ts"
-const countries = defineCollection({
- loader: async () => {
- const response = await fetch("https://restcountries.com/v3.1/all");
- const data = await response.json();
- // Must return an array of entries with an id property, or an object with IDs as keys and entries as values
- return data.map((country) => ({
- id: country.cca3,
- ...country,
- }));
- },
- schema: /* ... */
+import { defineCollection } from 'astro:content';
+import { myLoader } from './loader.ts';
+
+const blog = defineCollection({
+ loader: myLoader({
+ url: "https://api.example.com/posts",
+ apiKey: "my-secret",
+ }),
});
```
-The returned entries are stored in the collection and can be queried using the `getCollection()` and `getEntry()` functions.
-
-##### Loader objects
+:::tip
+Find community-built and third-party loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders).
+:::
-For more control over the loading process, you can use the Content Loader API to create a loader object. For example, with access to the `load` method directly, you can create a loader that allows entries to be updated incrementally or clears the store only when necessary.
+Using a custom loader to fetch your data will automatically create a collection from your remote data. This gives you all the benefits of local collections, including collection-specific API helpers such as `getCollection()` and `render()` to [query and display your data](#querying-build-time-collections), as well as schema validation.
-Similar to creating an Astro integration or Vite plugin, you can [distribute your loader as an NPM package](/en/reference/publish-to-npm/) that others can use in their projects.
+Similar to creating an Astro integration or Vite plugin, you can [distribute your loader as an npm package](/en/reference/publish-to-npm/) that others can use in their projects.
-See the full [Content Loader API](/en/reference/content-loader-reference/) and examples of how to build your own loader.
+See the full [Content Loader API](/en/reference/content-loader-reference/) for examples of how to build your own loader.
-### Defining the collection schema
+## Defining the collection schema
Schemas enforce consistent frontmatter or entry data within a collection through Zod validation. A schema **guarantees** that this data exists in a predictable form when you need to reference or query it. If any file violates its collection schema, Astro will provide a helpful error to let you know.
@@ -212,12 +332,12 @@ Schemas also power Astro's automatic TypeScript typings for your content. When y
In order for Astro to recognize a new or updated schema, you may need to restart the dev server or [sync the content layer](/en/reference/cli-reference/#astro-dev) (s + enter) to define the `astro:content` module.
:::
-Every frontmatter or data property of your collection entries must be defined using a [Zod data type](/en/reference/modules/astro-zod/#common-data-type-validators):
+Providing a `schema` is optional, but highly recommended! If you choose to use a schema, then every frontmatter or data property of your collection entries must be defined using a [Zod data type](/en/reference/modules/astro-zod/#common-data-type-validators):
```ts title="src/content.config.ts" {7-12,16-20}
import { defineCollection } from 'astro:content';
-import { glob, file } from 'astro/loaders';
import { z } from 'astro/zod';
+import { glob, file } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
@@ -240,23 +360,23 @@ const dogs = defineCollection({
export const collections = { blog, dogs };
```
-#### Defining datatypes with Zod
+### Defining datatypes with Zod
-Astro uses [Zod](https://github.com/colinhacks/zod) to power its content schemas. With Zod, Astro is able to validate every file's data within a collection *and* provide automatic TypeScript types when you go to query content from inside your project.
+Astro uses [Zod](https://github.com/colinhacks/zod) to power its content schemas. With Zod, Astro is able to validate every file's data within a collection *and* provide automatic TypeScript types when you query content from inside your project.
-To use Zod in Astro, import the `z` utility from `"astro/zod"`. This is a re-export of the Zod library, and it supports all of the features of Zod.
+To use Zod in Astro, import the `z` utility from `"astro/zod"`. This is a re-export of the Zod library, and it supports all of the features of Zod 4.
See the [`z` utility reference](/en/reference/modules/astro-zod/) for a cheatsheet of common datatypes and to learn how Zod works and what features are available.
-##### Zod schema methods
+#### Zod schema methods
All [Zod schema methods](/en/reference/modules/astro-zod/#using-zod-methods) (e.g. `.parse()`, `.transform()`) are available, with some limitations. Notably, performing custom validation checks on images using `image().refine()` is unsupported.
-#### Defining collection references
+### Defining collection references
Collection entries can also "reference" other related entries.
-With the [`reference()` function](/en/reference/modules/astro-content/#reference) from the Collections API, you can define a property in a collection schema as an entry from another collection. For example, you can require that every `space-shuttle` entry includes a `pilot` property which uses the `pilot` collection's own schema for type checking, autocomplete, and validation.
+With the [`reference()` function](/en/reference/modules/astro-content/#reference), you can define a property in a collection schema as an entry from another collection. For example, you can require that every `space-shuttle` entry includes a `pilot` property which uses the `pilot` collection's own schema for type checking, autocomplete, and validation.
A common example is a blog post that references reusable author profiles stored as JSON, or related post URLs stored in the same collection:
@@ -266,21 +386,21 @@ import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
const blog = defineCollection({
- loader: glob({ pattern: '**/[^_]*.md', base: "./src/data/blog" }),
+ loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
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`
+ // Reference an array of related posts from the `blog` collection by `id`
relatedPosts: z.array(reference('blog')),
})
});
const authors = defineCollection({
- loader: glob({ pattern: '**/[^_]*.json', base: "./src/data/authors" }),
+ loader: glob({ pattern: '**/*.json', base: "./src/data/authors" }),
schema: z.object({
name: z.string(),
- portfolio: z.string().url(),
+ portfolio: z.url(),
})
});
@@ -289,50 +409,29 @@ export const collections = { blog, authors };
This example blog post specifies the `id`s of related posts and the `id` of the post author:
-```yaml title="src/data/blog/welcome.md"
+```yaml title="src/content/blog/welcome.md"
---
title: "Welcome to my blog"
author: ben-holmes # references `src/data/authors/ben-holmes.json`
relatedPosts:
-- about-me # references `src/data/blog/about-me.md`
-- my-year-in-review # references `src/data/blog/my-year-in-review.md`
+- about-me # references `src/content/blog/about-me.md`
+- my-year-in-review # references `src/content/blog/my-year-in-review.md`
---
```
These references will be transformed into objects containing a `collection` key and an `id` key, allowing you to easily [query them in your templates](/en/guides/content-collections/#accessing-referenced-data).
-### Defining custom IDs
+## Querying build-time collections
-When using the `glob()` loader with Markdown, MDX, Markdoc, or JSON files, every content entry [`id`](/en/reference/modules/astro-content/#id) is automatically generated in an URL-friendly format based on the content filename. The `id` is used to query the entry directly from your collection. It is also useful when creating new pages and URLs from your content.
-
-You can override an entry’s generated `id` by adding your own `slug` property to the file frontmatter or data object for JSON files. This is similar to the “permalink” feature of other web frameworks.
-
-```md title="src/blog/1.md" {3}
----
-title: My Blog Post
-slug: my-custom-id/supports/slashes
----
-Your blog post content here.
-```
-
-```json title="src/categories/1.json" {3}
-{
- "title": "My Category",
- "slug": "my-custom-id/supports/slashes",
- "description": "Your category description here."
-}
-```
-
-## Querying Collections
-
-Astro provides helper functions to query a collection and return one (or more) content entries.
+Astro provides helper functions to query a build-time collection and return one or more content entries.
- [`getCollection()`](/en/reference/modules/astro-content/#getcollection) fetches an entire collection and returns an array of entries.
- [`getEntry()`](/en/reference/modules/astro-content/#getentry) fetches a single entry from a collection.
These return entries with a unique `id`, a `data` object with all defined properties, and will also return a `body` containing the raw, uncompiled body of a Markdown, MDX, or Markdoc document.
-```js
+```astro title="src/pages/index.astro"
+---
import { getCollection, getEntry } from 'astro:content';
// Get all entries from a collection.
@@ -342,8 +441,7 @@ const allBlogPosts = await getCollection('blog');
// Get a single entry from a collection.
// Requires the name of the collection and `id`
const poodleData = await getEntry('dogs', 'poodle');
-
-
+---
```
The sort order of generated collections is non-deterministic and platform-dependent. This means that if you are calling `getCollection()` and need your entries returned in a specific order (e.g. blog posts sorted by date), you must sort the collection entries yourself:
@@ -362,7 +460,9 @@ const posts = (await getCollection('blog')).sort(
### Using content in Astro templates
-After querying your collections, you can access each entry's content directly inside of your Astro component template. For example, you can create a list of links to your blog posts, displaying information from your entry's frontmatter using the `data` property.
+After querying your collections, you can access each entry's content and metadata directly inside of your Astro component template.
+
+For example, you can create a list of links to your blog posts, displaying information from your entry's frontmatter using the `data` property:
```astro title="src/pages/index.astro"
@@ -377,21 +477,20 @@ const posts = await getCollection('blog');
))}
```
-#### Rendering body content
-Once queried, you can render Markdown and MDX entries to HTML using the [`render()`](/en/reference/modules/astro-content/#render) function property. Calling this function gives you access to rendered HTML content, including both a `` component and a list of all rendered headings.
+### Rendering body content
+
+Once queried, you can render Markdown and MDX entries to HTML using the [`render()`](/en/reference/modules/astro-content/#render) function from `astro:content`. Calling this function gives you access to rendered HTML content, including both a `` component and a list of all rendered headings.
-```astro title="src/pages/blog/post-1.astro" {5,8}
+```astro title="src/pages/blog/post-1.astro" {2,6,10}
---
import { getEntry, render } from 'astro:content';
const entry = await getEntry('blog', 'post-1');
-if (!entry) {
- // Handle Error, for example:
- throw new Error('Could not find blog post 1');
-}
-const { Content, headings } = await render(entry);
+
+const { Content } = await render(entry);
---
+
{entry.data.title}
Published on: {entry.data.published.toDateString()}
```
@@ -422,48 +521,60 @@ const { post } = Astro.props;
You can use this to filter by any content criteria you like. For example, you can filter by properties like `draft` to prevent any draft blog posts from publishing to your blog:
-```js
+```astro title="src/pages/blog.astro"
+---
// Example: Filter out content entries with `draft: true`
import { getCollection } from 'astro:content';
const publishedBlogEntries = await getCollection('blog', ({ data }) => {
return data.draft !== true;
});
+---
```
You can also create draft pages that are available when running the dev server, but not built in production:
-```js
+```astro title="src/pages/blog.astro"
+---
// Example: Filter out content entries with `draft: true` only when building for production
import { getCollection } from 'astro:content';
const blogEntries = await getCollection('blog', ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true;
});
+---
```
The filter argument also supports filtering by nested directories within a collection. Since the `id` includes the full nested path, you can filter by the start of each `id` to only return items from a specific nested directory:
-```js
+```astro title="src/pages/blog.astro"
+---
// Example: Filter entries by sub-directory in the collection
import { getCollection } from 'astro:content';
const englishDocsEntries = await getCollection('docs', ({ id }) => {
return id.startsWith('en/');
});
+---
```
### Accessing referenced data
-Any [references defined in your schema](/en/guides/content-collections/#defining-collection-references) must be queried separately after first querying your collection entry. Since the [`reference()` function](/en/reference/modules/astro-content/#reference) transforms a reference to an object with `collection` and `id` as keys, you can use the `getEntry()` function to return a single referenced item, or `getEntries()` to retrieve multiple referenced entries from the returned `data` object.
+To access [references defined in your schema](/en/guides/content-collections/#defining-collection-references), first query your collection entry. Your references will be available on the returned `data` object. (e.g. `entry.data.author` and `entry.data.relatedPosts`)
+
+Then, you can use the `getEntry()` function again (or `getEntries()` to retrieve multiple referenced entries) by passing those returned values. The `reference()` function in your schema transforms those values into one or more `collection` and `id` objects as a convenient way to query this related data.
-```astro title="src/pages/blog/welcome.astro"
+
+```astro title="src/pages/blog/adventures-in-space.astro"
---
import { getEntry, getEntries } from 'astro:content';
-const blogPost = await getEntry('blog', 'welcome');
+// First, query a blog post
+const blogPost = await getEntry('blog', 'Adventures in Space');
-// Resolve a singular reference (e.g. `{collection: "authors", id: "ben-holmes"}`)
+// Retrieve a single reference item: the blog post's author
+// Equivalent to querying `{collection: "authors", id: "ben-holmes"}`
const author = await getEntry(blogPost.data.author);
-// Resolve an array of references
-// (e.g. `[{collection: "blog", id: "about-me"}, {collection: "blog", id: "my-year-in-review"}]`)
+
+// Retrieve an array of referenced items: all the related posts
+// Equivalent to querying `[{collection: "blog", id: "visiting-mars"}, {collection: "blog", id: "leaving-earth-for-the-first-time"}]`
const relatedPosts = await getEntries(blogPost.data.relatedPosts);
---
@@ -480,17 +591,17 @@ const relatedPosts = await getEntries(blogPost.data.relatedPosts);
## Generating Routes from Content
-Content collections are stored outside of the `src/pages/` directory. This means that no pages or routes are generated for your collection items by default.
+Content collections are stored outside of the `src/pages/` directory. This means that no pages or routes are generated for your collection items by default by Astro's [file-based routing](/en/guides/routing/).
-You will need to manually create a new [dynamic route](/en/guides/routing/#dynamic-routes) if you want to generate HTML pages for each of your collection entries, such as individual blog posts. Your dynamic route will map the incoming request param (e.g. `Astro.params.slug` in `src/pages/blog/[...slug].astro`) to fetch the correct entry for each page.
+You will need to manually create a new [dynamic route](/en/guides/routing/#dynamic-routes) if you want to generate HTML pages for each of your collection entries, such as individual blog posts. Your dynamic route will map the incoming request param (e.g. `Astro.params.id` in `src/pages/blog/[...id].astro`) to fetch the correct entry for each page.
The exact method for generating routes will depend on whether your pages are prerendered (default) or rendered on demand by a server.
### Building for static output (default)
-If you are building a static website (Astro's default behavior), use the [`getStaticPaths()`](/en/reference/routing-reference/#getstaticpaths) function to create multiple pages from a single page component (e.g. `src/pages/[slug]`) during your build.
+If you are building a static website (Astro's default behavior) with build-time collections, use the [`getStaticPaths()`](/en/reference/routing-reference/#getstaticpaths) function to create multiple pages from a single page component (e.g. `src/pages/[id].astro`) during your build.
-Call `getCollection()` inside of `getStaticPaths()` to have your collection data available for building static routes. Then, create the individual URL paths using the `id` property of each content entry. Each page is passed the entire collection entry as a prop for [use in your page template](#using-content-in-astro-templates).
+Call `getCollection()` inside of `getStaticPaths()` to have your collection data available for building static routes. Then, create the individual URL paths using the `id` property of each content entry. Each page receives the entire collection entry as a prop for [use in your page template](#using-content-in-astro-templates).
```astro title="src/pages/posts/[id].astro" "{ id: post.id }" "{ post }"
---
@@ -514,28 +625,37 @@ const { Content } = await render(post);
This will generate a page route for every entry in the `blog` collection. For example, an entry at `src/blog/hello-world.md` will have an `id` of `hello-world`, and therefore its final URL will be `/posts/hello-world/`.
:::note
-If your custom slugs contain the `/` character to produce URLs with multiple path segments, you must use a [rest parameter (e.g. `[...slug]`)](/en/guides/routing/#rest-parameters) in the `.astro` filename for this dynamic routing page.
+If your custom slugs contain the `/` character to produce URLs with multiple path segments, you must use a [rest parameter (e.g. `[...id]`)](/en/guides/routing/#rest-parameters) in the `.astro` filename for this dynamic routing page.
:::
-### Building for server output (SSR)
+### Building routes on demand at request time
-If you are building a dynamic website (using Astro's SSR support), you are not expected to generate any paths ahead of time during the build. Instead, your page should examine the request (using `Astro.request` or `Astro.params`) to find the `slug` on-demand, and then fetch it using [`getEntry()`](/en/reference/modules/astro-content/#getentry).
+With an adapter installed for [on-demand rendering](/en/guides/on-demand-rendering/), you can generate your dynamic page routes at request time. First, examine the request (using `Astro.request` or `Astro.params`) to find the slug on demand, and then fetch it using one of Astro's content collection helper functions:
+
+- [`getEntry()`](/en/reference/modules/astro-content/#getentry) for build-time collection pages that are generated once, upon first request.
+- [`getLiveEntry()`](/en/reference/modules/astro-content/#getentry) for live collection pages where data is (re)fetched at each request time.
```astro title="src/pages/posts/[id].astro"
---
+export const prerender = false; // Not needed in 'server' mode
+
import { getEntry, render } from "astro:content";
+
// 1. Get the slug from the incoming server request
const { id } = Astro.params;
if (id === undefined) {
return Astro.redirect("/404");
}
+
// 2. Query for the entry directly using the request slug
const post = await getEntry("blog", id);
+
// 3. Redirect if the entry does not exist
if (post === undefined) {
return Astro.redirect("/404");
}
+
// 4. Render the entry to HTML in the template
const { Content } = await render(post);
---
@@ -544,10 +664,211 @@ const { Content } = await render(post);
```
:::tip
-Explore the `src/pages/` folder of the [blog tutorial demo code on GitHub](https://github.com/withastro/blog-tutorial-demo/tree/content-collections/src/pages) to see full examples of creating pages from your collections for blog features like a list of blog posts, tags pages, and more!
+Explore the `src/pages/` folder of the [blog tutorial demo code on GitHub](https://github.com/withastro/blog-tutorial-demo/tree/content-collections/src/pages) to see full examples of creating dynamic pages from your collections for blog features like a list of blog posts, tags pages, and more!
+:::
+
+## Live content collections
+
+Live collections use a different API than build-time content collections, although the configuration and helper functions are designed to feel familiar.
+
+Key differences include:
+
+1. **Execution time**: Run at request time instead of build time
+2. **Configuration file**: Use `src/live.config.ts` instead of `src/content.config.ts`
+3. **Collection definition**: Use `defineLiveCollection()` instead of `defineCollection()`
+4. **Loader API**: Implement `loadCollection` and `loadEntry` methods instead of the `load` method
+5. **Data return**: Return data directly instead of storing in the data store
+6. **User-facing functions**: Use `getLiveCollection()`/`getLiveEntry()` instead of `getCollection()`/`getEntry()`
+
+Additionally, you must have an adapter configured for [on-demand rendering](/en/guides/on-demand-rendering/) of live collection data.
+
+Define your live collections in the special file `src/live.config.ts` (separate from your `src/content.config.ts` for build-time collections, if you have one).
+
+Each individual collection configures:
+- a [live `loader`](#creating-a-live-loader) for your data source, and optionally for type safety (required)
+- a [live collection `schema`](#using-zod-schemas-with-live-collections) for type safety (optional)
+
+Unlike for build-time collections, there are no built-in live loaders available. You will need to [create a custom live loader](#creating-a-live-loader) for your specific data source or find a third-party loader to pass to your live collection's `loader` property.
+
+You can optionally [include type safety in your live loaders](/en/reference/content-loader-reference/#the-liveloader-object). Therefore, [defining a Zod `schema`](#using-zod-schemas-with-live-collections) for live collections is optional. However, if you provide one, it will take precedence over the live loader's types.
+
+```ts title="src/live.config.ts"
+// Define live collections for accessing real-time data
+import { defineLiveCollection } from 'astro:content';
+import { storeLoader } from '@mystore/astro-loader';
+
+const products = defineLiveCollection({
+ loader: storeLoader({
+ apiKey: process.env.STORE_API_KEY,
+ endpoint: 'https://api.mystore.com/v1',
+ }),
+});
+
+// Export a single `collections` object to register your collection(s)
+export const collections = { products };
+```
+
+You can then use the dedicated `getLiveCollection()` and `getLiveEntry()` functions to [access your live data](#accessing-live-data) and render your content.
+
+You can [generate page routes](#generating-routes-from-content) from your live collection entries on demand, fetching your data fresh at runtime upon each request without needing a rebuild of your site like [build-time collections](#defining-build-time-content-collections) do. This is useful when accessing live, up-to-the-moment data is more important than having your content available in a performant data storage layer that persists between site builds.
+
+### Creating a live loader
+
+You can build a custom [live loader](/en/reference/content-loader-reference/#live-loaders) using the Live Loader API to fetch remote content fresh upon request from any data source, such as a CMS, a database or an API endpoint. You will have to tell your live loader how to fetch and return content entries from your desired data source, as well as provide error handling for unsuccessful data requests.
+
+Using a live loader to fetch your data will automatically create a collection from your remote data. This gives you all the benefits of Astro's content collections, including collection-specific API helpers such as `getLiveCollection()` and `render()` to [query and display your data](#querying-build-time-collections), as well as helpful error handling.
+
+:::tip
+Find community-built and third-party live loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders).
:::
-## Collection JSON Schemas
+See the basics of [building a live loader](/en/reference/content-loader-reference/#building-a-live-loader) using the Live Loader API
+
+### Using Zod schemas with live collections
+
+You can use Zod schemas with live collections to validate and transform data at runtime. This Zod validation works the same way as [schemas for build-time collections](#defining-the-collection-schema).
+
+When you define a schema for a live collection, it takes precedence over [the live loader's types](/en/reference/content-loader-reference/#the-liveloader-object) when you query the collection:
+
+```ts title="src/live.config.ts"
+import { defineLiveCollection } from 'astro:content';
+import { z } from 'astro/zod';
+import { apiLoader } from './loaders/api-loader';
+
+const products = defineLiveCollection({
+ loader: apiLoader({ endpoint: process.env.API_URL }),
+ schema: z
+ .object({
+ id: z.string(),
+ name: z.string(),
+ price: z.number(),
+ // Transform the API's category format
+ category: z.string().transform((str) => str.toLowerCase().replace(/\s+/g, '-')),
+ // Coerce the date to a Date object
+ createdAt: z.coerce.date(),
+ })
+ .transform((data) => ({
+ ...data,
+ // Add a formatted price field
+ displayPrice: `$${data.price.toFixed(2)}`,
+ })),
+});
+
+export const collections = { products };
+```
+
+When using Zod schemas with live collections, validation errors are automatically caught and returned as `AstroError` objects:
+
+```astro title="src/pages/store/index.astro"
+---
+export const prerender = false; // Not needed in 'server' mode
+
+import { LiveCollectionValidationError } from 'astro/content/runtime';
+import { getLiveEntry } from 'astro:content';
+
+const { entry, error } = await getLiveEntry('products', '123');
+
+// You can handle validation errors specifically
+if (LiveCollectionValidationError.is(error)) {
+ console.error(error.message);
+ return Astro.rewrite('/500');
+}
+
+// TypeScript knows entry.data matches your Zod schema, not the loader's type
+console.log(entry?.data.displayPrice); // e.g., "$29.99"
+---
+```
+
+See [Zod's README](https://github.com/colinhacks/zod) for complete documentation on how Zod works and what features are available.
+
+
+### Accessing live data
+
+Astro provides live collection helper functions to access live data on each request and return one (or more) content entries. These can be used similarly to their [build-time collection counterparts](#querying-build-time-collections).
+
+- [`getLiveCollection()`](/en/reference/modules/astro-content/#getlivecollection) fetches an entire collection and returns an array of entries.
+- [`getLiveEntry()`](/en/reference/modules/astro-content/#getliveentry) fetches a single entry from a collection.
+
+These return entries with a unique `id`, and `data` object with all defined properties from the live loader. When using third-party or community loaders distributed as npm packages, check their own documentation for the expected shape of data returned.
+
+You can use these functions to access your live data, passing the name of the collection and optionally filtering conditions.
+
+```astro title="src/pages/store/[slug].astro"
+---
+export const prerender = false; // Not needed in 'server' mode
+
+import { getLiveCollection, getLiveEntry } from 'astro:content';
+
+// Use loader-specific filters
+const { entries: draftArticles } = await getLiveCollection('articles', {
+ status: 'draft',
+ author: 'john-doe',
+});
+
+// Get a specific product by ID
+const { entry: product } = await getLiveEntry('products', Astro.params.slug);
+---
+```
+
+#### Rendering content
+
+If your live loader [returns a `rendered` property](/en/reference/content-loader-reference/#livedataentryrendered), you can use [the `render()` function and `` component](#rendering-body-content) to render your content directly in your pages, using the same method as build-time collections.
+
+You also have access to any [error returned by the live loader](/en/reference/content-loader-reference/#error-handling-in-live-loaders), for example, to rewrite to a 404 page when content cannot be displayed:
+
+```astro title="src/pages/store/[id].astro" "render(entry)" ""
+---
+export const prerender = false; // Not needed in 'server' mode
+
+import { getLiveEntry, render } from 'astro:content';
+const { entry, error } = await getLiveEntry('articles', Astro.params.id);
+if (error) {
+ return Astro.rewrite('/404');
+}
+
+const { Content } = await render(entry);
+---
+
+
{entry.data.title}
+
+```
+
+#### Error handling
+
+Live loaders can fail due to network issues, API errors, or validation problems. The API is designed to make error handling explicit.
+
+When you call `getLiveCollection()` or `getLiveEntry()`, the error will be one of:
+
+- The error type defined by the loader (if it returned an error)
+- A `LiveEntryNotFoundError` if the entry was not found
+- A `LiveCollectionValidationError` if the collection data does not match the expected schema
+- A `LiveCollectionCacheHintError` if the cache hint is invalid
+- A `LiveCollectionError` for other errors, such as uncaught errors thrown in the loader
+
+You can use `instanceof` to check the type of an error at runtime:
+
+```astro title="src/pages/store/[id].astro" "LiveEntryNotFoundError.is(error)"
+---
+export const prerender = false; // Not needed in 'server' mode
+
+import { LiveEntryNotFoundError } from 'astro/content/runtime';
+import { getLiveEntry } from 'astro:content';
+
+const { entry, error } = await getLiveEntry('products', Astro.params.id);
+
+if (error) {
+ if (error instanceof LiveEntryNotFoundError) {
+ console.error(`Product not found: ${error.message}`);
+ Astro.response.status = 404;
+ } else {
+ console.error(`Error loading product: ${error.message}`);
+ return Astro.redirect('/500');
+ }
+}
+---
+```
+
+## Using JSON Schema files in your editor
@@ -556,7 +877,7 @@ Astro auto-generates [JSON Schema](https://json-schema.org/) files for collectio
A JSON Schema file is generated for each collection in your project and output to the `.astro/collections/` directory.
For example, if you have two collections, one named `authors` and another named `posts`, Astro will generate `.astro/collections/authors.schema.json` and `.astro/collections/posts.schema.json`.
-### Use JSON Schemas in JSON files
+
Use JSON Schemas in JSON files
You can manually point to an Astro-generated schema by setting the `$schema` field in your JSON file.
The value should be a relative file path from the data file to the schema.
@@ -570,9 +891,9 @@ In the following example, a data file in `src/data/authors/` uses the schema gen
}
```
-#### Use a schema for a group of JSON files in VS Code
+
Use a schema for a group of JSON files in VS Code
-In VS Code, you can configure a schema to apply for all files in a collection using the [`json.schemas` setting](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings).
+In VS Code, you can configure a schema to apply to all files in a collection using the [`json.schemas` setting](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings).
In the following example, all files in the `src/data/authors/` directory will use the schema generated for the `authors` collection:
```json
@@ -586,7 +907,7 @@ In the following example, all files in the `src/data/authors/` directory will us
}
```
-### Use schemas in YAML files in VS Code
+
Use schemas in YAML files in VS Code
In VS Code, you can add support for using JSON schemas in YAML files using the [Red Hat YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) extension.
With this extension installed, you can reference a schema in a YAML file using a special comment syntax:
@@ -599,9 +920,9 @@ skills:
- Starlight
```
-#### Use schemas for a group of YAML files in VS Code
+
Use schemas for a group of YAML files in VS Code
-With the Red Hat YAML extension, you can configure a schema to apply for all YAML files in a collection using the `yaml.schemas` setting.
+With the Red Hat YAML extension, you can configure a schema to apply to all YAML files in a collection using the `yaml.schemas` setting.
In the following example, all YAML files in the `src/data/authors/` directory will use the schema generated for the `authors` collection:
```json
@@ -613,31 +934,3 @@ In the following example, all YAML files in the `src/data/authors/` directory wi
```
See [“Associating schemas”](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml#associating-schemas) in the Red Hat YAML extension documentation for more details.
-
-## When to create a collection
-
-You can [create a collection](#defining-collections) any time you have a group of related data or content that shares a common structure.
-
-Much of the benefit of using collections comes from:
-
-- Defining a common data shape to validate that an individual entry is "correct" or "complete", avoiding errors in production.
-- Content-focused APIs designed to make querying intuitive (e.g. `getCollection()` instead of `import.meta.glob()`) when importing and rendering content on your pages.
-- A [Content Loader API](/en/reference/content-loader-reference/) for retrieving your content that provides both built-in loaders and access to the low-level API. There are several third-party and community-built loaders available, and you can build your own custom loader to fetch data from anywhere.
-- Performance and scalability. The Content Layer API allows data to be cached between builds and is suitable for tens of thousands of content entries.
-
-[Define your data](#defining-collections) as a collection when:
-
-- You have multiple files or data to organize that share the same overall structure (e.g. blog posts written in Markdown which all have the same frontmatter properties).
-- You have existing content stored remotely, such as in a CMS, and want to take advantage of the collections helper functions and Content Layer API instead of using `fetch()` or SDKs.
-- You need to fetch (tens of) thousands of related pieces of data, and need a querying and caching method that handles at scale.
-
-### When not to create a collection
-
-Collections provide excellent structure, safety, and organization when you have **multiple pieces of content that must share the same properties**.
-
-Collections **may not be your solution** if:
-
-- You have only one or a small number of different pages. Consider [making individual page components](/en/basics/astro-pages/) such as `src/pages/about.astro` with your content directly instead.
-- You are displaying files that are not processed by Astro, such as PDFs. Place these static assets in the [`public/` directory](/en/basics/project-structure/#public) of your project instead.
-- Your data source has its own SDK/client library for imports that is incompatible with or does not offer a content loader and you prefer to use it directly.
-- You are using APIs that need to be updated in real time. Content collections are only updated at build time, so if you need live data, use other methods of [importing files](/en/guides/imports/#import-statements) or [fetching data](/en/guides/data-fetching/) with [on-demand rendering](/en/guides/on-demand-rendering/).
diff --git a/src/content/docs/en/guides/deploy/clever-cloud.mdx b/src/content/docs/en/guides/deploy/clever-cloud.mdx
index 6cd0231c45cc1..5dfae0eed2537 100644
--- a/src/content/docs/en/guides/deploy/clever-cloud.mdx
+++ b/src/content/docs/en/guides/deploy/clever-cloud.mdx
@@ -11,7 +11,7 @@ i18nReady: true
import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
-[Clever Cloud](https://clever-cloud.com) is a European cloud platform that provides automated, scalable services.
+[Clever Cloud](https://clevercloud.com) is a European cloud platform that provides automated, scalable services.
## Project Configuration
@@ -45,7 +45,7 @@ To deploy your Astro project to Clever Cloud, you will need to **create a new ap
3. Select a **Node.js** application, or a **static** one.
4. Set up the minimal size for your instance and scalability options. Astro sites can typically be deployed using the **Nano** instance. Depending on your project's specifications and dependencies, you may need to adjust accordingly as you watch the metrics from the **Overview** page.
5. Select a **region** to deploy your instance.
-6. Skip [connecting **Add-ons** to your Clever application](https://www.clever-cloud.com/developers/doc/addons/) unless you're using a database or Keycloak.
+6. Skip [connecting **Add-ons** to your Clever application](https://www.clevercloud.com/developers/doc/addons/) unless you're using a database or Keycloak.
7. Inject **environment variables**:
- For **Node.js**, set the following environment variables based on your package manager:
@@ -111,5 +111,5 @@ For example, if you want to deploy your local `main` branch without renaming it,
## Official Resources
-- [Clever Cloud documentation for deploying a Node.js application](https://www.clever-cloud.com/developers/doc/applications/javascript/nodejs/)
-- [Clever Cloud documentation for deploying Astro as a static application](https://www.clever-cloud.com/developers/guides/astro/)
+- [Clever Cloud documentation for deploying a Node.js application](https://www.clevercloud.com/developers/doc/applications/nodejs/)
+- [Clever Cloud documentation for deploying Astro as a static application](https://www.clevercloud.com/developers/guides/astro/)
diff --git a/src/content/docs/en/guides/deploy/microsoft-azure.mdx b/src/content/docs/en/guides/deploy/microsoft-azure.mdx
index 9eba99e7c30ac..889968503d19d 100644
--- a/src/content/docs/en/guides/deploy/microsoft-azure.mdx
+++ b/src/content/docs/en/guides/deploy/microsoft-azure.mdx
@@ -40,7 +40,7 @@ The GitHub action yaml that is created for you assumes the use of node 14. This
```
"engines": {
- "node": ">=18.0.0"
+ "node": ">=22.12.0"
},
```
diff --git a/src/content/docs/en/guides/deploy/netlify.mdx b/src/content/docs/en/guides/deploy/netlify.mdx
index 1d59154bafb77..df5016fe5d20d 100644
--- a/src/content/docs/en/guides/deploy/netlify.mdx
+++ b/src/content/docs/en/guides/deploy/netlify.mdx
@@ -101,7 +101,7 @@ You can also create a new site on Netlify and link up your Git repository by ins
### Set a Node.js version
-If you are using a legacy [build image](https://docs.netlify.com/configure-builds/get-started/#build-image-selection) (Xenial) on Netlify, make sure that your Node.js version is set. Astro requires `v18.20.8` or `v20.3.0` or higher.
+If you are using a legacy [build image](https://docs.netlify.com/configure-builds/get-started/#build-image-selection) (Xenial) on Netlify, make sure that your Node.js version is set. Astro requires `v22.12.0` or higher.
You can [specify your Node.js version in Netlify](https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript) using:
- a [`.nvmrc`](https://github.com/nvm-sh/nvm#nvmrc) file in your base directory.
diff --git a/src/content/docs/en/guides/endpoints.mdx b/src/content/docs/en/guides/endpoints.mdx
index 9bedfb7bdb111..b604d8501eb8b 100644
--- a/src/content/docs/en/guides/endpoints.mdx
+++ b/src/content/docs/en/guides/endpoints.mdx
@@ -47,6 +47,8 @@ import type { APIRoute } from "astro";
export const GET = (async ({ params, request }) => { /* ... */ }) satisfies APIRoute;
```
+Note that endpoints whose URLs include a file extension (e.g. `src/pages/sitemap.xml.ts`) can only be accessed without a trailing slash (e.g. `/sitemap.xml`), regardless of your [`build.trailingSlash`](/en/reference/configuration-reference/#trailingslash) configuration.
+
### `params` and Dynamic routing
Endpoints support the same [dynamic routing](/en/guides/routing/#dynamic-routes) features that pages do. Name your file with a bracketed parameter name and export a [`getStaticPaths()` function](/en/reference/routing-reference/#getstaticpaths). Then, you can access the parameter using the `params` property passed to the endpoint function:
diff --git a/src/content/docs/en/guides/environment-variables.mdx b/src/content/docs/en/guides/environment-variables.mdx
index af2b99b85fd9e..de3a31e6ee9e5 100644
--- a/src/content/docs/en/guides/environment-variables.mdx
+++ b/src/content/docs/en/guides/environment-variables.mdx
@@ -58,7 +58,6 @@ Astro includes a few environment variables out of the box:
- `import.meta.env.DEV`: `true` if your site is running in development; `false` otherwise. Always the opposite of `import.meta.env.PROD`.
- `import.meta.env.BASE_URL`: The base URL your site is being served from. This is determined by the [`base` config option](/en/reference/configuration-reference/#base).
- `import.meta.env.SITE`: This is set to [the `site` option](/en/reference/configuration-reference/#site) specified in your project's `astro.config`.
-- `import.meta.env.ASSETS_PREFIX`: The prefix for Astro-generated asset links if the [`build.assetsPrefix` config option](/en/reference/configuration-reference/#buildassetsprefix) is set. This can be used to create asset links not handled by Astro.
Use them like any other environment variable.
@@ -320,7 +319,7 @@ envField.enum({
})
```
-For a complete list of validation fields, see the [`envField` API reference](/en/reference/configuration-reference/#envschema).
+For a complete list of validation fields, see the [`envField` API reference](/en/reference/modules/astro-config/#envfield).
### Retrieving secrets dynamically
diff --git a/src/content/docs/en/guides/fonts.mdx b/src/content/docs/en/guides/fonts.mdx
index a71078b9c8909..0dffeb52019bf 100644
--- a/src/content/docs/en/guides/fonts.mdx
+++ b/src/content/docs/en/guides/fonts.mdx
@@ -8,129 +8,309 @@ description: >-
i18nReady: true
---
import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro';
-import { Steps } from '@astrojs/starlight/components'
+import ReadMore from '~/components/ReadMore.astro';
+import { Steps, Tabs, TabItem } from '@astrojs/starlight/components';
-This guide will show you how to add web fonts to your project and use them in your components.
+This guide will show you how to add [web fonts](https://developer.mozilla.org/en-US/docs/Learn/CSS/Styling_text/Web_fonts) to your project and use them in your components.
-:::tip[Experimental Fonts API]
-Learn about Astro's [experimental Fonts API](/en/reference/experimental-flags/fonts/) that allows you to use fonts from your filesystem and various font providers through a unified, fully customizable, and type-safe API.
-:::
+Astro provides a way to use fonts from your filesystem and various font providers (e.g. Fontsource, Google) through a unified, [fully customizable](/en/reference/configuration-reference/#fonts), and type-safe API.
-## Using a local font file
+Web fonts can impact page performance at both load time and rendering time. This API helps you keep your site performant with automatic [web font optimizations](https://web.dev/learn/performance/optimize-web-fonts) including preload links, optimized fallbacks, and opinionated defaults. [See common usage examples](#examples).
-This example will demonstrate adding a custom font using the font file `DistantGalaxy.woff`.
+The Fonts API focuses on performance and privacy by downloading and caching fonts so they're served from your site. This can avoid sending user data to third-party sites, and also ensures that a consistent set of fonts is available to all your visitors.
+
+## Configuring custom fonts
+
+Registering custom fonts for your Astro project is done through [the `fonts` option](/en/reference/configuration-reference/#fonts) in your Astro config.
+
+For each font you want to use, you must specify its [name](/en/reference/configuration-reference/#fontname), a [CSS variable](/en/reference/configuration-reference/#fontcssvariable), and an Astro font provider.
+
+Astro provides [built-in support for the most popular font providers](/en/reference/font-provider-reference/#built-in-providers): Adobe, Bunny, Fontshare, Fontsource, Google, Google Icons and NPM, as well as for using your own local font files. Additionally, you can [further customize your font configuration](#granular-font-configuration) to optimize performance and visitor experience.
+
+
+### Using a local font file
+
+This example will demonstrate adding a custom font using the font file `DistantGalaxy.woff2`.
-1. Add your font file to `public/fonts/`.
-2. Add the following `@font-face` statement to your CSS. This could be in a global `.css` file you import, a `
+
+
+1. Find the font you want to use in [Fontsource's catalog](https://fontsource.org/). This example will use [Roboto](https://fontsource.org/fonts/roboto).
+
+2. Create a new font family in your Astro config file using the [Fontsource provider](/en/reference/font-provider-reference/#fontsource):
+
+ ```js title="astro.config.mjs"
+ import { defineConfig, fontProviders } from "astro/config";
+
+ export default defineConfig({
+ fonts: [{
+ provider: fontProviders.fontsource(),
+ name: "Roboto",
+ cssVariable: "--font-roboto",
+ }]
+ });
```
+3. Your font is now configured and ready to be [added to your page head](#applying-custom-fonts) so that it can be used in your project.
+
-## Using Fontsource
+## Applying custom fonts
-The [Fontsource](https://fontsource.org/) project simplifies using Google Fonts and other open-source fonts. It provides npm modules you can install for the fonts you want to use.
+After [a font is configured](#configuring-custom-fonts), it must be added to your page head with an identifying CSS variable. Then, you can use this variable when defining your page styles.
-1. Find the font you want to use in [Fontsource's catalog](https://fontsource.org/). This example will use [Twinkle Star](https://fontsource.org/fonts/twinkle-star).
-2. Install the package for your chosen font.
-
-
-
- ```shell
- npm install @fontsource/twinkle-star
- ```
-
-
- ```shell
- pnpm add @fontsource/twinkle-star
- ```
-
-
- ```shell
- yarn add @fontsource/twinkle-star
- ```
-
-
-
- :::tip
- You'll find the correct package name in the “Quick Installation” section of each font page on Fontsource's website. It will start with `@fontsource/` or `@fontsource-variable/` followed by the name of the font.
- :::
-
-3. Import the font package in the component where you want to use the font. Usually, you will want to do this in a common layout component to make sure the font is available across your site.
-
- The import will automatically add the necessary `@font-face` rules needed to set up the font.
-
- ```astro title="src/layouts/BaseLayout.astro"
+
+1. Import and include the [``](/en/reference/modules/astro-assets/#font-) component with the required `cssVariable` property in the head of your page, usually in a dedicated `Head.astro` component or in a [layout](/en/basics/layouts/) component directly:
+
+ ```astro title="src/layouts/Layout.astro" ins={2,7}
---
- import '@fontsource/twinkle-star';
+ import { Font } from "astro:assets";
---
+
+
+
+
+
+
+
+
+
```
-4. Use the font's name as shown in the `body` example on its Fontsource page as the `font-family` value. This will work anywhere you can write CSS in your Astro project.
+2. In any page rendered with that layout, including the layout component itself, you can now define styles with your font's `cssVariable` to apply your custom font.
- ```css
- h1 {
- font-family: "Twinkle Star", cursive;
- }
+ In the following example, the `
` heading will have the custom font applied, while the paragraph `
` will not.
+
+ ```astro title="src/pages/example.astro" ins={10-12}
+ ---
+ import Layout from "../layouts/Layout.astro";
+ ---
+
+
In a galaxy far, far away...
+
+
Custom fonts make my headings much cooler!
+
+
+
```
-
-To optimize your website’s rendering times, you may want to preload fonts that are essential for the initial page display.
-See the [Fontsource guide to preloading fonts](https://fontsource.org/docs/getting-started/preload) for more information and usage.
+
## Register fonts in Tailwind
-If you are using [Tailwind](/en/guides/styling/#tailwind), you can use either of the previous methods on this page to install your font, with some modifications. You can either add an [`@font-face` statement for a local font](#using-a-local-font-file) or use [Fontsource's `import` strategy](#using-fontsource) to install your font.
+If you are using [Tailwind](/en/guides/styling/#tailwind) for styling, you will not apply your styles with the `font-face` CSS property.
-To register your font in Tailwind:
+Instead, after [configuring your custom font](#configuring-custom-fonts) and [adding it to your page head](#applying-custom-fonts), you will need to update your Tailwind configuration to register your font:
-
-1. Follow either of the guides above, but skip the final step of adding `font-family` to your CSS.
-2. Add the typeface name to `src/styles/global.css`.
+
- This example adds `Inter` to the sans-serif font stack.
+
- ```css title="src/styles/global.css" ins={3-5}
- @import 'tailwindcss';
+ ```css title="src/styles/global.css" ins={4} ins="inline"
+ @import "tailwindcss";
- @theme {
- --font-sans: 'Inter', 'sans-serif';
+ @theme inline {
+ --font-sans: var(--font-roboto);
}
```
- Now, all sans-serif text (the default with Tailwind) in your project will use your chosen font and the `font-sans` class will also apply the Inter font.
-
+
+
+
+
+ ```js title="tailwind.config.mjs" ins={6-8}
+ /** @type {import("tailwindcss").Config} */
+ export default {
+ content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
+ theme: {
+ extend: {},
+ fontFamily: {
+ sans: ["var(--font-roboto)"]
+ }
+ },
+ plugins: []
+ };
+ ```
+
+
+
+
See [Tailwind's docs on adding custom font families](https://tailwindcss.com/docs/font-family#using-custom-values) for more information.
-## More resources
-- Learn how web fonts work in [MDN's web fonts guide](https://developer.mozilla.org/en-US/docs/Learn/CSS/Styling_text/Web_fonts).
-- Generate CSS for your font with [Font Squirrel's Webfont Generator](https://www.fontsquirrel.com/tools/webfont-generator).
+## Accessing font data programmatically
+
+The [`fontData`](/en/reference/modules/astro-assets/#fontdata) object allows you to retrieve lower-level font family data programmatically. For example, you can use it in an [API Route](/en/guides/endpoints/#server-endpoints-api-routes) to generate OpenGraph images using [satori](https://github.com/vercel/satori), combined with proper [formats](/en/reference/configuration-reference/#fontformats) configuration:
+
+```tsx title="src/pages/og.png.ts" {2} "fontData[\"--font-roboto\"]"
+import type{ APIRoute } from "astro";
+import { fontData } from "astro:assets";
+import satori from "satori";
+import { html } from "satori-html";
+
+export const GET: APIRoute = async (context) => {
+ const data = fontData["--font-roboto"];
+
+ const svg = await satori(
+ html`
hello, world
`,
+ {
+ width: 600,
+ height: 400,
+ fonts: [
+ {
+ name: "Roboto",
+ data: await fetch(
+ new URL(data[0].src[0].url, context.url.origin),
+ ).then((res) => res.arrayBuffer()),
+ weight: 400,
+ style: "normal",
+ },
+ ],
+ },
+ );
+
+ // ...
+}
+```
+
+## Granular font configuration
+
+A font family is defined by a combination of properties such as weights and styles (e.g. `weights: [500, 600]` and `styles: ["normal", "bold"]`), but you may want to download only certain combinations of these.
+
+For greater control over which font files are downloaded, you can specify the same font (ie. with the same `cssVariable`, `name`, and `provider` properties) multiple times with different combinations. Astro will merge the results and download only the required files. For example, it is possible to download normal `500` and `600` while downloading only italic `500`:
+
+```js title="astro.config.mjs"
+import { defineConfig, fontProviders } from "astro/config";
+
+export default defineConfig({
+ fonts: [
+ {
+ name: "Roboto",
+ cssVariable: "--roboto",
+ provider: fontProviders.google(),
+ weights: [500, 600],
+ styles: ["normal"]
+ },
+ {
+ name: "Roboto",
+ cssVariable: "--roboto",
+ provider: fontProviders.google(),
+ weights: [500],
+ styles: ["italic"]
+ }
+ ]
+});
+```
+
+## Caching
+
+The Fonts API caching implementation was designed to be practical in development and efficient in production. During builds, font files are copied to the `_astro/fonts` output directory, so they can benefit from HTTP caching of static assets (usually a year).
+
+To clear the cache in development, remove the `.astro/fonts` directory. To clear the build cache, remove the `node_modules/.astro/fonts` directory.
+
+## Examples
+
+Astro's font feature is based on flexible configuration options. Your own project's font configuration may look different from simplified examples, so the following are provided to show what various font configurations might look like when used in production.
+
+```js title="astro.config.mjs"
+import { defineConfig, fontProviders } from "astro/config";
+
+export default defineConfig({
+ fonts: [
+ {
+ name: "Roboto",
+ cssVariable: "--font-roboto",
+ provider: fontProviders.google(),
+ // Default included:
+ // weights: [400] ,
+ // styles: ["normal", "italics"],
+ // subsets: ["latin"],
+ // fallbacks: ["sans-serif"],
+ // formats: ["woff2"],
+ },
+ {
+ name: "Inter",
+ cssVariable: "--font-inter",
+ provider: fontProviders.fontsource(),
+ // Specify weights that are actually used
+ weights: [400, 500, 600, 700],
+ // Specify styles that are actually used
+ styles: ["normal"],
+ // Download only font files for characters used on the page
+ subsets: ["latin", "cyrillic"],
+ // Download more font formats
+ formats: ["woff2", "woff"],
+ },
+ {
+ name: "JetBrains Mono",
+ cssVariable: "--font-jetbrains-mono",
+ provider: fontProviders.fontsource(),
+ // Download only font files for characters used on the page
+ subsets: ["latin", "latin-ext"],
+ // Use a fallback font family matching the intended appearance
+ fallbacks: ["monospace"],
+ },
+ {
+ name: "Poppins",
+ cssVariable: "--font-poppins",
+ provider: fontProviders.local(),
+ options: {
+ // Weight and style are not specified so Astro
+ // will try to infer them for each variant
+ variants: [
+ {
+ src: [
+ "./src/assets/fonts/Poppins-regular.woff2",
+ "./src/assets/fonts/Poppins-regular.woff",
+ ]
+ },
+ {
+ src: [
+ "./src/assets/fonts/Poppins-bold.woff2",
+ "./src/assets/fonts/Poppins-bold.woff",
+ ]
+ },
+ ]
+ }
+ }
+ ],
+});
+```
diff --git a/src/content/docs/en/guides/framework-components.mdx b/src/content/docs/en/guides/framework-components.mdx
index 6d5f6d5c1260b..48227f214c438 100644
--- a/src/content/docs/en/guides/framework-components.mdx
+++ b/src/content/docs/en/guides/framework-components.mdx
@@ -191,7 +191,7 @@ For Svelte and Vue these slots can be referenced using a `` element with t
Inside of an Astro file, framework component children can also be hydrated components. This means that you can recursively nest components from any of these frameworks.
-```astro title="src/pages/nested-components.astro" {10-11}
+```astro title="src/pages/nested-components.astro" {9-10}
---
import MyReactSidebar from '../components/MyReactSidebar.jsx';
import MyReactButton from '../components/MyReactButton.jsx';
diff --git a/src/content/docs/en/guides/images.mdx b/src/content/docs/en/guides/images.mdx
index 251cf3249509d..8a1ceb4d90939 100644
--- a/src/content/docs/en/guides/images.mdx
+++ b/src/content/docs/en/guides/images.mdx
@@ -51,7 +51,7 @@ All native HTML tags, including `` and `