Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 63 additions & 40 deletions src/content/docs/en/guides/content-collections.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,16 @@ Your blog post content here.
You can also pass options to the `glob()` loader's [`generateID()` helper function](/en/reference/content-loader-reference/#generateid) when you define your build-time collection to adjust how `id`s are generated. For example, you may wish to revert the default behavior of converting uppercase letters to lowercase for each collection entry:

```js title="src/content.config.ts"
import { glob } from "astro/loaders";
import { defineCollection } from "astro:content";

const authors = defineCollection({
/* Retrieve all JSON files in your authors directory while retaining
* uppercase letters in the ID. */
loader: glob({
pattern: '**/*.json',
pattern: "**/*.json",
base: "./src/data/authors",
generateId: ({ entry }) => entry.replace(/\.json$/, ''),
generateId: ({ entry }) => entry.replace(/\.json$/, ""),
}),
});
```
Expand All @@ -224,8 +227,8 @@ const authors = defineCollection({
The [`file()` loader](/en/reference/content-loader-reference/#file-loader) fetches multiple entries from a single local file defined in your collection. The `file()` loader will automatically detect and parse (based on the file extension) a single array of objects from JSON and YAML files, and will treat each top-level table as an independent entry in TOML files.

```ts title="src/content.config.ts" {5}
import { defineCollection } from 'astro:content';
import { file } from 'astro/loaders';
import { defineCollection } from "astro:content";
import { file } from "astro/loaders";

const dogs = defineCollection({
loader: file("src/data/dogs.json"),
Expand Down Expand Up @@ -301,8 +304,8 @@ You can [build a custom loader](/en/reference/content-loader-reference/#building
Then you can import and define your custom loader in your collections config, passing any required values:

```ts title="src/content.config.ts"
import { defineCollection } from 'astro:content';
import { myLoader } from './loader.ts';
import { defineCollection } from "astro:content";
import { myLoader } from "./loader.ts";

const blog = defineCollection({
loader: myLoader({
Expand Down Expand Up @@ -335,9 +338,9 @@ In order for Astro to recognize a new or updated schema, you may need to restart
Providing a `schema` is optional, but highly recommended! If you choose to use a schema, then every frontmatter or data property of your collection entries must be defined using a [Zod data type](/en/reference/modules/astro-zod/#common-data-type-validators):

```ts title="src/content.config.ts" {7-12,16-20}
import { defineCollection } from 'astro:content';
import { z } from 'astro/zod';
import { glob, file } from 'astro/loaders';
import { defineCollection } from "astro:content";
import { z } from "astro/zod";
import { glob, file } from "astro/loaders";

const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/data/blog" }),
Expand All @@ -346,7 +349,7 @@ const blog = defineCollection({
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
})
}),
});
const dogs = defineCollection({
loader: file("src/data/dogs.json"),
Expand Down Expand Up @@ -381,27 +384,27 @@ With the [`reference()` function](/en/reference/modules/astro-content/#reference
A common example is a blog post that references reusable author profiles stored as JSON, or related post URLs stored in the same collection:

```ts title="src/content.config.ts"
import { defineCollection, reference } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
import { defineCollection, reference } from "astro:content";
import { glob } from "astro/loaders";
import { z } from "astro/zod";

const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
loader: glob({ base: "./src/content/blog", pattern: "**/*.{md,mdx}" }),
schema: z.object({
title: z.string(),
// Reference a single author from the `authors` collection by `id`
author: reference('authors'),
author: reference("authors"),
// Reference an array of related posts from the `blog` collection by `id`
relatedPosts: z.array(reference('blog')),
})
relatedPosts: z.array(reference("blog")),
}),
});

const authors = defineCollection({
loader: glob({ pattern: '**/*.json', base: "./src/data/authors" }),
loader: glob({ pattern: "**/*.json", base: "./src/data/authors" }),
schema: z.object({
name: z.string(),
portfolio: z.url(),
})
}),
});

export const collections = { blog, authors };
Expand Down Expand Up @@ -484,14 +487,19 @@ Once queried, you can render Markdown and MDX entries to HTML using the [`render

```astro title="src/pages/blog/post-1.astro" {2,6,10}
---
import { getEntry, render } from 'astro:content';
import { getEntry, render } from "astro:content";

const entry = await getEntry('blog', 'post-1');
const entry = await getEntry("blog", "post-1");

if (!entry) {
throw new Error("Entry not found");
}

const { Content } = await render(entry);
---

<h1>{entry.data.title}</h1>
<p>Published on: {entry.data.published.toDateString()}</p>
<p>Published on: {entry.data.pubDate.toDateString()}</p>
<Content />
```

Expand Down Expand Up @@ -564,10 +572,15 @@ Then, you can use the `getEntry()` function again (or `getEntries()` to retrieve

```astro title="src/pages/blog/adventures-in-space.astro"
---
import { getEntry, getEntries } from 'astro:content';
import { getEntry, getEntries } from "astro:content";

// First, query a blog post
const blogPost = await getEntry('blog', 'Adventures in Space');
const blogPost = await getEntry("blog", "Adventures in Space");

// If the blog post doesn't exist, throw an error
if (!blogPost) {
throw new Error("Blog post not found");
}

// Retrieve a single reference item: the blog post's author
// Equivalent to querying `{collection: "authors", id: "ben-holmes"}`
Expand All @@ -584,9 +597,7 @@ const relatedPosts = await getEntries(blogPost.data.relatedPosts);
<!-- ... -->

<h2>You might also like:</h2>
{relatedPosts.map(post => (
<a href={post.id}>{post.data.title}</a>
))}
{relatedPosts.map((post) => <a href={post.id}>{post.data.title}</a>)}
```

## Generating Routes from Content
Expand Down Expand Up @@ -797,16 +808,20 @@ You can use these functions to access your live data, passing the name of the co
---
export const prerender = false; // Not needed in 'server' mode

import { getLiveCollection, getLiveEntry } from 'astro:content';
import { getLiveCollection, getLiveEntry } from "astro:content";

if (!Astro.params.slug) {
return Astro.redirect('/404');
}

// Use loader-specific filters
const { entries: draftArticles } = await getLiveCollection('articles', {
status: 'draft',
author: 'john-doe',
const { entries: draftArticles } = await getLiveCollection("articles", {
status: "draft",
author: "john-doe",
});

// Get a specific product by ID
const { entry: product } = await getLiveEntry('products', Astro.params.slug);
const { entry: product } = await getLiveEntry("products", Astro.params.slug);
---
```

Expand All @@ -820,10 +835,14 @@ You also have access to any [error returned by the live loader](/en/reference/co
---
export const prerender = false; // Not needed in 'server' mode

import { getLiveEntry, render } from 'astro:content';
const { entry, error } = await getLiveEntry('articles', Astro.params.id);
if (error) {
return Astro.rewrite('/404');
if (!Astro.params.id) {
return Astro.redirect("/404");
}

import { getLiveEntry, render } from "astro:content";
const { entry, error } = await getLiveEntry("articles", Astro.params.id);
if (!entry || error) {
return Astro.rewrite("/404");
}

const { Content } = await render(entry);
Expand Down Expand Up @@ -851,18 +870,22 @@ You can use `instanceof` to check the type of an error at runtime:
---
export const prerender = false; // Not needed in 'server' mode

import { LiveEntryNotFoundError } from 'astro/content/runtime';
import { getLiveEntry } from 'astro:content';
import { LiveEntryNotFoundError } from "astro/content/runtime";
import { getLiveEntry } from "astro:content";

if (!Astro.params.id) {
return Astro.redirect("/404");
}

const { entry, error } = await getLiveEntry('products', Astro.params.id);
const { entry, error } = await getLiveEntry("products", Astro.params.id);

if (error) {
if (error instanceof LiveEntryNotFoundError) {
console.error(`Product not found: ${error.message}`);
Astro.response.status = 404;
} else {
console.error(`Error loading product: ${error.message}`);
return Astro.redirect('/500');
return Astro.redirect("/500");
}
}
---
Expand Down
9 changes: 5 additions & 4 deletions src/content/docs/en/guides/images.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -468,11 +468,12 @@ For example, you could create a component for your blog post images that receive

```astro title="src/components/BlogPostImage.astro"
---
import { Image } from 'astro:assets';
import { Image } from "astro:assets";

const { src, ...attrs } = Astro.props;
const { alt, src, ...attrs } = Astro.props;
---
<Image src={src} {...attrs} />

<Image alt={alt} src={src} {...attrs} />

<style>
img {
Expand Down Expand Up @@ -649,7 +650,7 @@ const optimizedBackground = await getImage({ src: myBackground, format: "avif" }
<div id="background" data-src={optimizedBackground.src}></div>

<script>
const src = document.getElementById("background").dataset.src;
const src = document.getElementById("background")?.dataset.src;
// use src client-side as needed
</script>
```
Expand Down
12 changes: 9 additions & 3 deletions src/content/docs/en/guides/markdown-content.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ Here is my _great_ post!

```astro title="src/pages/my-posts.astro"
---
import * as greatPost from './posts/great-post.md';
import * as greatPost from "./posts/great-post.md";
const compiled = await greatPost.compiledContent();
const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));
const posts = Object.values(import.meta.glob("./posts/*.md", { eager: true }));
---

<p>{greatPost.frontmatter.title}</p>
Expand All @@ -62,7 +62,13 @@ const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));

<p>Post Archive:</p>
<ul>
{posts.map(post => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
{
posts.map((post: any) => (
<li>
<a href={post.url}>{post.frontmatter.title}</a>
</li>
))
}
</ul>
```

Expand Down
Loading