В QuotyAI я использую Astro для создания лендингов, агентных AI-интерфейсов и постов в блоге, поэтому у меня есть практический опыт того, как использовать его правильно и как вайб-кодить без головной боли.
Astro — лучший фреймворк для контентных сайтов на данный момент: первое место по удовлетворённости разработчиков в опросе State of JS 2025, при поддержке Cloudflare с января 2026 года. Но, как и любой инструмент, он вознаграждает тех, кто использует его по назначению.
«Astro не делает ваш сайт быстрым. Он делает невозможным создание медленного.»
Это справочник, который я хотел бы иметь, когда начинал. Собираете ли вы свой первый проект на Astro или вайб-кодите блог в 2 часа ночи — вот привычки, которые стоит выработать с первого дня.
💡 Инсайт: Мы перенесли наш корпоративный лендинг с Next.js SPA на Astro за два дня. Показатель производительности Lighthouse вырос с 62 до 97 без единого изменения в дизайне — просто за счёт удаления клиентского JavaScript, который был не нужен. Результатом сборки стали 40 небольших HTML-файлов вместо одного JavaScript-бандла на 287 КБ.
Важно о версиях: Эта статья охватывает Astro 6.x (вышел в марте 2026) и Astro 6.4 (вышел в мае 2026). Некоторые API из старых туториалов теперь устарели — они явно отмечены ниже. Всегда проверяйте руководство по обновлению при переходе между мажорными версиями.
🖼️ Ресурсы и Медиа
1. Используйте <Image /> Вместо <img />
Встроенный компонент <Image /> в Astro делает много работы на этапе сборки, которую обычные теги <img> оставляют за кадром: он конвертирует изображения в WebP, генерирует правильные атрибуты width и height для предотвращения смещения макета и сжимает всё без необходимости трогать конфигурационные файлы.
---
import { Image } from 'astro:assets';
import hero from '../assets/hero.png';
---
<!-- ✅ Оптимизировано: конвертировано в WebP, сжато, без смещения макета -->
<Image src={hero} alt="Hero image" />
<!-- ❌ Пропускает всё это -->
<img src="/hero.png" alt="Hero image" />
Для сценариев art direction (разные изображения на разных контрольных точках) используйте <Picture /> вместо него. Полную документацию по API смотрите в Astro Image docs.
2. Используйте встроенный Fonts API в Astro 6
Почти каждый сайт использует кастомные шрифты, но настроить их правильно на удивление сложно — компромиссы производительности, вопросы конфиденциальности, самостоятельный хостинг, генерация запасных шрифтов и подсказки предзагрузки. Astro 6 добавил встроенный Fonts API, который берёт всё это на себя.
Настройте шрифты в astro.config.mjs:
// astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';
export default defineConfig({
fonts: [
{
name: 'Inter',
cssVariable: '--font-inter',
provider: fontProviders.fontsource(), // or fontProviders.google()
},
],
});
Затем добавьте компонент <Font /> в ваш базовый макет:
---
// src/layouts/Layout.astro
import { Font } from 'astro:assets';
---
<head>
<Font cssVariable="--font-inter" preload />
<style is:global>
body { font-family: var(--font-inter); }
</style>
</head>
За кулисами Astro загружает и кэширует шрифт для самостоятельного хостинга, генерирует оптимизированные запасные варианты, добавляет font-display: swap и вставляет правильные подсказки <link rel="preload">. Ноль ручной настройки. Подробности в Astro Fonts API docs.
💡 Инсайт: До появления Fonts API мы тратили два часа на настройку
font-display, подсказок предзагрузки и запасных шрифтов для каждого проекта. Встроенный API удалил 47 строк шаблонного кода из нашего базового макета и улучшил показатель производительности Lighthouse на 8 пунктов — только за счёт правильной работы со шрифтами.
Почему не Google Fonts CDN? Это стоит вам стороннего DNS-запроса, сетевого обхода и передаёт доставку шрифтов Google. Fonts API автоматически размещает всё на вашем собственном CDN.
🎨 Стилизация
3. Используйте Tailwind v4 через Vite-плагин
Старая интеграция @astrojs/tailwind устарела для Tailwind v4. Используйте @tailwindcss/vite вместо неё — он запускает Tailwind внутри конвейера Vite, что обеспечивает более быстрый HMR, меньший размер production CSS и отсутствие отдельного прохода PostCSS.
npm install tailwindcss @tailwindcss/vite
// astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
vite: {
plugins: [tailwindcss()],
},
});
⚠️ Устарело:
@astrojs/tailwind— это старая интеграция для v3. Не используйте её для новых проектов. Подробности в Tailwind v4 installation guide.
4. Конфиг теперь живёт в CSS (Tailwind v4)
В v4 файла tailwind.config.js больше нет. Токены дизайна указываются прямо в CSS с помощью @theme {}:
/* src/styles/global.css */
@import "tailwindcss";
@theme {
--color-brand: oklch(0.75 0.18 175);
--font-family-sans: var(--font-inter); /* подключите переменную из Fonts API */
}
Импортируйте его один раз в базовом макете — Vite сделает всё остальное.
⚡ Архитектура островов и клиентская интерактивность
5. По умолчанию используйте обычные .astro-компоненты, а не React
Это самый важный сдвиг в мышлении при переходе с Next.js: вам не нужен JS-фреймворк для большей части вашего интерфейса.
.astro-компоненты рендерятся на сервере, не поставляют ни капли JavaScript и поддерживают пропсы, слоты и изолированные стили. Они покрывают хедеры, навбары, карточки, футеры и всё, что не требует клиентского состояния. Используйте React, Vue или Svelte только когда вам действительно нужна интерактивность — как интерфейсы чата с AI в реальном времени, которые мы создаём в QuotyAI.
---
// src/components/Card.astro — ноль JS, полный функционал
const { title, description } = Astro.props;
---
<article class="card">
<h2>{title}</h2>
<p>{description}</p>
</article>
«Лучший компонент — тот, который никогда не отправляет JavaScript клиенту.»
Если вы переходите с Next.js: Astro — это не React-фреймворк с прикрученным SSG. Это HTML-фреймворк, который позволяет опционально добавить React для интерактивных компонентов. Это различие критически важно для производительности веба.
6. Выбирайте правильную директиву client:*
Когда вам всё же нужен JavaScript остров, подходите осознанно к тому, когда он гидратируется:
| Директива | Когда гидратируется | Для чего лучше |
|---|---|---|
client:load |
Немедленно при загрузке | Интерактивный UI выше сгиба |
client:idle |
Когда браузер бездействует | Некритичные виджеты |
client:visible |
При прокрутке в область видимости | Компоненты ниже сгиба |
client:only="react" |
Только на клиенте, без SSR | Компоненты, зависящие от браузерных API |
Самая распространённая ошибка — использовать client:load повсюду. Если компонент находится ниже сгиба, client:visible означает, что его JavaScript даже не будет запрошен, пока пользователь не прокрутит до него.
<!-- ❌ Загружается и гидратируется сразу, даже если никогда не виден -->
<HeavyChart client:load />
<!-- ✅ Гидратируется только при прокрутке в область видимости -->
<HeavyChart client:visible />
7. Острова загружаются параллельно — используйте это
В отличие от традиционных SPA, где тяжёлый компонент блокирует страницу, острова Astro гидратируются независимо. Тяжёлый график внизу не заблокирует лёгкую навигацию вверху.
💡 Инсайт: Когда мы переносили наш лендинг с React SPA на Astro, размер JavaScript-бандла сократился с 287 КБ до 14 КБ. Оставшиеся 14 КБ — это все интерактивные острова, и три из них могли бы использовать
client:visibleвместоclient:load. Всегда есть лишний жирок, который можно срезать.
Структурируйте осознанно: высокоприоритетные интерактивные компоненты наверху с client:load, всё остальное ниже с client:visible или client:idle.
🚀 Навигация и воспринимаемая производительность
8. Включите встроенную предзагрузку
Одна строка конфига делает все внутренние ссылки предзагружаемыми при наведении — навигация кажется мгновенной, потому что страница уже в памяти до того, как пользователь нажал.
// astro.config.mjs
export default defineConfig({
prefetch: {
prefetchAll: true,
defaultStrategy: 'hover', // также: 'tap', 'viewport'
},
});
Для отдельных ссылок можно подключиться без prefetchAll:
<a href="/blog/my-post" data-astro-prefetch="viewport">Read more</a>
Все опции конфигурации смотрите в Astro prefetch docs.
⚠️ Устарело:
@astrojs/prefetch(старый пакет-интеграция) устарел в Astro 3.5. Используйте встроенную опцию конфигаprefetchвыше.
9. Добавьте View Transitions для ощущения SPA без цены SPA
Один импорт в вашем базовом макете даёт плавные анимированные переходы между страницами без поставки полноценного клиентского роутера:
---
// src/layouts/Layout.astro
import { ClientRouter } from 'astro:transitions';
---
<head>
<ClientRouter />
</head>
Элементы с одинаковыми атрибутами transition:name морфируются между страницами. Это одна из самых недооценённых возможностей Astro. Подробнее в View Transitions guide.
📝 Коллекции контента и Developer Experience
10. Используйте Content Collections для всего Markdown
Content Collections дают типобезопасную фронтматерию с валидацией схемы. Больше никакого post.data.title, возвращающего undefined во время выполнения.
// src/content/config.ts
import { defineCollection } from 'astro:content';
import { z } from 'astro/zod'; // ← правильный импорт в Astro 6
const blog = defineCollection({
schema: z.object({
title: z.string(),
date: z.coerce.date(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
⚠️ Устарело: В старых туториалах используется
import { z } from 'astro:content'. В Astro 6 Zod 4 поставляется отдельно — импортируйте из'astro/zod'.
Astro 6 также поддерживает живые коллекции через Content Layer API, которые получают данные во время запроса (без пересборки при изменении контента в CMS), используя defineLiveCollection() в src/live.config.ts. Мы используем этот паттерн для страниц документации — обновления контента отражаются мгновенно без полной пересборки сайта.
11. Настройте алиасы путей в TypeScript
Перестаньте писать ../../../components/Card.astro. Настройте алиасы один раз в tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@layouts/*": ["src/layouts/*"],
"@lib/*": ["src/lib/*"]
}
}
}
Каждый импорт становится чистым:
import Card from '@components/Card.astro';
import { formatDate } from '@lib/utils';
12. Используйте MDX, когда ваш контент требует компонентов
Обычный Markdown отлично подходит для текста. MDX отлично подходит для текста плюс интерактивные демо, кастомные выделения и встраиваемые компоненты.
npx astro add mdx
---
title: My Post
---
import CodeSandbox from '@components/CodeSandbox.astro';
Here's a live example:
<CodeSandbox src="https://..." />
And then the article continues in plain Markdown...
13. Используйте компонент <Code /> для динамических блоков кода
Astro поставляет встроенный компонент <Code /> на основе Shiki — того же подсветчика, который используется для Markdown-блоков кода, но доступный как компонент в файлах .astro и .mdx. Это правильный инструмент, когда нужно отрендерить код, который формируется динамически на этапе сборки: из файла, CMS, переменной или пропса.
---
import { Code } from 'astro:components';
const snippet = await Astro.glob('./examples/*.ts');
---
<!-- Подсветка синтаксиса для любой строки кода -->
<Code code={`const foo = 'bar';`} lang="js" />
<!-- Динамический код из файла или CMS -->
<Code code={snippet[0].default} lang="ts" theme="github-dark" />
<!-- Встроенный рендеринг кода -->
<p>Используйте <Code code="npm run dev" lang="bash" inline /> для запуска.</p>
Никаких дополнительных пакетов, никакой конфигурации. Поддерживаются все Shiki themes, все языки и даже Shiki transformers для подсветки различий и фокусировки на строках.
Вы также можете задать глобальную тему для Markdown-блоков кода в astro.config.mjs:
export default defineConfig({
markdown: {
shikiConfig: {
themes: {
light: 'github-light',
dark: 'github-dark',
},
},
},
});
Примечание:
<Code />не наследуетshikiConfigиз настроек Markdown — передавайтеthemeнапрямую как пропс, когда нужен определённый внешний вид.
14. Используйте современный процессор Markdown (Astro 6.4)
Astro 6.4 представил новый подключаемый API markdown.processor и процессор на Rust под названием Sätteri, который значительно быстрее стандартного конвейера unified.
Если вы не используете remark/rehype-плагины, переключитесь на Sätteri:
npm install @astrojs/markdown-satteri
// astro.config.mjs
import { satteri } from '@astrojs/markdown-satteri';
export default defineConfig({
markdown: {
processor: satteri(),
},
});
Если вы используете remark/rehype-плагины, мигрируйте на новый API unified-процессора:
// astro.config.mjs
import { unified } from '@astrojs/markdown-remark';
import remarkToc from 'remark-toc';
export default defineConfig({
markdown: {
processor: unified({
remarkPlugins: [remarkToc],
}),
},
});
⚠️ Устарело: Верхнеуровневые
markdown.remarkPlugins,markdown.rehypePlugins,markdown.gfmиmarkdown.smartypantsустарели в Astro 6.4 и будут удалены в Astro 8. Перенесите их вunified({...}).
15. Используйте Astro.logger для структурированной отладки
console.log работает, но он теряется в стене выводов сборки без контекста. Astro 6.2 представил экспериментальный структурированный логгер, который можно использовать напрямую в страницах и компонентах через Astro.logger.
Включите его в astro.config.mjs:
// astro.config.mjs
import { defineConfig, logHandlers } from 'astro/config';
export default defineConfig({
experimental: {
logger: logHandlers.console(), // или .json({ pretty: true }) для структурированного вывода
},
});
Затем используйте его в любом месте фронтматерии Astro:
---
const posts = await getCollection('blog');
Astro.logger.info(`Рендеринг индекса блога с ${posts.length} постами`);
if (posts.length === 0) {
Astro.logger.warn('Посты не найдены — проверьте директорию с контентом');
}
---
Три уровня: info, warn, error. Ошибки идут в stderr, остальное — в stdout. Для CI-пайплайнов и агрегаторов логов используйте logHandlers.json() для структурированного вывода, который легко парсить:
experimental: {
logger: logHandlers.json({ pretty: true, level: 'warn' }) // только warn+error
}
Вы также можете передать --experimentalJson в astro build в CLI без изменения конфига.
🌐 Стратегия интернационализации
16. Добавьте i18n-роутинг до того, как появится маршруты, о которых вы пожалеете
Добавление интернационализации на существующий сайт задним числом означает перестройку всей директории src/pages/ и обновление каждой внутренней ссылки. Сделайте это в начале, даже если сегодня вы поддерживаете только один язык.
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'vi', 'ja'], // добавьте больше позже
routing: {
prefixDefaultLocale: false, // /blog вместо /en/blog
},
},
});
Используйте getRelativeLocaleUrl() для всех внутренних ссылок, чтобы они оставались локаль-зависимыми:
---
import { getRelativeLocaleUrl } from 'astro:i18n';
const { currentLocale } = Astro;
---
<a href={getRelativeLocaleUrl(currentLocale, '/blog')}>Blog</a>
Организуйте контент по локалям в ваших коллекциях:
src/content/blog/
en/post-1.md
vi/post-1.md
💡 Инсайт: Мы наблюдали, как команда потратила три недели на добавление i18n на существующий сайт Astro. Им нужно было обновить 47 внутренних ссылок, перестроить 12 коллекций контента и настроить 4 правила редиректов в Cloudflare. Настройка i18n в начале заняла бы 15 минут. Цена «потом» составила 160 человеко-часов.
Даже если вы запускаетесь на одном языке, структура папок и конфиг i18n ничего не стоят сейчас и избавляют от болезненной миграции позже. Посмотрите, как QuotyAI структурирует контент на английском, русском и вьетнамском — откройте документацию на предпочитаемом языке.
🔍 SEO и обнаруживаемость
17. Добавьте @astrojs/sitemap
Одна интеграция — автоматическая генерация sitemap из всех ваших маршрутов:
npx astro add sitemap
// astro.config.mjs
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://yourdomain.com', // обязательно
integrations: [sitemap()],
});
Astro генерирует /sitemap-index.xml на этапе сборки. Отправьте его в Google Search Console — и готово.
18. Всегда указывайте site: в конфиге
Это одно поле открывает Astro.site во всём проекте, заставляет канонические URL работать корректно и обязательно для интеграции sitemap.
export default defineConfig({
site: 'https://yourdomain.com',
});
19. Определитесь со стратегией слешей
Google всё равно, используете вы /blog/ или /blog, но ему не всё равно, если вы смешиваете оба варианта. Выберите один:
export default defineConfig({
trailingSlash: 'always', // или 'never'
});
Непоследовательность создаёт проблемы с дублированием контента, которые незаметно вредят вашему SEO.
20. Добавьте RSS-ленту
Два файла — и ваш контент становится доступным для подписки. Полезно для читателей, агрегаторов и функции импорта лент Dev.to.
npm install @astrojs/rss
// 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');
return rss({
title: 'My Blog',
description: 'My thoughts on dev stuff',
site: context.site,
items: posts.map(post => ({
title: post.data.title,
pubDate: post.data.date,
link: `/blog/${post.slug}/`,
})),
});
}
🌍 Конвейер развёртывания
21. Разворачивайте на Edge CDN-платформах
Вывод Astro по умолчанию — статический HTML. Это значит, что ему место на CDN с глобальной доставкой с периферии, а не на традиционном сервере. Cloudflare Pages, Netlify и Vercel поддерживают Astro с нулевой конфигурацией.
# Cloudflare Pages (полноценная поддержка с момента приобретения Astro компанией Cloudflare)
npx astro add cloudflare
Именно здесь окупается вся работа на этапе сборки. Ваш «сервер» — это просто файлы на CDN, обслуживаемые из ближайшего к посетителю дата-центра. Хотите увидеть, как это работает? Попробуйте QuotyAI бесплатно — всё приложение работает именно на этой архитектуре.
22. Явно указывайте output: 'static'
Это значение по умолчанию, но его явное указание передаёт намерение:
export default defineConfig({
output: 'static', // предварительно рендерить всё на этапе сборки
});
Если коллега случайно добавит серверный маршрут, сразу станет очевидно, что это не вписывается в архитектуру.
TL;DR
| Категория | Привычка |
|---|---|
| Изображения | Используйте <Image /> |
| Шрифты | Встроенный Fonts API (Astro 6) — сам хостинг, запасные шрифты и предзагрузка |
| Стилизация | Tailwind v4 через @tailwindcss/vite, @theme {} в CSS |
| Интерактивность | По умолчанию .astro, не React; выбирайте client:* по приоритету компонента |
| Производительность | Включите prefetch, добавьте View Transitions |
| Контент | Content Collections + import { z } from 'astro/zod' + MDX + <Code /> + Sätteri |
| Логирование | Astro.logger для структурированной отладки (Astro 6.2+) |
| i18n | Настройте в первый день, а не на сотый |
| SEO | Sitemap, site:, единообразие слешей, RSS |
| Развёртывание | Edge CDN, явный output: 'static' |
Astro вознаграждает разработчиков, которые доверяют его умолчаниям. Поставляйте статический HTML, гидратируйте точечно, оптимизируйте на этапе сборки — и у вас будет быстрый сайт почти случайно.
«Большинство проблем с производительностью в веб-разработке решаются на этапе сборки, а не во время выполнения.»
Что ещё почитать
Если эта статья оказалась для вас полезной, возможно, вам также понравится:
- Why I’m Using Astro for Landings and Blog — разбор изначального решения
- Vibe Coding vs Open Source — как мы создаём сайты на Astro с помощью AI
- How I Run a Production AI Startup for $30/Month — инфраструктура развёртывания QuotyAI
Часто задаваемые вопросы
Почему стоит использовать Astro вместо Next.js для контентных сайтов? Astro — это HTML-фреймворк, который по умолчанию не поставляет JavaScript, гидратируя только интерактивные острова. Next.js — это в первую очередь React-фреймворк, поэтому вы платите «налог React» даже на статическом контенте. Именно из-за этого фундаментального различия Astro занял первое место по удовлетворённости разработчиков в опросе State of JS 2025.
Как настроить Tailwind v4 в Astro?
Установите tailwindcss и @tailwindcss/vite, добавьте плагин Vite в astro.config.mjs и разместите токены дизайна в CSS с помощью @theme {}. Старая интеграция @astrojs/tailwind устарела для v4 — не используйте её в новых проектах.
В чём разница между client:load и client:visible?
client:load гидратирует компонент сразу при загрузке страницы, тогда как client:visible ждёт, пока компонент не попадёт в область видимости. Используйте client:visible для компонентов ниже сгиба, чтобы избежать загрузки и выполнения JavaScript, который пользователь, возможно, никогда не увидит.
Как добавить кастомные шрифты в Astro 6?
Настройте шрифты в astro.config.mjs с помощью встроенного Fonts API, используя fontProviders.fontsource() или fontProviders.google(), затем добавьте компонент <Font cssVariable="--font-inter" preload /> в базовый макет. Astro самостоятельно обрабатывает хостинг, запасные шрифты и подсказки предзагрузки.
Что такое Content Layer API в Astro 6?
Content Layer API расширяет Content Collections поддержкой живых коллекций, которые получают данные во время запроса — никакой пересборки при изменении контента в CMS не требуется. Определяйте их с помощью defineLiveCollection() в src/live.config.ts.
Tags: astro webdev javascript performance