Astro calendar_today 23 апр. 2026 г. schedule 2 мин

Блог на Astro: Пошаговый гайд с нуля в 2026 году

Пошаговое руководство по созданию блога на Astro.js: структура проекта, MDX-статьи, теги, пагинация, RSS, комментарии и деплой на Cloudflare Pages.

person
Журналист
Автор
Блог на Astro — пошаговый гайд по созданию

Персональный блог — самый популярный use-case для Astro. Неудивительно: чистый HTML, нулевой JavaScript по умолчанию, встроенные Content Collections и бесплатный деплой на Cloudflare Pages делают Astro идеальной платформой для блогинга. Этот гайд проведёт вас от npm create до живого блога.

Почему Astro — лучший выбор для блога

  • PageSpeed 100/100 из коробки — важно для SEO и читателей
  • MDX — пишите статьи в Markdown с поддержкой React/Astro компонентов
  • Content Collections — типизированные статьи с Zod-валидацией
  • Бесплатный хостинг — Cloudflare Pages, GitHub Pages
  • RSS — одна интеграция, 5 минут настройки

Шаг 1: Создание проекта

code
npm create astro@latest my-blog
cd my-blog
npm install

Выберите шаблон Blog — он уже включает базовую структуру для блога.

Или создайте с нуля и установите нужные интеграции:

code
npx astro add mdx sitemap

Шаг 2: Структура проекта

code
my-blog/
├── public/
│   └── favicon.svg
├── src/
│   ├── assets/
│   │   └── covers/          # Обложки статей
│   ├── components/
│   │   ├── Header.astro
│   │   ├── Footer.astro
│   │   ├── ArticleCard.astro
│   │   └── BaseHead.astro
│   ├── content/
│   │   └── blog/            # MDX-статьи
│   │       ├── first-post.mdx
│   │       └── second-post.mdx
│   ├── layouts/
│   │   ├── BaseLayout.astro  # Обёртка с head, header, footer
│   │   └── PostLayout.astro  # Шаблон статьи
│   └── pages/
│       ├── index.astro       # Главная
│       ├── blog/
│       │   ├── index.astro   # Список статей
│       │   └── [slug].astro  # Страница статьи
│       └── rss.xml.js        # RSS-лента
└── astro.config.mjs

Шаг 3: Content Collection для статей

code
// src/content.config.ts
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.mdx', base: './src/content/blog' }),
  schema: ({ image }) =>
    z.object({
      title: z.string().max(100),
      description: z.string().max(160),
      date: z.coerce.date(),
      author: z.string().default('Автор'),
      tags: z.array(z.string()).default([]),
      cover: image().optional(),
      coverAlt: z.string().optional(),
      draft: z.boolean().default(false),
    }),
});

export const collections = { blog };

Шаг 4: Frontmatter статьи

code
---
title: 'Моя первая статья в блоге на Astro'
description: 'Подробный разбор того, как я создал блог на Astro за выходные'
date: 2026-04-23
author: 'Имя Автора'
tags: ['Astro', 'Веб-разработка', 'Tutorial']
---

## Введение

Это мой первый пост на новом блоге, работающем на **Astro.js**...

Шаг 5: Список статей с пагинацией

code
---
// src/pages/blog/index.astro
import { getCollection } from 'astro:content';

const allPosts = await getCollection('blog', ({ data }) => !data.draft);
const sortedPosts = allPosts.sort(
  (a, b) => b.data.date.getTime() - a.data.date.getTime(),
);
---

<section>
  <h1>Все статьи</h1>
  <ul>
    {
      sortedPosts.map((post) => (
        <li>
          <a href={`/blog/${post.id.replace('.mdx', '')}/`}>
            <h2>{post.data.title}</h2>
            <p>{post.data.description}</p>
            <time>{post.data.date.toLocaleDateString('ru-RU')}</time>
          </a>
        </li>
      ))
    }
  </ul>
</section>

Пагинация через встроенный paginate():

code
---
// src/pages/blog/[page].astro
import { getCollection } from 'astro:content';

export async function getStaticPaths({ paginate }) {
  const posts = await getCollection('blog', ({ data }) => !data.draft);
  const sorted = posts.sort((a, b) => b.data.date - a.data.date);
  return paginate(sorted, { pageSize: 10 });
}

const { page } = Astro.props;
---

{page.data.map((post) => <ArticleCard post={post} />)}

<nav>
  {page.url.prev && <a href={page.url.prev}>← Назад</a>}
  <span>Страница {page.currentPage} из {page.lastPage}</span>
  {page.url.next && <a href={page.url.next}>Вперёд →</a>}
</nav>

Шаг 6: Страница статьи

code
---
// src/pages/blog/[slug].astro
import { getCollection, render } from 'astro:content';
import PostLayout from '../../layouts/PostLayout.astro';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({
    params: { slug: post.id.replace('.mdx', '') },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content, headings } = await render(post);
---

<PostLayout
  title={post.data.title}
  description={post.data.description}
  date={post.data.date}
  author={post.data.author}
>
  <!-- Оглавление -->
  <nav>
    {
      headings.map((h) => (
        <a href={`#${h.slug}`} style={`padding-left: ${(h.depth - 2) * 16}px`}>
          {h.text}
        </a>
      ))
    }
  </nav>

  <Content />
</PostLayout>

Шаг 7: RSS-лента

code
// src/pages/rss.xml.js
import rss from '@astrojs/rss';
import { getCollection } from 'astro:content';

export async function GET(context) {
  const posts = await getCollection('blog', ({ data }) => !data.draft);
  return rss({
    title: 'Мой блог на Astro',
    description: 'Статьи о веб-разработке',
    site: context.site,
    items: posts.map((post) => ({
      title: post.data.title,
      description: post.data.description,
      pubDate: post.data.date,
      link: `/blog/${post.id.replace('.mdx', '')}/`,
    })),
    customData: `<language>ru-RU</language>`,
  });
}

Установите интеграцию: npx astro add rss

Шаг 8: Теги и фильтрация

code
---
// src/pages/tags/[tag].astro
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  // Получить все уникальные теги
  const allTags = [...new Set(posts.flatMap((p) => p.data.tags))];

  return allTags.map((tag) => ({
    params: { tag },
    props: { posts: posts.filter((p) => p.data.tags.includes(tag)) },
  }));
}

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

<h1>Статьи с тегом: {tag}</h1>
{posts.map((post) => <ArticleCard post={post} />)}
edit

Черновики

Добавьте поле draft: true в frontmatter — статья не попадёт в список и не будет собираться. Идеально для незаконченных материалов.

schedule

Время чтения

Рассчитайте в [slug].astro: const readingTime = Math.ceil(post.body.split(' ').length / 200) минут.

image

OG-изображения

Генерируйте уникальные OG-картинки через @vercel/og или satori прямо на этапе сборки Astro.

Деплой на Cloudflare Pages (бесплатно)

code
# Сборка
npm run build

# Деплой через Wrangler CLI
npx wrangler pages deploy dist/ --project-name my-blog

Или подключите GitHub репозиторий в Cloudflare Pages Dashboard для автодеплоя при каждом git push.

Итог

Блог на Astro — от npm create до продакшна за один день. Content Collections дают типизированный контент, MDX позволяет вставлять интерактивные компоненты прямо в статьи, а SSG обеспечивает PageSpeed 100 без каких-либо усилий. Это лучший стек для персонального и корпоративного блогинга в 2026 году.

Следующий шаг — деплой на хостинг и настройка SEO.

Портрет автора Дмитрий Соколов

Senior Frontend Engineer / Tech Writer

Senior Frontend Engineer с 9-летним опытом. Специализируется на Astro.js и JAMstack.

Комментарии

Загрузка комментариев...

Оставить комментарий

Комментарии проходят модерацию перед публикацией. Правила

Рекомендуем к прочтению