diff --git a/src/i18n/en/nav.ts b/src/i18n/en/nav.ts
index 43dc7803353fa..988f2bd8997ad 100644
--- a/src/i18n/en/nav.ts
+++ b/src/i18n/en/nav.ts
@@ -63,7 +63,7 @@ export default [
},
{ text: 'Authoring Content', slug: 'guides/content', key: 'guides/content' },
{
- text: 'Content Collections (Experimental)',
+ text: 'Content Collections',
slug: 'guides/content-collections',
key: 'guides/content-collections',
},
diff --git a/src/pages/en/core-concepts/astro-pages.mdx b/src/pages/en/core-concepts/astro-pages.mdx
index 9d0377aa5ec09..80ebf7ea51ecb 100644
--- a/src/pages/en/core-concepts/astro-pages.mdx
+++ b/src/pages/en/core-concepts/astro-pages.mdx
@@ -20,7 +20,7 @@ Astro supports the following file types in the `src/pages/` directory:
Astro leverages a routing strategy called **file-based routing**. Each file in your `src/pages/` directory becomes an endpoint on your site based on its file path.
-A single file can also generate multiple pages using [dynamic routing](/en/core-concepts/routing/#dynamic-routes). This allows you to create pages even if your content lives outside of the special `/pages/` directory, such as in a [content collection](/en/guides/content-collections/#rendering-entry-content) or a [CMS](/en/guides/cms/).
+A single file can also generate multiple pages using [dynamic routing](/en/core-concepts/routing/#dynamic-routes). This allows you to create pages even if your content lives outside of the special `/pages/` directory, such as in a [content collection](/en/guides/content-collections/) or a [CMS](/en/guides/cms/).
📚 Read more about [Routing in Astro](/en/core-concepts/routing/).
diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx
index 918d356d2cd80..59f86628a9f4c 100644
--- a/src/pages/en/guides/content-collections.mdx
+++ b/src/pages/en/guides/content-collections.mdx
@@ -16,166 +16,196 @@ import TypeScriptSettingTabs from '~/components/tabs/TypeScriptSettingTabs.astro
-Content collections help organize your Markdown or MDX and type-check your frontmatter with schemas. Collections may be helpful if you:
+**Content collections** are the best way to work with Markdown and MDX in any Astro project. Content collections are a feature of Astro that help manage your content files in a project. Collections help to organize your content, validate your frontmatter, and provide automatic TypeScript type-safety for all of your content.
-- **Plan to use Markdown content in multiple areas** of your site (landing pages, footers, navigation, etc).
-- **Want Astro to enforce frontmatter fields,** and fail if fields are missing (e.g. every blog post should have a title and description).
-## Getting started
+## What are Content Collections?
-First, run the [`astro dev`](/en/reference/cli-reference/#astro-dev), [`astro build`](/en/reference/cli-reference/#astro-build), or [`astro sync`](/en/reference/cli-reference/#astro-sync) commands to generate types. This will make the `astro:content` module available for [querying](#querying-content-collections) and [configuring](#configuring-collections) your content collections.
+A **content collection** is any directory inside the reserved `src/content` project directory, such as `src/content/newsletter` and `src/content/blog`. Only content collections are allowed inside the `src/content` directory. This directory cannot be used for anything else.
-### Update TypeScript configuration
+A **content entry** is any piece of content stored inside of your content collection directory. Content entries are stored as either Markdown (`.md`) or MDX (`.mdx`) files. You can use any filename you want, but we recommend using a consistent naming scheme (lower-case, dashes instead of spaces) to make it easier to find and organize your content.
-To benefit from the full TypeScript and autocompletion features of [using schemas with your collections](#defining-a-collection-schema), you may also need to update `tsconfig.json`. Follow the instructions below based on your Astro project's current [TypeScript configuration](/en/guides/typescript/#setup).
+
+- src/content/
+ - **newsletter/** the "newsletter" collection
+ - week-1.md a collection entry
+ - week-2.md a collection entry
+ - week-3.md a collection entry
+
-
-
- If you are extending Astro's `base` TypeScript config (e.g. you chose "relaxed" TypeScript when creating your project with `create astro`), add `"strictNullChecks": true` under `compilerOptions`.
-
- Or, you can change your `base` TypeScript config to `strict` or `strictest` to use a broader set of stricter type-checks in your project.
+Once you have a collection, you can start [querying your content](#querying-collections) using Astro's built-in content APIs.
- ```json title="tsconfig.json" ins={3-5} "base"
- {
- "extends": "astro/tsconfigs/base",
- "compilerOptions": {
- "strictNullChecks": true
- }
- }
- ```
-
-
- No update necessary!
-
- `"strictNullChecks": true` is already enabled in your `strict` or `strictest` configuration.
-
- ```json title="tsconfig.json" "strict" "strictest"
- {
- "extends": "astro/tsconfigs/strict" // or `strictest`
- }
- ```
-
-
- If you have a custom TypeScript configuration that does not include `strict: true`, add `"strictNullChecks": true` under `compilerOptions` if it does not already exist.
-
- ```json title="tsconfig.json" ins={3}
- {
- "compilerOptions": {
- "strictNullChecks": true
- }
- }
- ```
-
-
+### The ".astro" Directory
-### Set up the `.astro` directory
+Astro stores important metadata for content collections in an `.astro` directory in your project. No action is needed on your part to maintain or update this directory. You are encouraged to ignore it entirely while working in your project.
-Astro generates TypeScript types from your content collections in the `.astro` directory. These types will be updated anytime you run the [`astro dev`](/en/reference/cli-reference/#astro-dev), [`astro build`](/en/reference/cli-reference/#astro-build), or [`astro sync`](/en/reference/cli-reference/#astro-sync) commands.
+The `.astro` directory will be updated for you automatically anytime you run the [`astro dev`](/en/reference/cli-reference/#astro-dev), [`astro build`](/en/reference/cli-reference/#astro-build) commands. You can run [`astro sync`](/en/reference/cli-reference/#astro-sync) at any time to update the `.astro` directory manually.
:::tip
-If you're using Git for version control, we recommend ignoring this generated directory by adding `.astro` to your `.gitignore`.
-:::
-
-These commands will also generate a [reference path](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-path-) to include `.astro` types in your project. A [`src/env.d.ts` file](/en/guides/typescript/#setup) will be created for you if one does not exist, and the following will be added:
+If you're using Git for version control, we recommend ignoring the `.astro` directory by adding `.astro` to your `.gitignore`. This tells Git to ignore this directory and any files inside of it.
-```ts title="src/env.d.ts"
-// using a relative path `../` to the `.astro` directory
-///
+```bash
+echo ".astro" >> .gitignore
```
+:::
-## The content directory
-
-Astro treats the `src/content/` directory as special. This is where **collections** (folders) of Markdown/MDX **entries** (files) can be stored, with a single configuration file to define each collection's **schema** (frontmatter data types and shape). Files other than your `.md`/`.mdx` content are not permitted inside `src/content/`.
-## Collections
-A collection is a directory in `src/content/` containing Markdown or MDX files. Every Markdown or MDX file in `src/content/` **must** belong to a collection directory, since Astro [provides built-in functions](#querying-content-collections) for querying your content by the collection directory name.
+### Organizing with multiple collections
-Content within a collection should share the same frontmatter shape and types. You can optionally enforce these types [by configuring a schema](/en/guides/content-collections/#defining-a-collection-schema).
+If two files represent different kinds of content (e.g. a blog post and an author profile), they most likely belong in different collections. This is important because many features (frontmatter validation, automatic TypeScript type-safety) require that all entries in a collection share a similar frontmatter structure.
-To create a collection, add a new directory to `src/content/`. Then, add Markdown or MDX entries that share frontmatter properties. The following example shows two collections: `blog` and `newsletter`.
+If you find yourself working with different types of content, you should create multiple collection to represent each type. You can create as many different collections in your project as you'd like.
- src/content/
- - **blog/** All blog posts have the same frontmatter properties
- - columbia.md
- - endeavour.md
- - enterprise.md
- - **newsletter/** All newsletters have the same frontmatter properties
+ - **newsletter/**
- week-1.md
- week-2.md
- week-3.md
+ - **authors/** split different content types into new collections
+ - grace-hopper.md
+ - alan-turing.md
+ - batman.md
-### Collections with nested directories
-Collections are **top-level folders** within `src/content/`. You cannot nest collections, but you may use nested directories within a collection to better organize a collection's content. All nested directories will share the same schema defined for the top-level collection.
+### Organizing with subdirectories
-For example, you can use this structure for internationalization:
+A content collection is always a top-level folder inside of the `src/content/` directory. You cannot nest one collection inside of another. However, you can use subdirectories to organize your content within a collection.
+
+For example, you can use the following directory structure to organize i18n translations within a single `docs` collection. When you query this collection, you'll be able to filter the result by language using the file path.
- src/content/
- - docs/ docs schema applies to all nested directories
- - en/
- - es/
- - ...
+ - docs/ this collection uses subdirectories to organize by language
+ - **en/**
+ - **es/**
+ - **de/**
-## Configuring collections
+## Defining Collections
-You can configure your content collections with an optional `src/content/config.ts` file (`.js` and `.mjs` extensions are also supported).
+:::note
+The `src/content/config.ts` file is optional. However, choosing not to define your collections will disable some of their best features like frontmatter schema validation or automatic TypeScript typings.
+:::
-This file currently allows you to [define a collection schema](#defining-a-collection-schema), and to [create custom slugs](#custom-entry-slugs) for your collections.
+To get the most out of your content collections, 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 automatically load and use to configure your content collections.
-### Defining a collection schema
-Schemas are an optional way to enforce frontmatter types in a collection. Astro uses [Zod](https://github.com/colinhacks/zod) to validate your frontmatter with schemas in the form of [Zod objects](https://github.com/colinhacks/zod#objects).
+```ts
+// src/content/config.ts
+// 1. Import utilities from `astro:content`
+import { defineCollection } from 'astro:content';
+// 2. Define your collection(s)
+const blogCollection = defineCollection({ /* ... */ });
+// 3. Export a single `collections` object to register your collection(s)
+// This key should match your collection directory name in "src/content"
+export const collections = {
+ 'blog': blogCollection,
+};
+```
-To configure schemas, create a `src/content/config.ts` file (`.js` and `.mjs` extensions are also supported). This file should:
-1. Import the `defineCollection` and `z` utilities from `astro:content`.
-2. Define a `schema` for each collection.
-2. Export a single `collections` object, with each object key corresponding to the collection's folder name.
+### Setting up TypeScript
-For example, say you maintain two collections: one for release announcements and one for blog content. Your entries at `src/content/releases/` should include a `title` and `version`. Your `src/content/engineering-blog/` collection entries should have a `title`, list of `tags`, and an optional `image` URL.
+To get the most out of content collections in Astro, you may need to update your `tsconfig.json` to enable `strictNullChecks`. This change is only required if you **do not** already extend Astro's `strict` or `strictest` recommended TypeScript settings in your `tsconfig.json` file.
-You can specify the expected shape of your frontmatter with the `schema` field of `defineCollection`:
+```json title="tsconfig.json" ins={5}
+{
+ // Note: No change needed if you use "astro/tsconfigs/strict" or "astro/tsconfigs/strictest"
+ "extends": "astro/tsconfigs/base",
+ "compilerOptions": {
+ "strictNullChecks": true
+ }
+}
+```
+
+### Defining a collection schema
+
+Schemas enforce consistent frontmatter within a collection. A schema **guarantees** that your frontmatter 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.
+
+Schemas also power Astro's automatic TypeScript typings for your content. When you define a schema for your collection, Astro will automatically generate and apply a TypeScript interface to it. The result is full TypeScript support when you query your collection, including proprety autocompletion and type-checking.
+
+To create your first content schema, create a `src/content/config.ts` file if one does not already exist (`.js` and `.mjs` extensions are also supported). This file should:
+
+1. Import the proper utilities from `astro:content`.
+2. Define each collection that you'd like to validate with a schema.
+3. Export a single `collections` object to register your collections.
```ts
// src/content/config.ts
+// 1. Import utilities from `astro:content`
import { z, defineCollection } from 'astro:content';
-
-const releases = defineCollection({
- schema: z.object({
- title: z.string(),
- version: z.number(),
- }),
-});
-
-const engineeringBlog = defineCollection({
+// 2. Define a schema for each collection you'd like to validate.
+const blogCollection = defineCollection({
schema: z.object({
title: z.string(),
tags: z.array(z.string()),
image: z.string().optional(),
}),
});
+// 3. Export a single `collections` object to register your collection(s)
+export const collections = {
+ 'blog': blogCollection,
+};
+```
+
+### Defining multiple collections
+
+You can use `defineCollection()` as many times as you want to create multiple schemas. All collections must be exported from inside the single `collections` object.
+
+```ts
+// src/content/config.ts
+export const collections = {
+ 'blog': defineCollection({ /* ... */ }),
+ 'newsletter': defineCollection({ /* ... */ }),
+ 'profile-authors': defineCollection({ /* ... */ }),
+};
+```
+
+As your project grows, you are also free to reorganize your codebase and move logic out of the `src/content/config.ts` file. Defining your schemas seperately can be useful for reusing schemas across multiple collections and sharing schemas with other parts of your project.
+
+```ts
+// src/content/config.ts
+// 1. Import your utilities and schemas
+import { defineCollection } from 'astro:content';
+import {blogSchema, newsletterSchema} from '../schemas';
+// 2. Define your collections
+const blogCollection = defineCollection({ schema: blogSchema });
+const newsletterCollection = defineCollection({ schema: newsletterSchema });
+// 3. Export multiple collections to register them
+export const collections = {
+ 'blog': blogCollection,
+ 'newsletter': newsletterCollection,
+};
+```
+
+### Using third-party collection schemas
+
+You can import collection schemas from anywhere, including external npm packages. This can be useful when working with themes and libraries that provide their own collection schemas for you to use.
+
+```ts
+// src/content/config.ts
+import {blogSchema} from 'my-blog-theme';
+const blogCollection = defineCollection({ schema: blogSchema });
+// Export the blog collection, using an external schema from 'my-blog-theme'
export const collections = {
- releases: releases,
- // Don't forget 'quotes' for collection names containing dashes
- 'engineering-blog': engineeringBlog,
+ 'blog': blogCollection,
};
```
-#### Schema data types with Zod
-Markdown and MDX frontmatter can contain booleans, strings, numbers, objects, and arrays. When defining a schema, you must include every frontmatter property along with its data type. To define and validate this schema, we use a library called [Zod](https://github.com/colinhacks/zod), which is available via the `z` import.
+### 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 frontmatter within a collection *and* provide automatic TypeScript types when you go to query content from inside your project.
-You can extend any of these types with `.optional()` if a frontmatter property is not always required or `.defaultValue(value)` to provide a value to use when the property is not set in frontmatter. If only a limited set of values is valid for a property, you can specify these using [the `.enum()` method](https://github.com/colinhacks/zod#zod-enums).
+To use Zod in Astro, import the `z` utility from `"astro:content"`. This is a re-export of the Zod library, and it supports all of the features of Zod. See [Zod’s README](https://github.com/colinhacks/zod) for complete documentation on how Zod works and what features are available.
-The following schema illustrates each of these data types in use:
```ts
+// Example: A cheatsheet of many common Zod datatypes
import { z, defineCollection } from 'astro:content';
defineCollection({
@@ -187,290 +217,252 @@ defineCollection({
src: z.string(),
alt: z.string(),
}),
- tags: z.array(z.string()), // An array of strings
- footnote: z.string().optional(),
author: z.string().default('Anonymous'),
language: z.enum(['en', 'es']),
+ tags: z.array(z.string()),
+ // An optional frontmatter property. Very common!
+ footnote: z.string().optional(),
+ // Convert a standard date-string into a `Date` object
+ publishDate: z.string().transform(str => new Date(str)),
+ // Advanced: Validate that the string is also an email
+ authorContact: z.string().email(),
+ // Advanced: Validate that the string is also a URL
+ canonicalURL: z.string().url(),
})
})
```
-#### Advanced schema features
+### Defining custom slugs
-You can use all of Zod’s properties and methods with content schemas. This includes transforming a frontmatter value into another value, checking the shape of string values with built-in regexes, and more.
+Every content entry generates a URL-friendly `slug` property from its file `id`. The slug is used to query the entry directly from your collection. It is also useful when creating new pages and URLs from your content.
-```ts
-// Allow only strings representing email addresses
-authorContact: z.string().email(),
-// Allow URL strings only (e.g. `https://example.com`)
-canonicalURL: z.string().url(),
-// Parse publishDate as a browser-standard `Date` object
-publishDate: z.string().transform(str => new Date(str)),
-```
+To customize the generated `slug` value, you can manually override Astro's default behavior by providing a `slug()` function in `defineCollection()`. Whatever value you return from this function will be used as the new `slug` property for each entry.
-📚 See [Zod’s documentation](https://github.com/colinhacks/zod) for a complete list of features.
-
-### Custom entry slugs
+```ts {4-6}
+// Example: Override the default entry slug with a custom value.
+const blogCollection = defineCollection({
+ slug: ({ id, defaultSlug }) => {
+ return myCustomSlugProcessor(id) || defaultSlug;
+ },
+});
+```
-By default, Astro will generate a `slug` for each content entry based on its `id` parsed by [github-slugger](https://github.com/Flet/github-slugger).
+For performance reasons, the `slug()` function does not have access to your content's body or frontmatter data. If you need these values to generate your slug, you should do that directly inside the `src/pages` Astro component responsible for rendering your content. See [Generating custom routes](#generating-custom-routes) for more details.
-If you want to generate custom slugs for each entry, you can provide a `slug()` function in `defineCollection()`. Your `slug()` function can use the entry ID, default slug, parsed frontmatter (as `data`), and the raw body of the entry to generate the slug.
-For example, to use a frontmatter `permalink` property as the slug for your blog pages instead of the file path, you can use the following `slug()` function. For extra safety, conditionally return the entry's `defaultSlug` as a fallback.
+## Querying Collections
-```ts {5-9}
-// src/content/config.ts
-import { defineCollection, z } from 'astro:content';
+Astro provides two functions to query a collection and return one (or more) content entries: [`getCollection()`](/en/reference/api-reference/#getcollection) and [`getEntryBySlug()`](/en/reference/api-reference/#getentrybyslug).
-const blog = defineCollection({
- slug: ({ id, defaultSlug, data, body }) => {
- // Use `permalink` from the entry’s frontmatter as the slug, if it exists.
- // Otherwise, fall back to the default slug.
- return data.permalink || defaultSlug;
- },
- schema: z.object({
- permalink: z.string().optional(),
- }),
-});
-
-export const collections = { blog };
+```js
+import { getCollection, getEntryBySlug } from 'astro:content';
+// Get all entries from a collection. Requires the name of the collection as an argument.
+const allBlogPosts = await getCollection('blog');
+// Get a single entry from a collection. Requires the name of the collection and the entry's slug as arguments.
+const oneBlogPost = await getEntryBySlug('blog', 'enterprise');
```
-## Querying content collections
-
-Astro provides two functions to query collections:
+Both functions return content entries as defined by the [`CollectionEntry`](/en/reference/api-reference/#collection-entry-type) type.
-### `getCollection()`
+#### Filtering collection queries
-`getCollection()` returns multiple entries in a collection. It requires the name of a `collection` as a parameter. By default, it returns all items in the collection.
+`getCollection()` takes an optional "filter" callback that allows you to filter your query based on an entry's `id`, `slug`, or `data` (frontmatter) properties.
-It can also take a second, optional parameter: a filter function based on schema properties. This allows you to query for only some items in a collection based on `id`, `slug`, or frontmatter values via the `data` object.
+You can use this to filter by any content criteria you like. For example, you can filter by frontmatter properties like `draft` to prevent any draft blog posts from publishing to your blog:
-```astro
----
+```js
+// Example: Filter content entries with `draft: true` frontmatter
import { getCollection } from 'astro:content';
-
-// Get all `src/content/blog/` entries
-const allBlogPosts = await getCollection('blog');
-
-// Only return posts with `draft: true` in the frontmatter
-const draftBlogPosts = await getCollection('blog', ({ data }) => {
- return data.draft === true;
+const draftBlogEntries = await getCollection('blog', ({ data }) => {
+ return data.draft !== true;
});
----
```
-#### Querying nested directories
-
-The filter function can also be used to query for 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:
+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:
-```astro
----
+```js
+// Example: Filter entries by sub-directory in the collection
import { getCollection } from 'astro:content';
-const enDocs = await getCollection('docs', ({ id }) => {
- // Return all entries in `src/content/docs/en/`
+const englishDocsEntries = await getCollection('docs', ({ id }) => {
return id.startsWith('en/');
});
----
-```
-
-### `getEntry()`
-
-`getEntry()` is a function that returns a specific entry in a collection by entry ID (file path relative to the collection). Both of these are required parameters.
-
-```astro
----
-import { getEntry } from 'astro:content';
-
-const enterprise = await getEntry('blog', 'enterprise.md');
----
```
-### Data returned from a collection query
+### Using content in Astro templates
-`getCollection()` and `getEntry()` will return entries that include:
- - `id` - a unique ID using the file path relative to `src/content/[collection]`.
- - `slug` - a URL-ready slug. Defaults to the `id` parsed by [github-slugger](https://github.com/Flet/github-slugger).
- - `data` - an object of frontmatter properties inferred from your collection schema. Defaults to `any` if no schema is configured.
- - `body` - a string containing the raw, uncompiled body of the Markdown or MDX document.
- - `render()` - a function that returns the compiled body of the Markdown or MDX document via the `` component ([See complete documentation](#rendering-entry-content)).
+Once you have queried your collection entries, you can access each entry directly inside of your Astro component template. This lets you to render HTML for things like links to your content (using the content `slug`) or information about your content (using the `data` property).
-Querying your content files with `getCollection()` or `getEntry()` allows you to use frontmatter properties from an entry's `data` object in [JSX-like expressions](/en/core-concepts/astro-components/#jsx-like-expressions) or pass props to other components, such as a layout. You can optionally add type safety with a built-in utility.
+For information about rendering your content to HTML, see [Rendering Content to HTML](/en/guides/content-collections/#rendering-content-to-html) below.
-For example, you can use a `getCollection()` query to filter and then display a list of links to all your published blog posts:
```astro
---
// src/pages/index.astro
import { getCollection } from 'astro:content';
-
-// Get all published blog posts
-const blogPosts = await getCollection('blog', ({ data }) => {
- return data.status === 'published';
-});
+const blogEntries = await getCollection('blog');
---
```
-### Collection entry types
+### Passing content as props
+
+A component can also pass an entire content entry as a prop.
-If a page or component uses content from a `getCollection()` or `getEntry()` query, you can use the `CollectionEntry` utility to type its props:
+If you do this, you can use the [`CollectionEntry`](/en/reference/api-reference/#collection-entry-type) utility to correctly type your components props using TypeScript. This utility takes a string argument that matches the name of your collection schema, and will inherit all of the properties of that collection's schema.
```astro /CollectionEntry(?:<.+>)?/
---
// src/components/BlogCard.astro
import type { CollectionEntry } from 'astro:content';
-
interface Props {
- // Get type of a `blog` collection entry
post: CollectionEntry<'blog'>;
}
-// `post.data` will match your collection schema
+// `post` will match your 'blog' collection schema type
const { post } = Astro.props;
---
```
-## Rendering entry content
+### Rendering content to HTML
-Every collection entry includes a `render()` function that gives you access to the contents of the Markdown or MDX file. This includes a `` component for rendering the document body, as well as the document headings and frontmatter modified via remark or rehype.
+Once queried, you can render a collection entry to HTML using the entry `render()` function property. Calling this function gives you access to rendered content and metadata, including both a `` component and a list of all rendered headings.
```astro {5}
---
// src/pages/render-example.astro
-import { getEntry } from 'astro:content';
-const entry = await getEntry('blog', 'post-1.md');
-const { Content, headings, remarkPluginFrontmatter } = await entry.render();
+import { getEntryBySlug } from 'astro:content';
+const entry = await getEntryBySlug('blog', 'post-1');
+const { Content, headings } = await entry.render();
---
+
Written by: {entry.data.author}
+
```
-### Access the `` component from `render()`
-To render the content of a Markdown or MDX entry, use the `` component returned by its `render()` function. This allows you to generate pages from your content entries (see [Generating pages from content collections](#generating-pages-from-content-collections)), add post previews to your homepage, or display your content elsewhere on your site.
+## Generating Routes from Content
-For example, this page renders the contents of `content/announcements/welcome.md` and uses some of its frontmatter properties:
+Because content collections live outside of the `src/pages/` directory, it's up to you to create [dynamic routes](/en/core-concepts/routing/#dynamic-routes) that will render your content and any collection entries to HTML.
-```astro "render()"
----
-// src/pages/welcome-announcement.astro
-import Layout from '../../layouts/Layout.astro';
-import { getEntry } from 'astro:content';
-const announcementPost = await getEntry('announcements', 'welcome.md');
-const { Content } = await announcementPost.render();
----
-
-
{announcementPost.data.title}
-
Written by: {announcementPost.data.author}
-
-
-```
-
-### Access headings from `render()`
-
-Astro [generates a list of headings](/en/guides/markdown-content/#exported-properties) for Markdown and MDX documents. You can access this list using the `headings` property from `render()`:
-
-```astro "{ headings }"
----
-import { getCollection } from 'astro:content';
-const blogPosts = await getCollection('blog');
----
+The way that you generate routes in Astro depends on your build [`output`](/en/reference/configuration-reference/#output) mode: 'static' (the default) or 'server' (for SSR).
-{blogPosts.map(async (post) => {
- const { headings } = await post.render();
- const h1 = headings.find(h => h.depth === 1);
- return
{h1}
-})}
-```
+### Building for static output (default)
-### Access modified frontmatter from `render()`
+If you are building a static website (Astro's default behavior), you would use the [`getStaticPaths()`](/en/reference/api-reference/#getstaticpaths) function to create multiple pages from a single `src/pages/` component during your build.
-Astro allows you to [modify frontmatter using remark or rehype plugins](/en/guides/markdown-content/#modifying-frontmatter-programmatically). You can access this modified frontmatter using the `remarkPluginFrontmatter` property from `render()`:
+Call [`getCollection()`](/en/reference/api-reference/#getcollection) inside of `getStaticPaths()` to query your content. Then, create your new URL paths using the `slug` property of each content entry.
-```astro "{ remarkPluginFrontmatter }"
+```astro "{ slug: entry.slug }"
---
+// src/pages/posts/[slug].astro
import { getCollection } from 'astro:content';
-const blogPosts = await getCollection('blog');
+// 1. Generate a new path for every collection entry
+export async function getStaticPaths() {
+ const blogEntries = await getCollection('blog');
+ return blogEntries.map(entry => ({
+ params: { slug: entry.slug }, props: { entry },
+ }));
+}
+// 2. When its time to render, you can get the entry directly from the prop
+const { entry } = Astro.props;
+const { Content } = await entry.render();
---
-
-{blogPosts.map(async (post) => {
- const { remarkPluginFrontmatter } = await post.render();
- return
+
```
-Assuming `readingTime` was injected ([see our reading time example](/en/guides/markdown-content/#example-calculate-reading-time)), it will be available on the `remarkPluginFrontmatter` object.
+This will generate a new page for every entry in the `blog` collection. For example, an entry at `src/content/blog/hello-world.md` will have a slug of `hello-world`, and therefore its final URL will be `/posts/hello-world/`.
-:::caution
-Remark and rehype plugins will have access to the _raw_ Markdown or MDX document frontmatter. This means frontmatter will not reflect any changes or defaults applied by your `schema`.
-:::
-
-
-**🙋 Why don't `getCollection()` and `getEntry()` contain these values?**
-
-The remark and rehype pipelines are only run when your content is **rendered.** This lets `render()` access anything generated by these plugins like injected frontmatter. To stay performant, `getCollection()` and `getEntry()` do not have this capability.
-
-
+### Building for server output (SSR)
-## Generating pages from content collections
+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 [`getEntryBySlug()`](/en/reference/api-reference/#getentrybyslug).
-You can create pages based on your content collections using [dynamic routes](/en/core-concepts/routing/#dynamic-routes).
-Use `getCollection()` inside a [`getStaticPaths()`](/en/reference/api-reference/#getstaticpaths) function to query your content entries and provide the `slug` parameter for each page.
-
-For example, you can dynamically create a page for each entry in a `blog` collection, including nested directories, by creating a `.astro` page with a [rest parameter in its filename](/en/core-concepts/routing/#rest-parameters) to match file paths of any depth.
-
- ```astro "{ slug: entry.slug }"
- ---
- // src/pages/posts/[...slug].astro
- import { getCollection } from 'astro:content';
-
- export async function getStaticPaths() {
- const blog = await getCollection('blog');
- return blog.map(entry => ({
- params: { slug: entry.slug },
- }));
- }
- ---
- ```
-
-This will generate page routes for every entry in the `blog` collection, mapping each entry’s slug to a URL. For example, an entry at `src/content/blog/hello-world.md` will have a slug of `hello-world` and the collection entry `src/content/blog/en/intro.md` will have a slug of `en/intro`.
+```astro
+---
+// src/pages/posts/[slug].astro
+import { getEntryBySlug } from 'astro:content';
+// 1. Get the slug from the incoming server request
+const { slug } = Astro.params;
+// 2. Query for the entry directly using the request slug
+const entry = await getEntryBySlug('blog', slug);
+// 3. (Optional) render the entry to HTML in the template
+const { Content } = await entry.render();
+---
+
{entry.data.title}
+
+```
-Because this dynamic route is in `src/pages/posts/`, the final URLs will be `/posts/hello-world/` and `/posts/en/intro/`.
+### Generating custom routes
-### Rendering post contents
+The `slug()` function in `defineCollection()` lets you customize how a content entry `slug` value is created. However, this function does not have access to any frontmatter.
-When you pass each page route an entry via `props` in your `getStaticPaths()` function, you have access to the entry from `Astro.props`. You can use its frontmatter values via the `data` object and use `render()` to render its content via a `` component. You can optionally add type safety using the `CollectionEntry` utility.
+You can still create custom URLs from your content frontmatter inside the `getStaticPaths()` function, when building for static output. For example, to create a custom URL for each blog post based on its `permalink` frontmatter property, you could do the following:
-```astro "render()" "props: entry"
+```astro "entry.data.permalink"
---
-// src/pages/blog/[...slug].astro
-import { getCollection, CollectionEntry } from 'astro:content';
-
+// src/pages/posts/[slug].astro
+// 1. Query the collection and generate a new path for each entry,
+// based on its "permalink" frontmatter property.
export async function getStaticPaths() {
- const docs = await getCollection('docs');
- return docs.map(entry => ({
- // Pass blog entry via props
- params: { slug: entry.slug }, props: { entry },
+ const blogEntries = await getCollection('blog');
+ return blogEntries.map(entry => ({
+ params: { slug: entry.data.permalink || entry.data.slug },
+ props: { entry },
}));
}
+// 2. When its time to render, you can get the entry directly from the prop.
+const { entry } = Astro.props;
+---
+```
-interface Props {
- // Optionally use `CollectionEntry` for type safety
- entry: CollectionEntry<'docs'>;
-}
+If you are building for server output (SSR), you would do a similar lookup of the entire collection on every request to find your matching entry. However this is not recommended for large collections, where loading the entire collection on every request may slow down your page response time.
-const { entry } = Astro.props;
+```astro "entry.data.permalink"
+---
+// src/pages/posts/[slug].astro
+import { getEntryBySlug } from 'astro:content';
+// 1. Get the slug from the incoming server request
+const { slug } = Astro.params;
+// 2. Query and filter the entire collection
+const blogEntries = await getCollection('blog', (entry) => {
+ return (entry.data.permalink || entry.slug) === slug;
+});
+// 3. (Optional) render the entry to HTML in the template
const { Content } = await entry.render();
---
-
{entry.data.title}
```
+
+## Modifying Frontmatter with Remark
+
+:::caution
+**Not recommended.** Remark and rehype plugins access the _raw_ Markdown or MDX document frontmatter. This means that `remarkPluginFrontmatter` frontmatter is handled seperately from your type-safe `schema`, and will not reflect any changes or defaults applied through Astro. Use at your own risk!
+:::
+
+Astro supports remark or rehype plugins that [modify your frontmatter directly](/en/guides/markdown-content/#modifying-frontmatter-programmatically). You can access this modified frontmatter inside of a content entry by using the `remarkPluginFrontmatter` property returned from `render()`:
+
+```astro "{ remarkPluginFrontmatter }"
+---
+import { getEntryBySlug } from 'astro:content';
+const blogPost = await getEntryBySlug('blog', 'post-1');
+const { remarkPluginFrontmatter } = await blogPost.render();
+---
+
+
+```
+
+The remark and rehype pipelines only runs when your content is rendered, which explains why `remarkPluginFrontmatter` is only available after you call `render()` on your content entry. In contrast, `getCollection()` and `getEntryBySlug()` cannot return these values directly because they do not render your content.
+
diff --git a/src/pages/en/reference/api-reference.mdx b/src/pages/en/reference/api-reference.mdx
index af2b41b23b776..8070b4432bece 100644
--- a/src/pages/en/reference/api-reference.mdx
+++ b/src/pages/en/reference/api-reference.mdx
@@ -768,35 +768,34 @@ const draftBlogPosts = await getCollection('blog', ({ data }) => {
---
```
-[See the `getCollection()` guide section](/en/guides/content-collections/#getcollection) for complete usage examples.
+[See the `Content Collection` guide](/en/guides/content-collections/#querying-collections) for example usage.
-### `getEntry()`
+### `getEntryBySlug()`
**Type:** `(collection: string, id: string) => CollectionEntry`
-`getEntry()` is a function that retrieves a single collection entry by collection name and [entry `id`](#id).
+`getEntryBySlug()` is a function that retrieves a single collection entry by collection name and entry `slug`.
```astro
---
-import { getEntry } from 'astro:content';
+import { getEntryBySlug } from 'astro:content';
-const enterprise = await getEntry('blog', 'enterprise.md');
+const enterprise = await getEntryBySlug('blog', 'enterprise.md');
---
```
+[See the `Content Collection` guide](/en/guides/content-collections/#querying-collections) for example usage.
+
### Collection Entry Type
-The [`getCollection()`](#getcollection) and [`getEntry()`](#getentry) functions each return entries with the `CollectionEntry` type. This type is available as a utility from `astro:content`:
+The [`getCollection()`](#getcollection) and [`getEntryBySlug()`](#getentrybyslug) functions each return entries with the `CollectionEntry` type. This type is available as a utility from `astro:content`:
```ts
import type { CollectionEntry } from 'astro:content';
-
-// Example: Receive a `src/content/blog/` entry as props
-type Props = CollectionEntry<'blog'>;
```
-A `CollectionEntry` is an object with the following values:
+The `CollectionEntry` type is an object with the following values. `TCollectionName` is the name of the collection you're querying (e.g. `CollectionEntry<'blog'>`).
#### `id`
@@ -834,14 +833,14 @@ A function to compile a given Markdown or MDX document for rendering. This retur
```astro
---
-import { getEntry } from 'astro:content';
-const entry = await getEntry('blog', 'entry-1.md');
+import { getEntryBySlug } from 'astro:content';
+const entry = await getEntryBySlug('blog', 'entry-1.md');
const { Content, headings, remarkPluginFrontmatter } = await entry.render();
---
```
-See [the rendering entry contents guide](/en/guides/content-collections/#rendering-entry-content) for complete usage examples.
+[See the `Content Collection` guide](/en/guides/content-collections/#rendering-content-to-html) for example usage.
## `import.meta`
diff --git a/src/pages/es/reference/api-reference.mdx b/src/pages/es/reference/api-reference.mdx
index 6d0b4e6795007..c7d15a78a6fb3 100644
--- a/src/pages/es/reference/api-reference.mdx
+++ b/src/pages/es/reference/api-reference.mdx
@@ -759,7 +759,7 @@ const draftBlogPosts = await getCollection('blog', ({ data }) => {
---
```
-[Consulta la sección de guÃa de `getCollection()`](/es/guides/content-collections/#getcollection) para ejemplos de uso completo.
+[Consulta la sección de guÃa de `getCollection()`](/es/guides/content-collections/#querying-collections) para ejemplos de uso completo.
### `getEntry()`
@@ -829,7 +829,7 @@ const {Content, headings, remarkPluginFrontmatter} = await entry.render();
---
```
-Ver [la guÃa de renderizado de contenido de entrada](/es/guides/content-collections/#rendering-entry-content) para ejemplos de uso completo.
+Ver [la guÃa de renderizado de contenido de entrada](/es/guides/content-collections/) para ejemplos de uso completo.
## `import.meta`
diff --git a/src/pages/pt-br/reference/api-reference.mdx b/src/pages/pt-br/reference/api-reference.mdx
index d335c06402c24..0d6e694ecc0fd 100644
--- a/src/pages/pt-br/reference/api-reference.mdx
+++ b/src/pages/pt-br/reference/api-reference.mdx
@@ -754,7 +754,7 @@ const postagensRascunhoBlog = await getCollection('blog', ({ data }) => {
---
```
-[Veja a seção guia de `getCollection()`](/pt-br/guides/content-collections/#getcollection) para exemplos completos de uso.
+[Veja a seção guia de `getCollection()`](/pt-br/guides/content-collections/#querying-collections) para exemplos completos de uso.
### `getEntry()`
@@ -824,7 +824,7 @@ const { Content, headings, remarkPluginFrontmatter } = await entry.render();
---
```
-Veja [o guia de renderização de conteúdos de entrada](/pt-br/guides/content-collections/#rendering-entry-content) para exemplos de uso completos.
+Veja [o guia de renderização de conteúdos de entrada](/pt-br/guides/content-collections/) para exemplos de uso completos.
## `import.meta`