From 74950dda536f8cd87a82eb57cb6920157afa4be8 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Fri, 13 Jan 2023 23:30:29 -0800 Subject: [PATCH 01/18] content collections docs pass --- src/i18n/en/nav.ts | 2 +- src/pages/en/guides/content-collections.mdx | 520 +++++++++----------- 2 files changed, 234 insertions(+), 288 deletions(-) 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/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 918d356d2cd80..1247ff59f60c4 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -16,166 +16,152 @@ 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 a feature to help manage your Markdown and MDX files in Astro. A collection will organize your content, validate your frontmatter, and provide automatic TypeScript type-safety of fetched 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). +Content collections are the best way to work with Markdown and MDX in your project. For smaller projects, you can also [save your Markdown directly inside of the `src/pages/` directory.](/en/core-concepts/astro-pages/#markdownmdx-pages) -## 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 +Content collections help you organize the content files in your project. A content collection can contain any number of related Markdown (`.md`) or MDX (`.mdx`) files. These files within a collection are called **content entries.** -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). - - - - 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. - - ```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 - } - } - ``` - - - -### Set up the `.astro` directory - -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. - -:::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: - -```ts title="src/env.d.ts" -// using a relative path `../` to the `.astro` directory -/// -``` - -## 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/`. + +- src/content/ + - **newsletter/** + - week-1.md + - week-2.md + - week-3.md + -## Collections +Once you have a content collection in place with at least one content entry, you can query your content using Astro's built-in APIs. -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. +### Creating a new content collection -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). +To create a new content collection, add a new top-level directory to the `src/content/` folder of your project. If a `src/content` directory doesn't already exist in your project, create it. -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`. +Fill your collection directory with related Markdown and/or MDX files. Content collections work best when all files in the collection follow a similar frontmatter structure. If two files represent two different kinds of content (a blog post and an author profile, for example) they most likely belong in two different collections. - 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/** diffrent content types belong in different collections + - grace-hopper.md + - alan-turing.md + - batman.md -### Collections with nested directories +If your content frontmatter varies too wildly between files in a single collection, features like frontmatter validation and automatic TypeScript typings become less powerful. You can optionally define and enforce a required [frontmatter schema](#creating-a-collection-schema) across the entire collection. + +### Organizing your collections with subdirectories -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. +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 a nested subdirectory to organize your content within a collection. -For example, you can use this structure for internationalization: +For example, you can use the following subdirectory structure to add international translations within a collection: - src/content/ - - docs/ docs schema applies to all nested directories + - docs/ - en/ - es/ - ... -## Configuring collections +## Configuration -You can configure your content collections with an optional `src/content/config.ts` file (`.js` and `.mjs` extensions are also supported). +Astro allows you to customize and configure your project's content collections through an optional `src/content/config.ts` file (`.js` and `.mjs` extensions are also supported). -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. +Currently, this file supports [creating a collection schema](#creating-a-collection-schema) and [defining custom unique slugs](#defining-custom-slugs) for your collections. -### Defining a collection schema +### Creating 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). +Schemas are an optional way to enforce consistent frontmatter within a collection. Without a schema, you have no way to guarantee that a frontmatter property actually exists when you go to reference it inside a page or component template. -To configure schemas, create a `src/content/config.ts` file (`.js` and `.mjs` extensions are also supported). This file should: +To create a content collection schema, first 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 `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. - -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. - -You can specify the expected shape of your frontmatter with the `schema` field of `defineCollection`: +1. Import the proper utilities from `astro:content`. +2. Define a schema for each collection you'd like to validate. +3. Export a single `collections` object to apply your schemas. ```ts // src/content/config.ts +// 1. Import the proper 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 +Astro supports working with multiple collections in a single project. + +```ts +// src/content/config.ts +// ... + +// Export multiple collections to register them +export const collections = { + 'blog': blogCollection, + 'newsletter': newsletterCollection, + 'profile': profileCollection, +}; +``` + +As your project grows, you are free to reorganize your codebase and move your schemas and collections out of the `src/content/config.ts` file. Your collections and schemas can live anywhere in your project, as long as they are imported into your `src/content/config.ts` file and and then re-exported from the `collections` object. + +```ts +// src/content/config.ts +// 1. Import your schemas and utilities +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 = { - releases: releases, - // Don't forget 'quotes' for collection names containing dashes - 'engineering-blog': engineeringBlog, + 'blog': blogCollection, + 'newsletter': newsletterCollection, }; ``` -#### Schema data types with Zod +### Using third-party collection schemas + +You are free to import schemas and collections 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 = { + 'blog': blogCollection, +}; +``` + + +### Defining datatypes 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. +Astro leverages [Zod](https://github.com/colinhacks/zod) to create frontmatter schemas in the form of [Zod objects](https://github.com/colinhacks/zod#objects). 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 you are able to use all Zod features inside of Astro. 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 +173,250 @@ 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. +Astro will generate a unique `slug` value for each content entry. By default, a content entry's `slug` is based on its `id` (its filename). -```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 this value, provide a `slug()` function in `defineCollection()`. Your `slug()` function can override the default slug with a custom value of your choosing. + +```ts {4-6} +// Example: Override the default entry slug with the +// "permalink" frontmatter property, if it exists. +const blog = defineCollection({ + slug: ({ id, defaultSlug, data, body }) => { + return data.permalink || defaultSlug; + }, +}); ``` -📚 See [Zod’s documentation](https://github.com/colinhacks/zod) for a complete list of features. -### Custom entry slugs +### Setting up TypeScript for content collections -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). +To benefit from the full TypeScript and autocompletion features of [using schemas with your collections](#creating-a-collection-schema), you may need to update your `tsconfig.json` to enable `strictNullChecks`. -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. +```json title="tsconfig.json" ins={3} +{ + "compilerOptions": { + "strictNullChecks": true + } +} +``` -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. +Note that if you are currently extending Astro's `strict` or `strictest` recommended settings, no changes is needed. -```ts {5-9} -// src/content/config.ts -import { defineCollection, z } from 'astro:content'; +```json title="tsconfig.json" "strict" "strictest" +{ + // No change needed! + "extends": "astro/tsconfigs/strict" +} +``` -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(), - }), -}); +### The ".astro" Directory -export const collections = { blog }; -``` +Astro generates type information and metadata from your content collections in a new `.astro` directory. No action is needed on your part to maintain or update this directory. It is safe to ignore it in your project. -## Querying content collections +:::tip +If you're using Git for version control, we recommend ignoring this generated directory by adding `.astro` to your `.gitignore`. +::: -Astro provides two functions to query collections: +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) yourself to update `.astro` manually. -### `getCollection()` -`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. +## The "src/content" Directory -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. +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/`. -```astro ---- -import { getCollection } from 'astro:content'; -// Get all `src/content/blog/` entries -const allBlogPosts = await getCollection('blog'); +## Querying Collections -// Only return posts with `draft: true` in the frontmatter -const draftBlogPosts = await getCollection('blog', ({ data }) => { - return data.draft === true; -}); ---- +Astro provides two functions to query a collection and return one (or more) content entries from it: [`getCollection()`](en/reference/api-reference/#getcollection) and [`getEntryBySlug()`](en/reference/api-reference/#getentrybyslug). + +```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 nested directories +#### Filtering collection queries -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: +`getCollection` takes an optional "filter" callback that allows you to filter your query based on an entry's `id`, `slug`, or `data` (frontmatter) properties. -```astro ---- +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: + +```js +// Example: Filter content entries with `draft: true` frontmatter import { getCollection } from 'astro:content'; -const enDocs = await getCollection('docs', ({ id }) => { - // Return all entries in `src/content/docs/en/` - return id.startsWith('en/'); +const draftBlogEntries = await getCollection('blog', ({ data }) => { + return data.draft !== true; }); ---- ``` -### `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. +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 ---- -import { getEntry } from 'astro:content'; - -const enterprise = await getEntry('blog', 'enterprise.md'); ---- +```js +// Example: Filter entries by sub-directory in the collection +import { getCollection } from 'astro:content'; +const englishDocsEntries = await getCollection('docs', ({ id }) => { + return id.startsWith('en/'); +}); ``` -### 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 use those entries to render information about your content. 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'); ---
    - {blogPosts.map(post => ( + {blogEntries.map(blogPostEntry => (
  • - {post.data.title} -
  • ))}
``` -### Collection entry types +### Passing content as props + +A component can also expects a `getCollection()` or `getEntryBySlug()` 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` 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. - -For example, this page renders the contents of `content/announcements/welcome.md` and uses some of its frontmatter properties: - -```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()`: +### Advanced: Modifying frontmatter with remark -```astro "{ headings }" ---- -import { getCollection } from 'astro:content'; -const blogPosts = await getCollection('blog'); ---- - -{blogPosts.map(async (post) => { - const { headings } = await post.render(); - const h1 = headings.find(h => h.depth === 1); - return

{h1}

-})} -``` - -### Access modified frontmatter from `render()` +:::caution +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 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()`: +While not recommended, Astro allows you to [modify frontmatter directly](/en/guides/markdown-content/#modifying-frontmatter-programmatically) using remark or rehype plugins. You can access this modified frontmatter within Content Collections by using the `remarkPluginFrontmatter` property returned from `render()`: ```astro "{ remarkPluginFrontmatter }" --- -import { getCollection } from 'astro:content'; -const blogPosts = await getCollection('blog'); +import { getEntryBySlug } from 'astro:content'; +const blogPost = await getEntryBySlug('blog', 'post-1'); +const { remarkPluginFrontmatter } = await blogPost.render(); --- - -{blogPosts.map(async (post) => { - const { remarkPluginFrontmatter } = await post.render(); - return

{post.data.title} — {remarkPluginFrontmatter.readingTime}

-})} + +

{blogPost.data.title} — {remarkPluginFrontmatter.readingTime}

``` -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. - -:::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?** +**🙋 Why don't `getCollection()` and `getEntryBySlug()` 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. +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 `getEntryBySlug()` do not have this capability.
-## Generating pages from content collections -You can create pages based on your content collections using [dynamic routes](/en/core-concepts/routing/#dynamic-routes). +## Generating Routes from Content -Use `getCollection()` inside a [`getStaticPaths()`](/en/reference/api-reference/#getstaticpaths) function to query your content entries and provide the `slug` parameter for each page. +Because content collections live outside of the `src/pages` directory, its up to you to create your [dynamic routes](/en/core-concepts/routing/#dynamic-routes) that will render your content and any collection entries to HTML. -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. +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). - ```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 }, - })); - } - --- - ``` +### Building for static output (default) -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`. +If you are building a static website (Astro's default behavior), you are expected use Astro's [`getStaticPaths()`](/en/reference/api-reference/#getstaticpaths) to create multiple routes from the same `src/pages` component at build-time. -Because this dynamic route is in `src/pages/posts/`, the final URLs will be `/posts/hello-world/` and `/posts/en/intro/`. +Inside of `getStaticPaths()`, you would call [`getCollection()`](en/reference/api-reference/#getcollection) to query your content entries and then create all of the static paths you need using the `slug` parameter for each page. -### Rendering post contents - -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. - -```astro "render()" "props: entry" +```astro "{ slug: entry.slug }" --- -// src/pages/blog/[...slug].astro -import { getCollection, CollectionEntry } from 'astro:content'; - +// src/pages/posts/[slug].astro +import { getCollection } from 'astro:content'; +// 1. Generate a new path for every collection entry export async function getStaticPaths() { - const docs = await getCollection('docs'); - return docs.map(entry => ({ - // Pass blog entry via props + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ params: { slug: entry.slug }, props: { entry }, })); } - -interface Props { - // Optionally use `CollectionEntry` for type safety - entry: CollectionEntry<'docs'>; -} - +// 2. When its time to render, you can get the entry directly from the prop const { entry } = Astro.props; const { Content } = await entry.render(); --- +

{entry.data.title}

+ +``` + +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/`. + +### Building for server output (SSR) + +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()`. + +```astro "{ slug: Astro.params }" +--- +// src/pages/posts/[slug].astro +// NOTE: This example will only work in +import { getEntryBySlug } from 'astro:content'; +// 1. Get the slug from the request, on-demand +const { slug } = Astro.params; +// 2. Query the entry directly using its 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}

``` From 0912e11fc687e9a4e1002f5396f59576765e5744 Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger Date: Fri, 20 Jan 2023 15:43:16 +0000 Subject: [PATCH 02/18] update link from api-reference page --- src/pages/en/reference/api-reference.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/reference/api-reference.mdx b/src/pages/en/reference/api-reference.mdx index af2b41b23b776..c30cc6f83c730 100644 --- a/src/pages/en/reference/api-reference.mdx +++ b/src/pages/en/reference/api-reference.mdx @@ -841,7 +841,7 @@ 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 rendering entry contents guide](/en/guides/content-collections/#rendering-content) for complete usage examples. ## `import.meta` From 3364c00d09453555bae9de41b0e7655fe1ee173e Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger Date: Fri, 20 Jan 2023 16:21:06 +0000 Subject: [PATCH 03/18] updates for getEntryBySlug incl code examples --- src/pages/en/reference/api-reference.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/en/reference/api-reference.mdx b/src/pages/en/reference/api-reference.mdx index c30cc6f83c730..5f0af0487ec35 100644 --- a/src/pages/en/reference/api-reference.mdx +++ b/src/pages/en/reference/api-reference.mdx @@ -770,24 +770,24 @@ const draftBlogPosts = await getCollection('blog', ({ data }) => { [See the `getCollection()` guide section](/en/guides/content-collections/#getcollection) for complete usage examples. -### `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'); --- ``` ### 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'; @@ -834,8 +834,8 @@ 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(); --- From 1fae08a2fc4fd51a73923a83040be4efae640d74 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sat, 21 Jan 2023 13:35:38 -0800 Subject: [PATCH 04/18] Update content-collections.mdx --- src/pages/en/guides/content-collections.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 1247ff59f60c4..879a802817fe0 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -244,7 +244,7 @@ Astro treats the `src/content/` directory as special. This is where **collection ## Querying Collections -Astro provides two functions to query a collection and return one (or more) content entries from it: [`getCollection()`](en/reference/api-reference/#getcollection) and [`getEntryBySlug()`](en/reference/api-reference/#getentrybyslug). +Astro provides two functions to query a collection and return one (or more) content entries from it: [`getCollection()`](/en/reference/api-reference/#getcollection) and [`getEntryBySlug()`](/en/reference/api-reference/#getentrybyslug). ```js import { getCollection, getEntryBySlug } from 'astro:content'; @@ -304,7 +304,7 @@ const blogEntries = await getCollection('blog'); ### Passing content as props -A component can also expects a `getCollection()` or `getEntryBySlug()` content entry as a prop. +A component can also pass an entire content entry as a prop. If you do this, you can use the `CollectionEntry` 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. @@ -377,7 +377,7 @@ The way that you generate routes in Astro depends on your build [`output`](/en/r If you are building a static website (Astro's default behavior), you are expected use Astro's [`getStaticPaths()`](/en/reference/api-reference/#getstaticpaths) to create multiple routes from the same `src/pages` component at build-time. -Inside of `getStaticPaths()`, you would call [`getCollection()`](en/reference/api-reference/#getcollection) to query your content entries and then create all of the static paths you need using the `slug` parameter for each page. +Inside of `getStaticPaths()`, you would call [`getCollection()`](/en/reference/api-reference/#getcollection) to query your content entries and then create all of the static paths you need using the `slug` parameter for each page. ```astro "{ slug: entry.slug }" --- @@ -402,7 +402,7 @@ This will generate a new page for every entry in the `blog` collection. For exam ### Building for server output (SSR) -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()`. +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). ```astro "{ slug: Astro.params }" From acd9460961c3ba4231c59d6179447985b405a6fa Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sat, 21 Jan 2023 21:48:15 -0800 Subject: [PATCH 05/18] fred pass 3 --- src/pages/en/guides/content-collections.mdx | 235 ++++++++++---------- src/pages/en/reference/api-reference.mdx | 9 +- 2 files changed, 124 insertions(+), 120 deletions(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 879a802817fe0..dfc3e0cf91e75 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -16,31 +16,46 @@ import TypeScriptSettingTabs from '~/components/tabs/TypeScriptSettingTabs.astro

-**Content collections** are a feature to help manage your Markdown and MDX files in Astro. A collection will organize your content, validate your frontmatter, and provide automatic TypeScript type-safety of fetched content. +**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. -Content collections are the best way to work with Markdown and MDX in your project. For smaller projects, you can also [save your Markdown directly inside of the `src/pages/` directory.](/en/core-concepts/astro-pages/#markdownmdx-pages) ## What are 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. -Content collections help you organize the content files in your project. A content collection can contain any number of related Markdown (`.md`) or MDX (`.mdx`) files. These files within a collection are called **content entries.** +A **content entry** is any piece of content stored inside of your content collection. Content entries are stored as either Markdown (`.md`) or MDX (`.mdx`) files, saved inside of content collection directory. Your content entries can be named anything you want, but we recommend using a consistent naming scheme to make it easier to find and organize your content. - src/content/ - - **newsletter/** - - week-1.md - - week-2.md - - week-3.md + - **newsletter/** the "newsletter" collection + - week-1.md a collection entry + - week-2.md a collection entry + - week-3.md a collection entry -Once you have a content collection in place with at least one content entry, you can query your content using Astro's built-in APIs. +Once you have a collection, you can start [querying your content](#querying-collections) using Astro's built-in content APIs. + +### 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. + +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 the `.astro` directory by adding `.astro` to your `.gitignore`. This tells Git to ignore this directory and any files inside of it. + +```bash +echo ".astro" >> .gitignore +``` +::: + -### Creating a new content collection -To create a new content collection, add a new top-level directory to the `src/content/` folder of your project. If a `src/content` directory doesn't already exist in your project, create it. +### Organizing with multiple collections -Fill your collection directory with related Markdown and/or MDX files. Content collections work best when all files in the collection follow a similar frontmatter structure. If two files represent two different kinds of content (a blog post and an author profile, for example) they most likely belong in two different collections. +If two files represent two different kinds of content (e.g. a blog post and an author profile), they most likely belong in two different collections. This is an important concept to remember because important features like frontmatter validation and automatic TypeScript typings require that all entries share a similar frontmatter structure. + +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/ @@ -48,47 +63,79 @@ Fill your collection directory with related Markdown and/or MDX files. Content c - week-1.md - week-2.md - week-3.md - - **authors/** diffrent content types belong in different collections + - **authors/** split different content types into new collections - grace-hopper.md - alan-turing.md - batman.md -If your content frontmatter varies too wildly between files in a single collection, features like frontmatter validation and automatic TypeScript typings become less powerful. You can optionally define and enforce a required [frontmatter schema](#creating-a-collection-schema) across the entire collection. -### Organizing your collections with subdirectories +### Organizing with subdirectories -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 a nested subdirectory to organize your content within a collection. +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 subdirectory structure to add international translations within a collection: +For example, you can use the following directory structure to organize i18n translations within a single `docs` collection. When you go to query this collection, you'll be able to filter the result by language using the file path. - src/content/ - - docs/ - - en/ - - es/ - - ... + - docs/ this collection uses subdirectories to organize by language + - **en/** + - **es/** + - **de/** -## Configuration +## Defining Collections + +:::caution +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. +::: -Astro allows you to customize and configure your project's content collections through an optional `src/content/config.ts` file (`.js` and `.mjs` extensions are also supported). +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. -Currently, this file supports [creating a collection schema](#creating-a-collection-schema) and [defining custom unique slugs](#defining-custom-slugs) for your collections. -### Creating a collection schema +```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, +}; +``` + -Schemas are an optional way to enforce consistent frontmatter within a collection. Without a schema, you have no way to guarantee that a frontmatter property actually exists when you go to reference it inside a page or component template. +### Setting up TypeScript -To create a content collection schema, first create a `src/content/config.ts` file if one does not already exist (`.js` and `.mjs` extensions are also supported). This file should: +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. + +```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 auto-completion 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 a schema for each collection you'd like to validate. -3. Export a single `collections` object to apply your schemas. +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 the proper utilities from `astro:content` +// 1. Import utilities from `astro:content` import { z, defineCollection } from 'astro:content'; // 2. Define a schema for each collection you'd like to validate. const blogCollection = defineCollection({ @@ -106,25 +153,22 @@ export const collections = { ### Defining multiple collections -Astro supports working with multiple collections in a single project. +You can define as many collections in your project as you would like. As long as they are exported from the `src/content/config.ts` file from inside the `collections` object, they will be loaded by Astro. ```ts // src/content/config.ts -// ... - -// Export multiple collections to register them export const collections = { - 'blog': blogCollection, - 'newsletter': newsletterCollection, - 'profile': profileCollection, + 'blog': defineCollection({ /* ... */ }), + 'newsletter': defineCollection({ /* ... */ }), + 'profile-authors': defineCollection({ /* ... */ }), }; ``` -As your project grows, you are free to reorganize your codebase and move your schemas and collections out of the `src/content/config.ts` file. Your collections and schemas can live anywhere in your project, as long as they are imported into your `src/content/config.ts` file and and then re-exported from the `collections` object. +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 schemas and utilities +// 1. Import your utilities and schemas import { defineCollection } from 'astro:content'; import {blogSchema, newsletterSchema} from '../schemas'; // 2. Define your collections @@ -139,7 +183,7 @@ export const collections = { ### Using third-party collection schemas -You are free to import schemas and collections 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. +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 @@ -155,9 +199,9 @@ export const collections = { ### Defining datatypes with Zod -Astro leverages [Zod](https://github.com/colinhacks/zod) to create frontmatter schemas in the form of [Zod objects](https://github.com/colinhacks/zod#objects). 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. +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. -To use Zod in Astro, import the `z` utility from `"astro:content"`. This is a re-export of the Zod library, and you are able to use all Zod features inside of Astro. See [Zod’s README](https://github.com/colinhacks/zod) for complete documentation on how Zod works and what features are available. +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. ```ts @@ -205,46 +249,10 @@ const blog = defineCollection({ ``` -### Setting up TypeScript for content collections - -To benefit from the full TypeScript and autocompletion features of [using schemas with your collections](#creating-a-collection-schema), you may need to update your `tsconfig.json` to enable `strictNullChecks`. - -```json title="tsconfig.json" ins={3} -{ - "compilerOptions": { - "strictNullChecks": true - } -} -``` - -Note that if you are currently extending Astro's `strict` or `strictest` recommended settings, no changes is needed. - -```json title="tsconfig.json" "strict" "strictest" -{ - // No change needed! - "extends": "astro/tsconfigs/strict" -} -``` - -### The ".astro" Directory - -Astro generates type information and metadata from your content collections in a new `.astro` directory. No action is needed on your part to maintain or update this directory. It is safe to ignore it in your project. - -:::tip -If you're using Git for version control, we recommend ignoring this generated directory by adding `.astro` to your `.gitignore`. -::: - -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) yourself to update `.astro` manually. - - -## The "src/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/`. - ## Querying Collections -Astro provides two functions to query a collection and return one (or more) content entries from it: [`getCollection()`](/en/reference/api-reference/#getcollection) and [`getEntryBySlug()`](/en/reference/api-reference/#getentrybyslug). +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). ```js import { getCollection, getEntryBySlug } from 'astro:content'; @@ -254,9 +262,11 @@ const allBlogPosts = await getCollection('blog'); const oneBlogPost = await getEntryBySlug('blog', 'enterprise'); ``` +Both functions return content entries as defined by the [`CollectionEntry`](/en/reference/api-reference/#collection-entry-type) type. + #### Filtering collection queries -`getCollection` takes an optional "filter" callback that allows you to filter your query based on an entry's `id`, `slug`, or `data` (frontmatter) properties. +`getCollection()` takes an optional "filter" callback that allows you to filter your query based on an entry's `id`, `slug`, or `data` (frontmatter) properties. 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: @@ -280,7 +290,7 @@ const englishDocsEntries = await getCollection('docs', ({ id }) => { ### Using content in Astro templates -Once you have queried your collection entries, you can use those entries to render information about your content. 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). +Once you have queried your collection entries, 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). For information about rendering your content to HTML, see [Rendering Content to HTML](/en/guides/content-collections/#rendering-content-to-html) below. @@ -306,7 +316,7 @@ const blogEntries = await getCollection('blog'); A component can also pass an entire content entry as a prop. -If you do this, you can use the `CollectionEntry` 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. +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(?:<.+>)?/ --- @@ -321,7 +331,7 @@ const { post } = Astro.props; --- ``` -## Rendering Content to HTML +### Rendering content to HTML 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. @@ -337,47 +347,17 @@ const { Content, headings } = await entry.render(); ``` -### Advanced: Modifying frontmatter with remark - -:::caution -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! -::: - -While not recommended, Astro allows you to [modify frontmatter directly](/en/guides/markdown-content/#modifying-frontmatter-programmatically) using remark or rehype plugins. You can access this modified frontmatter within Content Collections 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(); ---- - -

{blogPost.data.title} — {remarkPluginFrontmatter.readingTime}

-``` - -
-**🙋 Why don't `getCollection()` and `getEntryBySlug()` 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 `getEntryBySlug()` do not have this capability. - -
- - ## Generating Routes from Content -Because content collections live outside of the `src/pages` directory, its up to you to create your [dynamic routes](/en/core-concepts/routing/#dynamic-routes) that will render your content and any collection entries to HTML. +Because content collections live outside of the `src/pages/` directory, its up to you to create [dynamic routes](/en/core-concepts/routing/#dynamic-routes) that will render your content and any collection entries to HTML. 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). ### Building for static output (default) -If you are building a static website (Astro's default behavior), you are expected use Astro's [`getStaticPaths()`](/en/reference/api-reference/#getstaticpaths) to create multiple routes from the same `src/pages` component at build-time. +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. -Inside of `getStaticPaths()`, you would call [`getCollection()`](/en/reference/api-reference/#getcollection) to query your content entries and then create all of the static paths you need using the `slug` parameter for each page. +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 "{ slug: entry.slug }" --- @@ -420,3 +400,28 @@ 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(); +--- + +

{blogPost.data.title} — {remarkPluginFrontmatter.readingTime}

+``` + +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 5f0af0487ec35..ae80c92fc2029 100644 --- a/src/pages/en/reference/api-reference.mdx +++ b/src/pages/en/reference/api-reference.mdx @@ -768,7 +768,7 @@ 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. ### `getEntryBySlug()` @@ -785,18 +785,17 @@ 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 [`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` From 7b388b53e1b08b5a91c8128d199878e8ca0b18fa Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sat, 21 Jan 2023 21:58:38 -0800 Subject: [PATCH 06/18] update bad links --- src/pages/en/reference/api-reference.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/en/reference/api-reference.mdx b/src/pages/en/reference/api-reference.mdx index ae80c92fc2029..86582a0a41ea9 100644 --- a/src/pages/en/reference/api-reference.mdx +++ b/src/pages/en/reference/api-reference.mdx @@ -789,7 +789,7 @@ const enterprise = await getEntryBySlug('blog', 'enterprise.md'); ### Collection Entry Type -The [`getCollection()`](#getcollection) and [`getEntryBySlug()`](#getEntryBySlug) functions each return entries with the `CollectionEntry` type. This type is available as a utility from `astro:content`: +The [`getCollection()`](#getcollection) and [`getEntryBySlug()`](#geteentrybyslug) 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'; @@ -840,7 +840,7 @@ const { Content, headings, remarkPluginFrontmatter } = await entry.render(); --- ``` -See [the rendering entry contents guide](/en/guides/content-collections/#rendering-content) for complete usage examples. +[See the `Content Collection` guide](/en/guides/content-collections/#rendering-content-to-html) for example usage. ## `import.meta` From 2818a9885af39a477e07fe61cc85861893100f63 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sat, 21 Jan 2023 22:34:11 -0800 Subject: [PATCH 07/18] update bad links --- src/pages/en/reference/api-reference.mdx | 2 +- src/pages/es/reference/api-reference.mdx | 2 +- src/pages/pt-br/reference/api-reference.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/en/reference/api-reference.mdx b/src/pages/en/reference/api-reference.mdx index 86582a0a41ea9..8070b4432bece 100644 --- a/src/pages/en/reference/api-reference.mdx +++ b/src/pages/en/reference/api-reference.mdx @@ -789,7 +789,7 @@ const enterprise = await getEntryBySlug('blog', 'enterprise.md'); ### Collection Entry Type -The [`getCollection()`](#getcollection) and [`getEntryBySlug()`](#geteentrybyslug) 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'; diff --git a/src/pages/es/reference/api-reference.mdx b/src/pages/es/reference/api-reference.mdx index 6d0b4e6795007..77af13cd60908 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()` diff --git a/src/pages/pt-br/reference/api-reference.mdx b/src/pages/pt-br/reference/api-reference.mdx index d335c06402c24..0271467cb09fc 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()` From 074fa35dcf6e215b9de024507ef54e0125b6b470 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sat, 21 Jan 2023 22:49:24 -0800 Subject: [PATCH 08/18] update bad links --- src/pages/en/core-concepts/astro-pages.mdx | 2 +- src/pages/es/reference/api-reference.mdx | 2 +- src/pages/pt-br/reference/api-reference.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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/es/reference/api-reference.mdx b/src/pages/es/reference/api-reference.mdx index 77af13cd60908..c7d15a78a6fb3 100644 --- a/src/pages/es/reference/api-reference.mdx +++ b/src/pages/es/reference/api-reference.mdx @@ -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 0271467cb09fc..0d6e694ecc0fd 100644 --- a/src/pages/pt-br/reference/api-reference.mdx +++ b/src/pages/pt-br/reference/api-reference.mdx @@ -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` From 99a42a42c90fa82e8b2786324a0e9459d033cf8c Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:21:30 -0800 Subject: [PATCH 09/18] Update src/pages/en/guides/content-collections.mdx Co-authored-by: Sarah Rainsberger --- src/pages/en/guides/content-collections.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index dfc3e0cf91e75..fffdfd75a2751 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -23,7 +23,7 @@ import TypeScriptSettingTabs from '~/components/tabs/TypeScriptSettingTabs.astro 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. -A **content entry** is any piece of content stored inside of your content collection. Content entries are stored as either Markdown (`.md`) or MDX (`.mdx`) files, saved inside of content collection directory. Your content entries can be named anything you want, but we recommend using a consistent naming scheme to make it easier to find and organize your content. +A **content entry** is any piece of content stored inside of your content collection. Content entries are stored as either Markdown (`.md`) or MDX (`.mdx`) files, saved inside of the content collection directory. Your content entries can be named anything you want, but we recommend using a consistent naming scheme to make it easier to find and organize your content. - src/content/ From 6eb697df81dc3c868b94a52b314d0d0a4ee4b380 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:25:09 -0800 Subject: [PATCH 10/18] Update src/pages/en/guides/content-collections.mdx Co-authored-by: Sarah Rainsberger --- src/pages/en/guides/content-collections.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index fffdfd75a2751..c0bb5e965b8b0 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -53,7 +53,7 @@ echo ".astro" >> .gitignore ### Organizing with multiple collections -If two files represent two different kinds of content (e.g. a blog post and an author profile), they most likely belong in two different collections. This is an important concept to remember because important features like frontmatter validation and automatic TypeScript typings require that all entries share a similar frontmatter structure. +If two files represent different kinds of content (e.g. a blog post and an author profile), they most likely belong in different collections. Helpful features like frontmatter validation and automatic TypeScript typings require that all entries in the same collection share a similar frontmatter structure. 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. From 5284892fd3abed77bb1886b9f21023b01dfd2a2a Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:24:38 -0800 Subject: [PATCH 11/18] update copy --- src/pages/en/guides/content-collections.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index c0bb5e965b8b0..bbb220c11b785 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -23,7 +23,7 @@ import TypeScriptSettingTabs from '~/components/tabs/TypeScriptSettingTabs.astro 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. -A **content entry** is any piece of content stored inside of your content collection. Content entries are stored as either Markdown (`.md`) or MDX (`.mdx`) files, saved inside of the content collection directory. Your content entries can be named anything you want, but we recommend using a consistent naming scheme to make it easier to find and organize your content. +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. - src/content/ @@ -53,7 +53,7 @@ echo ".astro" >> .gitignore ### Organizing with multiple collections -If two files represent different kinds of content (e.g. a blog post and an author profile), they most likely belong in different collections. Helpful features like frontmatter validation and automatic TypeScript typings require that all entries in the same collection share a similar frontmatter structure. +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. 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. From 49c56579f5cbfffe37bd95fb819642246a92b699 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:31:04 -0800 Subject: [PATCH 12/18] Update src/pages/en/guides/content-collections.mdx Co-authored-by: Sarah Rainsberger --- src/pages/en/guides/content-collections.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index bbb220c11b785..220a0bb00977d 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -125,7 +125,7 @@ To get the most out of content collections in Astro, you may need to update your 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 auto-completion and type-checking. +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: From 33adbe3258dbfce8627565e2a258167a174aadf1 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:31:25 -0800 Subject: [PATCH 13/18] Update src/pages/en/guides/content-collections.mdx Co-authored-by: Sarah Rainsberger --- src/pages/en/guides/content-collections.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 220a0bb00977d..a95eb9d5aaf6c 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -153,7 +153,7 @@ export const collections = { ### Defining multiple collections -You can define as many collections in your project as you would like. As long as they are exported from the `src/content/config.ts` file from inside the `collections` object, they will be loaded by Astro. +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 From 735b0f659458e8d2b6ae836889ddee8dae477c00 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:31:44 -0800 Subject: [PATCH 14/18] Update src/pages/en/guides/content-collections.mdx Co-authored-by: Sarah Rainsberger --- src/pages/en/guides/content-collections.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index a95eb9d5aaf6c..7030c8d78548f 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -290,7 +290,7 @@ const englishDocsEntries = await getCollection('docs', ({ id }) => { ### Using content in Astro templates -Once you have queried your collection entries, 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). +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). For information about rendering your content to HTML, see [Rendering Content to HTML](/en/guides/content-collections/#rendering-content-to-html) below. From 3479a9e4e654560271913e304f971b32fd6ae61d Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:31:58 -0800 Subject: [PATCH 15/18] Update src/pages/en/guides/content-collections.mdx Co-authored-by: Sarah Rainsberger --- src/pages/en/guides/content-collections.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 7030c8d78548f..4b35a15466b12 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -349,7 +349,7 @@ const { Content, headings } = await entry.render(); ## Generating Routes from Content -Because content collections live outside of the `src/pages/` directory, its up to you to create [dynamic routes](/en/core-concepts/routing/#dynamic-routes) that will render your content and any collection entries to HTML. +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. 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). From ed97d1be7be3f90c0a1f153a99d12a99c6ac4455 Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 10:32:13 -0800 Subject: [PATCH 16/18] Update src/pages/en/guides/content-collections.mdx Co-authored-by: Sarah Rainsberger --- src/pages/en/guides/content-collections.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 4b35a15466b12..931bb2a670142 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -74,7 +74,7 @@ If you find yourself working with different types of content, you should create 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 go to query this collection, you'll be able to filter the result by language using the file path. +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/ From ab6e92f6d21d1f1635972e0e5610a3c87f4e7e2f Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 17:10:56 -0800 Subject: [PATCH 17/18] update docs --- src/pages/en/guides/content-collections.mdx | 67 +++++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 931bb2a670142..52c09a4571515 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -86,7 +86,7 @@ For example, you can use the following directory structure to organize i18n tran ## Defining Collections -:::caution +:::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. ::: @@ -234,20 +234,20 @@ defineCollection({ ### Defining custom slugs -Astro will generate a unique `slug` value for each content entry. By default, a content entry's `slug` is based on its `id` (its filename). +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. -To customize this value, provide a `slug()` function in `defineCollection()`. Your `slug()` function can override the default slug with a custom value of your choosing. +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. ```ts {4-6} -// Example: Override the default entry slug with the -// "permalink" frontmatter property, if it exists. -const blog = defineCollection({ - slug: ({ id, defaultSlug, data, body }) => { - return data.permalink || defaultSlug; +// Example: Override the default entry slug with a custom value. +const blogCollection = defineCollection({ + slug: ({ id, defaultSlug }) => { + return myCustomSlugProcessor(id) || defaultSlug; }, }); ``` +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. ## Querying Collections @@ -385,14 +385,13 @@ This will generate a new page for every entry in the `blog` collection. For exam 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). -```astro "{ slug: Astro.params }" +```astro --- // src/pages/posts/[slug].astro -// NOTE: This example will only work in import { getEntryBySlug } from 'astro:content'; -// 1. Get the slug from the request, on-demand +// 1. Get the slug from the incoming server request const { slug } = Astro.params; -// 2. Query the entry directly using its slug +// 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(); @@ -401,6 +400,50 @@ const { Content } = await entry.render(); ``` +### Generating custom routes + +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. + +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 "entry.data.permalink" +--- +// 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 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; +--- +``` + +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. + +```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 From 691e541ec97cfce14f99e7d4d091ea0b8d0dd7ba Mon Sep 17 00:00:00 2001 From: "Fred K. Schott" Date: Sun, 22 Jan 2023 17:24:29 -0800 Subject: [PATCH 18/18] update docs --- src/pages/en/guides/content-collections.mdx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/en/guides/content-collections.mdx b/src/pages/en/guides/content-collections.mdx index 52c09a4571515..59f86628a9f4c 100644 --- a/src/pages/en/guides/content-collections.mdx +++ b/src/pages/en/guides/content-collections.mdx @@ -404,9 +404,7 @@ const { Content } = await entry.render(); 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. -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: +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 "entry.data.permalink" ---