diff --git a/astro.sidebar.ts b/astro.sidebar.ts
index ed4990f336ba0..c2870189bb783 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',
@@ -145,15 +146,9 @@ export const sidebar = [
'reference/experimental-flags',
'reference/experimental-flags/csp',
'reference/experimental-flags/fonts',
- 'reference/experimental-flags/live-content-collections',
'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/legacy-flags',
diff --git a/public/_headers b/public/_headers
index f56759cc14415..015d6749500c7 100644
--- a/public/_headers
+++ b/public/_headers
@@ -2,3 +2,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/tutorial/6-islands/4.mdx b/src/content/docs/de/tutorial/6-islands/4.mdx
index cd7c4fc0c4767..7c60d35154716 100644
--- a/src/content/docs/de/tutorial/6-islands/4.mdx
+++ b/src/content/docs/de/tutorial/6-islands/4.mdx
@@ -119,7 +119,8 @@ Aktualisiere Astro und alle Integrationen auf die neuesten Versionen, indem du i
```ts title="src/content.config.ts"
import { glob } from "astro/loaders";
- import { z, defineCollection } from "astro:content";
+ import { defineCollection } from "astro:content";
+ import { z } from "astro/zod";
const blog = defineCollection({
loader: glob({ pattern: '**/[^_]*.md', base: "./src/blog" }),
@@ -148,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/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/keystatic.mdx b/src/content/docs/en/guides/cms/keystatic.mdx
index d8fdc1e12276a..6e94b455e57d4 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
diff --git a/src/content/docs/en/guides/content-collections.mdx b/src/content/docs/en/guides/content-collections.mdx
index d85093421e967..462b9ab8cf3ac 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.
-```json title="tsconfig.json" ins={5} {6}
+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={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
-
-Individual collections use `defineCollection()` to configure:
-- a `loader` for a data source (required)
-- a `schema` for type safety (optional, but highly recommended!)
+## Defining build-time content collections
-### The collection config file
+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.
-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.
+
+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.
-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.
+## Build-time collection loaders
-#### Built-in loaders
+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.
-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.
+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 [`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.
+### The `glob()` loader
-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. Use this loader when your data file can be parsed as an array of objects.
+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.
-```ts title="src/content.config.ts" {6,10}
+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/#id) 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).
-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.
-
-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.
-
-:::tip
-Find community-built and third-party loaders in the [Astro integrations directory](https://astro.build/integrations/?search=&categories%5B%5D=loaders).
-:::
-
-##### Inline loaders
+### Custom build-time loaders
-You can define a loader inline, inside your collection, as an async function that returns an array of entries.
+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.
-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,18 +386,18 @@ 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(),
@@ -301,38 +421,17 @@ relatedPosts:
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
-
-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
+## Querying build-time 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
-```astro title="src/pages/blog/post-1.astro" {5,8}
+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" {2,9}
---
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`)
-```astro title="src/pages/blog/welcome.astro"
+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/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
+
+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:
-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).
+- [`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,210 @@ 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!
:::
-## Collection JSON Schemas
+## 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).
+:::
+
+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 { z, defineLiveCollection } from 'astro:content';
+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/#rendered-1), 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 +876,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 +890,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 +906,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 +919,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 +933,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/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 3329a1bd64dad..51f9c9c65904b 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: APIRoute = async ({ params, request }) => {...}
```
+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..24d3eba46ba38 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.
diff --git a/src/content/docs/en/guides/images.mdx b/src/content/docs/en/guides/images.mdx
index 58ed5107a8a09..fa38b308a1ecb 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 `