arrow_back Назад в блог
person Dmitrii Bolotov

22 лучших практик Astro: советы, которые стоит сохранить в закладки

#astro #webdev #javascript #performance #best-practices #static-site-generation
translate
Доступно на:
info Эта статья переведена с помощью ИИ

В 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.

Диаграмма, показывающая конвейер сборки Astro: исходные изображения автоматически конвертируются в WebP, сжимаются и обслуживаются с правильными размерами — всё на этапе сборки без нагрузки во время выполнения

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.

Диаграмма архитектуры, сравнивающая загрузку традиционного одностраничного приложения, где тяжёлый JavaScript-бандл блокирует всю страницу, с архитектурой островов Astro, где отдельные интерактивные компоненты гидратируются независимо и параллельно

🚀 Навигация и воспринимаемая производительность

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. Мы используем этот паттерн для страниц документации — обновления контента отражаются мгновенно без полной пересборки сайта.

Диаграмма рабочего процесса, иллюстрирующая конвейер коллекций контента Astro: MDX-файлы с фронтматерией, валидированной через Zod, проходят через Content Layer API, обеспечивая получение контента как на этапе сборки, так и во время запроса

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, гидратируйте точечно, оптимизируйте на этапе сборки — и у вас будет быстрый сайт почти случайно.

«Большинство проблем с производительностью в веб-разработке решаются на этапе сборки, а не во время выполнения.»


Что ещё почитать

Если эта статья оказалась для вас полезной, возможно, вам также понравится:


Часто задаваемые вопросы

Почему стоит использовать 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

Спасибо за чтение!
Читать другие статьи