Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tutorial] new tutorial extensions #4876

Merged
merged 41 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
5788338
initial structure and content for 2 new tutorial add-ons
sarah11918 Sep 29, 2023
30b53fd
current sidebar idea
sarah11918 Sep 29, 2023
2026f6a
updated to prerequisites for cc
sarah11918 Sep 29, 2023
7e701b0
first draft - content collections tutorial
sarah11918 Sep 29, 2023
1da69e6
made it clear view transitions is placeholder content only
sarah11918 Sep 29, 2023
f79ae0d
Merge branch 'main' into more-tut
sarah11918 Sep 29, 2023
63e1a19
remove links referring to other page
sarah11918 Sep 30, 2023
53e7c98
link fix
sarah11918 Sep 30, 2023
ce88863
revert heading change in sidebar
sarah11918 Sep 30, 2023
f8bea26
reorder sections to create reusable structure for these guides
sarah11918 Sep 30, 2023
76fb84f
replicating mini tut page structure for view transitions
sarah11918 Sep 30, 2023
bca33e8
draft of intro view transitions
sarah11918 Sep 30, 2023
c243b49
typos
sarah11918 Sep 30, 2023
b6ac69f
basic instructions to get default view transitions
sarah11918 Sep 30, 2023
54b54c0
avoid sidebar highlighting problem
sarah11918 Oct 9, 2023
b4ff214
adds instructions for updating scripts with `astro:page-load`
sarah11918 Oct 10, 2023
7b8eb5f
update theme toggle example
sarah11918 Oct 10, 2023
6a7df36
add animation examples
sarah11918 Oct 10, 2023
c0be394
data-astro-reload example
sarah11918 Oct 10, 2023
9034c86
added conclusion
sarah11918 Oct 10, 2023
a6a386e
update link for new location of extensions
sarah11918 Oct 10, 2023
2d36196
add the astro page swap script example
sarah11918 Oct 10, 2023
e6808f9
fix link bc it's actually called astro after swap
sarah11918 Oct 10, 2023
d0fad9e
Merge branch 'main' into more-tut
sarah11918 Oct 10, 2023
03ed190
update page titles and descriptions to match each other
sarah11918 Oct 10, 2023
cbc1219
add missing word
sarah11918 Oct 12, 2023
7d581a5
fix JS style
sarah11918 Oct 12, 2023
c63e217
Apply suggestions from Talking and Doc'ing code review
sarah11918 Oct 12, 2023
e69368d
Merge branch 'main' into more-tut
sarah11918 Oct 13, 2023
9d6a13e
Merge branch 'main' into more-tut
sarah11918 Oct 13, 2023
a2b06ce
improve message on content collections page
sarah11918 Oct 13, 2023
e078418
Yan nits!
sarah11918 Oct 13, 2023
7e6c503
less dense!
sarah11918 Oct 13, 2023
38e578c
added some tutorial UI.. for fun!
sarah11918 Oct 13, 2023
3f1b8e9
fix link
sarah11918 Oct 13, 2023
933fba3
missing wordl
sarah11918 Oct 13, 2023
e6a2183
missing space
sarah11918 Oct 15, 2023
5030975
extra space!
sarah11918 Oct 15, 2023
771df72
updated links at end of build a blog tutorial; renamed folder
sarah11918 Oct 15, 2023
01a51b6
fix link after folder rename
sarah11918 Oct 15, 2023
87779ae
Merge branch 'main' into more-tut
sarah11918 Oct 15, 2023
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
205 changes: 5 additions & 200 deletions src/content/docs/en/guides/content-collections.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -536,205 +536,11 @@ const { Content } = await entry.render();
---
```

## Migrating from File-Based Routing
This guide shows you how to convert an existing Astro project with Markdown files in the `src/pages/` folder to content collections. It uses the [Build a Blog tutorial's finished project](https://github.com/withastro/blog-tutorial-demo) as an example.

1. [Upgrade](/en/guides/upgrade-to/v3/) to Astro v3.0 or later, and upgrade all integrations to their latest versions.

2. [Set up TypeScript](/en/guides/content-collections/#setting-up-typescript) for content collections.
3. Create at least one collection (folder in `src/content/`) and move your Markdown and MDX pages from `src/pages/` into these subdirectories of `src/content/`. Collections work best when all files in the same collection have similar frontmatter properties. So, choose your new folder structure to reflect similar types of pages.

For example, to migrate the [blog posts in the tutorial](/en/tutorial/2-pages/2/), move the contents of `src/pages/posts/` to `src/content/posts/`.

4. Create a `src/content/config.ts` file and [define a schema](/en/guides/content-collections/#defining-a-collection-schema) for each content type. For the blog, we only have one content type, `posts`:

```ts title="src/content/config.ts"
// Import utilities from `astro:content`
import { z, defineCollection } from "astro:content";
// Define a `type` and `schema` for each collection
const postsCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
pubDate: z.date(),
description: z.string(),
author: z.string(),
image: z.object({
url: z.string(),
alt: z.string()
}),
tags: z.array(z.string())
})
});
// Export a single `collections` object to register your collection(s)
export const collections = {
posts: postsCollection,
};
```

:::tip
If your editor doesn't recognize `astro:content`, make sure you're on the latest version of Astro and try restarting the dev server.
:::


5. [Generate routes from your collections](/en/guides/content-collections/#generating-routes-from-content). Inside a collection, Markdown and MDX files no longer automatically become pages using Astro's [file-based routing](/en/guides/markdown-content/#file-based-routing), so you must generate the pages yourself.

For the tutorial, create a `src/pages/posts/[...slug].astro`. This page will use [dynamic routing](/en/core-concepts/routing/#dynamic-routes) to generate a page for each collection entry.

This page will also need to [query your collection](#querying-collections) to fetch page slugs and make the page content available to each route.

Render your post `<Content />` within the layout for your Markdown or MDX pages. This allows you to specify a common layout for all of your posts.

```astro title="src/pages/posts/[...slug].astro"
---
import { getCollection } from 'astro:content';
import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';

export async function getStaticPaths() {
const blogEntries = await getCollection('posts');
return blogEntries.map(entry => ({
params: { slug: entry.slug }, props: { entry },
}));
}

const { entry } = Astro.props;
const { Content } = await entry.render();
---
<MarkdownPostLayout frontmatter={entry.data}>
<Content />
</MarkdownPostLayout>
```

6. Remove the `layout` definition in each individual post's frontmatter. Your content is now wrapped in a layout when rendered, and this property is no longer needed.

```md title="src/content/post-1.md" del={2}
---
layout: ../../layouts/MarkdownPostLayout.astro
title: 'My First Blog Post'
pubDate: 2022-07-01
...
---
```

7. Replace `Astro.glob()` with [`getCollection()`](/en/reference/api-reference/#getcollection) to fetch content and metadata from your Markdown files. You will also need to update references to the returned post object, since you will now find your frontmatter values on the `data` property.

The blog index page in the tutorial lists a card for each post. This becomes:

```astro title="src/pages/blog.astro" "post.data" "getCollection(\"posts\")" "'/posts/' + post.slug"
---
import { getCollection } from "astro:content";
import BaseLayout from "../layouts/BaseLayout.astro";
import BlogPost from "../components/BlogPost.astro";

const pageTitle = "My Astro Learning Blog";
const allPosts = await getCollection("posts");
---

<BaseLayout pageTitle={pageTitle}>
<p>This is where I will post about my journey learning Astro.</p>
<ul>
{
allPosts.map((post) => (
<BlogPost url={'/posts/' + post.slug} title={post.data.title} />
))
}
</ul>
</BaseLayout>
```

The tutorial blog project also dynamically generates a page for each tag. This page now becomes:

```astro title="src/pages/tags/[tag].astro" "post.data" "getCollection(\"posts\")" "post.data.title" "'/posts/' + post.slug"
---
import { getCollection } from "astro:content";
import BaseLayout from "../../layouts/BaseLayout.astro";
import BlogPost from "../../components/BlogPost.astro";

export async function getStaticPaths() {
const allPosts = await getCollection("posts");
const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())];

return uniqueTags.map((tag) => {
const filteredPosts = allPosts.filter((post) =>
post.data.tags.includes(tag)
);
return {
params: { tag },
props: { posts: filteredPosts },
};
});
}

const { tag } = Astro.params;
const { posts } = Astro.props;
---

<BaseLayout pageTitle={tag}>
<p>Posts tagged with {tag}</p>
<ul>
{ posts.map((post) => <BlogPost url={'/posts/' + post.slug} title={post.data.title} />) }
</ul>
</BaseLayout>
```

The same logic appears in the tag index page, which becomes:

```astro title="src/pages/tags/index.astro" "post.data" "getCollection(\"posts\")"
---
import { getCollection } from "astro:content";
import BaseLayout from "../../layouts/BaseLayout.astro";
const allPosts = await getCollection("posts");
const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
const pageTitle = "Tag Index";
---
...
```

:::note
Any individual Markdown or MDX file imports should be replaced by [`getEntry()`](/en/reference/api-reference/#getentry).
:::

8. Update the code that uses the publish date in the `layouts/MarkdownPostLayout.astro` file.

Previously, `pubDate` was a string. Now, after introducing types for your posts' frontmatter, `pubDate` is a `Date`.
To render the date, convert it to a string:

```astro title="src/layouts/MarkdownPostLayout.astro" "frontmatter.pubDate.toDateString()"
...
<BaseLayout pageTitle={frontmatter.title}>
<p>{frontmatter.pubDate.toDateString()}</p>
<p><em>{frontmatter.description}</em></p>
<p>Written by: {frontmatter.author}</p>
<img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} />
...
```


Lastly, the tutorial blog project includes an RSS feed. This function must also use `getCollection` and the `data` object, and be converted to an async function to do so:

```js title="src/pages/rss.xml.js" {4-5, 10-15}
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";

export async function GET() {
const posts = await getCollection('posts');
return rss({
title: 'Astro Learner | Blog',
description: 'My journey learning Astro',
site: 'https://my-blog-site.netlify.app',
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.pubDate,
description: post.data.description,
link: `/posts/${post.slug}/`,
})),
customData: `<language>en-us</language>`,
});
}
```

For the full example of the blog tutorial using content collections, see the [Content Collections branch](https://github.com/withastro/blog-tutorial-demo/tree/content-collections) of the tutorial repo.
## Migrating from File-Based Routing
sarah11918 marked this conversation as resolved.
Show resolved Hide resolved

If you have an existing Astro project, such as a blog, that uses Markdown or MDX files in subfolders inside `src/pages/`, consider migrating related content or data files to content collections.

See how to convert a basic blog example from `src/pages/posts/` to `src/content/posts` in our [step-by-step tutorial](/en/tutorials/add-content-collections/) that uses the codebase from [the Build a Blog tutorial's finished project](https://github.com/withastro/blog-tutorial-demo).

## Modifying Frontmatter with Remark

Expand All @@ -755,5 +561,4 @@ const { remarkPluginFrontmatter } = await blogPost.render();

<RecipeLinks slugs={["en/recipes/reading-time" ]}/>

The remark and rehype pipelines only run when your content is rendered, which explains why `remarkPluginFrontmatter` is only available after you call `render()` on your content entry. In contrast, `getCollection()` and `getEntry()` cannot return these values directly because they do not render your content.

The remark and rehype pipelines only run when your content is rendered, which explains why `remarkPluginFrontmatter` is only available after you call `render()` on your content entry. In contrast, `getCollection()` and `getEntry()` cannot return these values directly because they do not render your content.
2 changes: 1 addition & 1 deletion src/content/docs/en/tutorial/6-islands/3.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Welcome to the universe, astronaut. 👩🏼‍🚀👨🏿‍🚀🧑‍🚀

## Next Steps

Continue with our [guide to migrate this project to content collections](/en/guides/content-collections/#migrating-from-file-based-routing)
Continue with either of our tutorial extensions to [add view transitions to this project](/en/tutorials/add-view-transitions/) or to [add a content collection to manage your plog posts](/en/tutorials/add-content-collections/)

[Start a new Astro project](/en/getting-started/)

Expand Down
Loading
Loading