astro/docs/core-concepts/collections.md
2021-07-07 20:10:09 +00:00

213 lines
8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
layout: ~/layouts/Main.astro
title: Collections
---
**Collections** are a special type of [Page](./astro-pages) that help you generate multiple pages from a larger set of data. Example use-cases include:
- Pagination: `/posts/1`, `/posts/2`, etc.
- Grouping content by author: `/author/fred`, `/author/matthew`, etc.
- Grouping content by some tag: `/tags/red`, `/tags/blue`, etc.
- Working with remote data
- Mixing remote and local data
**Use a Collection when you need to generate multiple pages from a single template.** If you just want to generate a single page (ex: a long list of every post on your site) then you can just fetch that data on a normal Astro page without using the Collection API.
## Using Collections
To create a new Astro Collection, you must do three things:
1. Create a new file in the `src/pages` directory that starts with the `$` symbol. This is required to enable the Collections API.
- Example: `src/pages/$posts.astro` -> `/posts/1`, `/posts/2`, etc.
- Example: `src/pages/$tags.astro` -> `/tags/:tag` (or `/tags/:tag/1`)
2. Define and export the `collection` prop: `collection.data` is how you'll access the data for every page in the collection. Astro populates this prop for you automatically. It MUST be named `collection` and it must be exported.
- Example: `const { collection } = Astro.props;`
3. Define and export `createCollection` function: this tells Astro how to load and structure your collection data. Check out the examples below for documentation on how it should be implemented. It MUST be named `createCollection` and it must be exported.
- Example: `export async function createCollection() { /* ... */ }`
- API Reference: [createCollection][collection-api]
## Example: Simple Pagination
```jsx
---
// Define the `collection` prop.
const { collection } = Astro.props;
// Define a `createCollection` function.
export async function createCollection() {
const allPosts = Astro.fetchContent('../posts/*.md'); // fetch local posts.
allPosts.sort((a, b) => a.title.localeCompare(b.title)); // sort by title.
return {
// Because you are not doing anything more than simple pagination,
// its fine to just return the full set of posts for the collection data.
async data() { return allPosts; },
// number of posts loaded per page (default: 25)
pageSize: 10,
};
}
---
<html lang="en">
<head>
<title>Pagination Example: Page Number {collection.page.current}</title>
</head>
<body>
{collection.data.map((post) => (
<h1>{post.title}</h1>
<time>{formatDate(post.published_at)}</time>
<a href={post.url}>Read Post</a>
))}
</body>
</html>
```
## Example: Pagination Metadata
```jsx
---
// In addition to `collection.data` usage illustrated above, the `collection`
// prop also provides some important metadata for you to use, like: `collection.page`,
// `collection.url`, `collection.start`, `collection.end`, and `collection.total`.
// In this example, we'll use these values to do pagination in the template.
const { collection } = Astro.props;
export async function createCollection() { /* See Previous Example */ }
---
<html lang="en">
<head>
<title>Pagination Example: Page Number {collection.page.current}</title>
<link rel="canonical" href={collection.url.current} />
<link rel="prev" href={collection.url.prev} />
<link rel="next" href={collection.url.next} />
</head>
<body>
<main>
<h5>Results {collection.start + 1}{collection.end + 1} of {collection.total}</h5>
{collection.data.map((post) => (
<h1>{post.title}</h1>
<time>{formatDate(post.published_at)}</time>
<a href={post.url}>Read Post</a>
))}
</main>
<footer>
<h4>Page {collection.page.current} / {collection.page.last}</h4>
<nav class="nav">
<a class="prev" href={collection.url.prev || '#'}>Prev</a>
<a class="next" href={collection.url.next || '#'}>Next</a>
</nav>
</footer>
</body>
</html>
```
## Example: Grouping Content by Tag, Author, etc.
```jsx
---
// Define the `collection` prop.
const { collection } = Astro.props;
// Define a `createCollection` function.
// In this example, we'll customize the URLs that we generate to
// create a new page to group every pokemon by first letter of their name.
export async function createCollection() {
const allPokemonResponse = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=150`);
const allPokemonResult = await allPokemonResponse.json();
const allPokemon = allPokemonResult.results;
const allLetters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
return {
// `routes` defines the total collection of routes as `params` data objects.
// In this example, we format each letter (ex: "a") to params (ex: {letter: "a"}).
routes: allLetters.map(letter => {
const params = {letter};
return params;
}),
// `permalink` defines the final URL for each route object defined in `routes`.
// It should always match the file location (ex: `src/pages/$pokemon.astro`).
permalink: ({ params }) => `/pokemon/${params.letter}`,
// `data` is now responsible for return the data for each page.
// Luckily we had already loaded all of the data at the top of the function,
// so we just filter the data here to group pages by first letter.
// If you needed to fetch more data for each page, you can do that here as well.
async data({ params }) {
return allPokemon.filter((pokemon) => pokemon.name[0] === params.letter);
},
// Finally, `pageSize` and `pagination` is still on by default. Because
// we don't want to paginate the already-grouped pages a second time, we'll
// disable pagination.
pageSize: Infinity,
};
}
---
<html lang="en">
<head>
<title>Pokemon: {collection.params.letter}</head>
<body>
{collection.data.map((pokemon) => (<h1>{pokemon.name}</h1>))}
</body>
</html>
```
## Example: Individual Pages from a Collection
```jsx
---
// Define the `collection` prop.
const { collection } = Astro.props;
// Define a `createCollection` function.
// In this example, we'll create a new page for every single pokemon.
export async function createCollection() {
const allPokemonResponse = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=150`);
const allPokemonResult = await allPokemonResponse.json();
const allPokemon = allPokemonResult.results;
return {
// `routes` defines the total collection of routes as data objects.
routes: allPokemon.map((pokemon, i) => {
const params = {name: pokemon.name, index: i};
return params;
}),
// `permalink` defines the final URL for each route object defined in `routes`.
permalink: ({ params }) => `/pokemon/${params.name}`,
// `data` is now responsible for return the data for each page.
// Luckily we had already loaded all of the data at the top of the function,
// so we just filter the data here to group pages by first letter.
// If you needed to fetch more data for each page, you can do that here as well.
// Note: data() is expected to return an array!
async data({ params }) {
return [allPokemon[params.index]];
},
// Note: The default pageSize is fine because technically only one data object
// is ever returned per route. We set it to Infinity in this example for completeness.
pageSize: Infinity,
};
}
---
<html lang="en">
<head>
<title>Pokemon: {collection.params.name}</head>
<body>
Who's that pokemon? It's {collection.data[0].name}!
</body>
</html>
```
## Tips
- If you find yourself duplicating markup across many pages and collections, you should probably be using more reusable components.
### 📚 Further Reading
- [Fetching data in Astro][docs-data]
- API Reference: [collection][collection-api]
- API Reference: [createCollection()][create-collection-api]
- API Reference: [Creating an RSS feed][create-collection-api]
[docs-data]: ../README.md#-fetching-data
[collection-api]: ./api.md#collection
[create-collection-api]: ./api.md#createcollection
[example-blog]: ../examples/blog
[fetch-content]: ./api.md#fetchcontent