DEV Community

Андрей Викулов (VProger)
Андрей Викулов (VProger)

Posted on • Originally published at viku-lov.ru on

Astro в 2025–2026: быстрый старт для фронтенд-разработчика (часть 1) — структура проекта, маршруты, MDX и Content Collections

Astro в 2025–2026: быстрый старт (часть 1)

Если коротко: Astro — это фреймворк для контентных сайтов, где важны скорость, SEO и контроль над тем, _сколько JS реально уезжает в браузер_. Блоги, документация, маркетинг, каталоги, лендинги, портфолио — его родная среда.

А если не коротко — сейчас будет.

Цель части 1: чтобы ты (или джун) смог:

  • поднять проект на Astro

  • понять структуру src/pages, компоненты, layout’ы

  • сделать маршруты (включая динамические)

  • подключить MDX и начать писать страницы/статьи

  • завести Content Collections со схемой (валидация + типы)


1) Почему Astro вообще стоит трогать в 2025–2026

Astro “по умолчанию быстрый”

Философия простая: рендерим HTML, а интерактивность добавляем _точечно_ (islands). Это почти всегда быстрее, чем “SPA везде и всегда”.

Astro “по умолчанию дружит с SEO”

Потому что результатом часто является обычная страница (HTML), понятная роботу, браузеру и человеку.

Astro 5+ сделал контент ещё удобнее

В Astro 5 появился Content Layer — более гибкая и типобезопасная система контента и загрузчиков (loaders), которая позволяет подтягивать данные не только из markdown-файлов, но и из API/ CMS/ любых источников — и всё это складывать в единый слой данных.

Плюс Astro 5 упростил "режимы рендера": статик по умолчанию, а SSR — точечно, где надо (через адаптер + prerender = false).


2) Установка и создание проекта Astro

Требования

  • Node.js LTS (обычно достаточно “последнего LTS”)
  • npm / pnpm / yarn (я покажу примеры на npm и pnpm)

Создать проект


npm create astro@latest

или

pnpm create astro@latest

Enter fullscreen mode Exit fullscreen mode

Официально именно так предлагают стартовать проект. ([Astro][1])

Дальше мастер спросит:

  • шаблон (например, blog, minimal, starter)
  • TypeScript (можно включить сразу)
  • eslint/format и т.д.

Запуск dev-сервера


npm run dev

или

pnpm dev

Enter fullscreen mode Exit fullscreen mode

3) Структура проекта Astro (чтобы не блуждать как в лабиринте)

Типичная база (упрощённо):


/

├─ astro.config.mjs

├─ package.json

├─ src/

│ ├─ pages/

│ ├─ layouts/

│ ├─ components/

│ ├─ content/ (если используешь collections)

│ └─ styles/

└─ public/

Enter fullscreen mode Exit fullscreen mode

src/pages/ — файловая маршрутизация (file-based routing)

  • src/pages/index.astro → /
  • src/pages/about.astro → /about
  • src/pages/blog/index.astro → /blog

Правило простое: файл = маршрут.

src/components/ — компоненты UI

Кнопки, карточки, шапки, футеры, “вставки в статью”.

src/layouts/ — лэйауты (общая обвязка страниц)

Обычно: базовый layout с

, шапкой/футером, контейнером, слотами.

public/ — статика “как есть”

Файлы отсюда доступны напрямую:

  • public/favicon.svg → /favicon.svg
  • public/assets/... → /assets/...

4) Первая страница на Astro: layout + slot

src/layouts/BaseLayout.astro


---

const {

title = "Site",

description = "Astro site",

} = Astro.props;

---

<!doctype html>

<html lang="ru">

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>{title}</title>

<meta name="description" content={description} />

</head>

<body>

<header style="padding:16px; border-bottom:1px solid rgba(255,255,255,.1)">

<a href="/">Главная</a>

<span style="opacity:.6; margin-left:8px">|</span>

<a href="/blog" style="margin-left:8px">Блог</a>

</header>

<main style="max-width: 860px; margin: 0 auto; padding: 24px;">

<slot />

</main>

<footer style="padding:16px; border-top:1px solid rgba(255,255,255,.1); opacity:.7">

© {new Date().getFullYear()}

</footer>

</body>

</html>

Enter fullscreen mode Exit fullscreen mode

slot — это “сюда вставится содержимое страницы”.

src/pages/index.astro


---

import BaseLayout from "../layouts/BaseLayout.astro";

---

<BaseLayout title="Главная" description="Стартовый проект на Astro">

<h1>Привет, Astro</h1>

<p>Если страница открылась — ты уже в деле.</p>

</BaseLayout>

Enter fullscreen mode Exit fullscreen mode

5) Маршруты: статические и динамические

Статический маршрут

src/pages/about.astro → /about — без сюрпризов.

Динамический маршрут (slug)

src/pages/blog/[slug].astro → /blog/что-угодно

База выглядит так (пока без контента, просто демонстрация):


---

const { slug } = Astro.params;

---

<h1>Пост: {slug}</h1>

Enter fullscreen mode Exit fullscreen mode

Дальше мы подключим Content Collections и начнём получать реальные статьи по slug.


6) Подключаем MDX в Astro (и зачем это нужно)

Что даёт MDX

MDX — это Markdown, который умеет встраивать компоненты и JS-выражения. В Astro это подключается официальной интеграцией @astrojs/mdx. (docs.astro.build)

Важные моменты из официальной документации:

  • интеграция расширяет Markdown JSX-компонентами и выражениями
  • поддерживает frontmatter в .mdx
  • .mdx пишется в синтаксисе MDX, а не в “astro-html” (docs.astro.build)

Установка MDX


npx astro add mdx

или

pnpm astro add mdx

Enter fullscreen mode Exit fullscreen mode

После этого в astro.config.mjs появится интеграция.


7) Пишем первую MDX-страницу

Создай файл: src/pages/guide.mdx


---

title: "Гайд по Astro"

description: "Пробная MDX-страница"

---

MDX в Astro работает ✅

Это обычный Markdown… но с бонусами.

- списки
- код
- и компоненты ниже

Enter fullscreen mode Exit fullscreen mode

Открой /guide — должна отрендериться страница.


8) Встраиваем компоненты в MDX (самое вкусное)

Сделаем компонент-выделялку:

src/components/Callout.astro


---

const { type = "info", title = "Важно" } = Astro.props;

const styles = {

info: "background: rgba(0, 180, 255, .08); border: 1px solid rgba(0, 180, 255, .35);",

warn: "background: rgba(255, 180, 0, .08); border: 1px solid rgba(255, 180, 0, .35);",

danger: "background: rgba(255, 80, 80, .08); border: 1px solid rgba(255, 80, 80, .35);",

};

---

<section style={padding: 14px 16px; border-radius: 12px; ${styles[type] ?? styles.info}}>

<strong style="display:block; margin-bottom: 6px">{title}</strong>

<div><slot /></div>

</section>

Enter fullscreen mode Exit fullscreen mode

Теперь используем в MDX:

src/pages/guide.mdx


---

title: "Гайд по Astro"

description: "Пробная MDX-страница"

---

import Callout from "../components/Callout.astro";

MDX + компоненты

<Callout type="info" title="Смысл MDX">

Ты пишешь статью как Markdown, но можешь вставлять “живые” компоненты.

</Callout>

<Callout type="warn" title="Пара слов о порядке">

Не превращай статью в React-приложение. Это всё ещё контент.

</Callout>

Enter fullscreen mode Exit fullscreen mode

9) Content Collections: контент как нормальные данные (а не “файлики в папке”)

Зачем вообще collections

Чтобы:

  • описать схему фронтматтера (какие поля обязательны)
  • ловить ошибки на этапе билда (а не в проде)
  • получать типизацию (особенно приятно в TS)

Официально: Astro использует Zod для схем коллекций и может валидировать данные + выдавать типы. (docs.astro.build)

Минимальная настройка src/content.config.ts

Да, именно так: один файл конфигурации — и дальше ты работаешь с контентом как с данными.


// src/content.config.ts

import { defineCollection } from "astro:content";

import { z } from "astro/zod";

const blog = defineCollection({

schema: z.object({

title: z.string().min(5),

description: z.string().min(20),

pubDate: z.date(),

updatedDate: z.date().optional(),

tags: z.array(z.string()).default([]),

category: z.string().default("Frontend"),

draft: z.boolean().default(false),

cover: z.string().optional(),

slug: z.string().min(3),

locale: z.string().default("ru"),

originalSlug: z.string().optional(),

}),

});

export const collections = { blog };

Enter fullscreen mode Exit fullscreen mode

Почему astro/zod? В документации прямо сказано, что z импортируется оттуда как реэкспорт Zod. (docs.astro.build)


10) Создаём первую статью в коллекции

Создай файл:

src/content/blog/astro-part-1.mdx


---

title: "Astro: старт (часть 1)"

description: "Статья в content collection, чтобы потом красиво собрать список и страницы."

pubDate: 2026-01-05T16:00:00.000Z

tags:

- astro
- mdx
- frontend

category: Frontend

draft: false

slug: "astro-part-1"

originalSlug: "astro-part-1"

---

import Callout from "../../components/Callout.astro";

Astro: старт (часть 1)

<Callout type="info" title="Это пост из коллекции">

Он лежит в src/content/blog и валидируется схемой.

</Callout>

Enter fullscreen mode Exit fullscreen mode

11) Выводим список статей (страница /blog)

src/pages/blog/index.astro


---

import BaseLayout from "../../layouts/BaseLayout.astro";

import { getCollection } from "astro:content";

const posts = (await getCollection("blog"))

.filter(p => !p.data.draft)

.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

---

<BaseLayout title="Блог" description="Список статей на Astro">

<h1>Блог</h1>

<ul>

{posts.map((post) => (

<li>

<a href={/blog/${post.data.slug}/}>{post.data.title}</a>

<div style="opacity:.7; font-size: 14px">{post.data.description}</div>

</li>

))}

</ul>

</BaseLayout>

Enter fullscreen mode Exit fullscreen mode

12) Страница статьи по slug ( /blog/:slug )

src/pages/blog/[slug].astro


---

import BaseLayout from "../../layouts/BaseLayout.astro";

import { getCollection } from "astro:content";

export async function getStaticPaths() {

const posts = await getCollection("blog");

return posts

.filter(p => !p.data.draft)

.map((post) => ({

params: { slug: post.data.slug },

props: { post },

}));

}

const { post } = Astro.props;

// Важно: у коллекционных MDX/MD файлах есть render()

const { Content } = await post.render();

---

<BaseLayout title={post.data.title} description={post.data.description}>

<article>

<h1>{post.data.title}</h1>

<p style="opacity:.7; margin-top:-6px">

{post.data.pubDate.toLocaleDateString("ru-RU")}

</p>

<Content />

</article>

</BaseLayout>

Enter fullscreen mode Exit fullscreen mode

13) Что мы уже сделали (и почему это хороший фундамент)

  • Подняли проект на Astro
  • Разобрали структуру папок и маршруты
  • Подключили MDX и вставили компонент прямо в статью (docs.astro.build)
  • Настроили Content Collections с Zod-схемой (валидация + порядок) (docs.astro.build)
  • Собрали список постов и динамическую страницу поста

Это уже реальный блоговый фундамент, который не развалится при первом “а давайте ещё 100 статей”.

Read more on viku-lov.ru

Top comments (0)