Astro: Content Collections — фильтр по тегу, сортировка по дате
Работа с Content Collections: получение коллекции, фильтрация по тегам и сортировка по дате публикации.
Как использовать
- Импортируйте getCollection из astro:content и вызовите с именем коллекции (например blog) и функцией-фильтром по data.
- Примените .sort() по data.pubDate.valueOf() для сортировки по дате (новые первыми — b - a).
- Используйте в frontmatter страницы или компонента; для пагинации добавьте .slice(offset, offset + limit).
В Astro списки постов и статей обычно хранят в Content Collections. Чтобы вывести только записи по тегу или категории и в нужном порядке (например новые первыми), коллекцию нужно фильтровать при получении и сортировать по дате. Проблема возникает, когда вызывают getCollection('blog') без фильтра и потом фильтруют вручную — лишняя работа и риск забыть исключить черновики. Симптомы: на странице отображаются черновики (draft), порядок по дате не тот или постов больше, чем нужно. Ниже — примеры фильтрации по одному тегу, по нескольким (AND/OR), по категории, сортировка по pubDate и получение последних N записей; плюс проверка в dev-режиме.
Решение
Получение коллекции контента с фильтрацией по тегам и сортировкой по дате. Используйте для вывода списков постов, фильтрации по категориям.
import { getCollection } from 'astro:content';
// 1. Получить все посты с фильтром по тегу
const postsWithTag = (await getCollection('blog', ({ data }) => {
return !data.draft && data.tags?.includes('astro');
}))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
// 2. Получить посты по нескольким тегам (AND)
const postsWithMultipleTags = (await getCollection('blog', ({ data }) => {
return !data.draft
&& data.tags?.includes('astro')
&& data.tags?.includes('tutorial');
}))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
// 3. Получить посты по любому из тегов (OR)
const targetTags = ['astro', 'react', 'vue'];
const postsWithAnyTag = (await getCollection('blog', ({ data }) => {
return !data.draft
&& data.tags?.some(tag => targetTags.includes(tag));
}))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
// 4. Получить последние N постов
const latestPosts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
.slice(0, 5);
// 5. Получить посты по категории и отсортировать
const categoryPosts = (await getCollection('blog', ({ data }) => {
return !data.draft && data.category === 'Frontend';
}))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
// 6. Получить уникальные теги из всех постов
const allPosts = await getCollection('blog', ({ data }) => !data.draft);
const allTags = Array.from(
new Set(allPosts.flatMap(post => post.data.tags || []))
).sort();
Использование в компоненте:
---
// src/components/PostList.astro
import { getCollection } from 'astro:content';
const posts = (await getCollection('blog', ({ data }) =>
!data.draft && data.tags?.includes('tutorial')
))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---
<ul>
{posts.map(post => (
<li>
<a href={`/blog/${post.slug}/`}>{post.data.title}</a>
<time>{post.data.pubDate.toLocaleDateString('ru')}</time>
</li>
))}
</ul>
Фильтр в getCollection() применяется на этапе получения коллекции, что эффективнее фильтрации после. getCollection() возвращает промис — не забывайте await. Сортировка по pubDate.valueOf() корректна только если в схеме коллекции pubDate — тип Date (проверьте src/content/config.ts).
Проверка
- Запуск dev и проверка страницы со списком — убедитесь, что отображаются только посты с нужным тегом и в порядке убывания даты:
pnpm dev
Откройте страницу, которая использует фильтр (например /blog/ или страницу тега). Черновики (draft: true) не должны попадать в список; порядок — сначала самые новые.
- Диагностика: сколько записей вернула коллекция — во frontmatter временно выведите длину массива:
---
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
console.log('Posts count:', posts.length);
---
Сверьте с ожидаемым количеством постов в src/content/blog/.
- Проверка схемы коллекции — в
src/content/config.tsу коллекцииblogполеpubDateдолжно быть с типомz.date()или аналогом, иначе.valueOf()может быть недоступен или дата окажется строкой.
Типичные ошибки
- Черновики попадают в список — в фильтр не добавлено условие
!data.draft. В callbackgetCollection('blog', ({ data }) => ...)обязательно включайте проверку на draft, если в коллекции есть черновики. - Сортировка не срабатывает или порядок неверный — убедитесь, что
pubDateв схеме — Date. Если в frontmatter строка, приведите к дате в схеме (напримерz.coerce.date()). Для «сначала новые» используйтеb.data.pubDate.valueOf() - a.data.pubDate.valueOf(). - getCollection не найден или коллекция пустая — проверьте имя коллекции (должно совпадать с папкой в
src/content/) и что вconfig.tsколлекция зарегистрирована. Импорт:import { getCollection } from 'astro:content'.
Где применять
- Статические страницы блога: главная с последними постами, страницы по тегам/категориям, виджет «последние N записей». Сборка — на этапе build, данные берутся из Content Collections.
- Dev и prod: один и тот же код; в prod после
pnpm buildсписок фиксируется в HTML.
Связанные сниппеты: Статический вывод по умолчанию, Статическая страница с минимальным HTML, API Route (endpoint).