Astro 2025–2026 (часть 2): islands, гидратация, View Transitions и SEO
Практический гайд по Astro: islands architecture, выборочная гидратация, View Transitions, SEO (canonical, OpenGraph, sitemap, RSS). Быстрый сайт без лишнего JS.
Требования
- Node.js LTS (18+)
- Базовые знания HTML/CSS/JS
- Опыт работы с Astro (рекомендуется прочитать часть 1)
Astro в 2025–2026 (часть 2): islands, гидратация, View Transitions и SEO
В первой части мы сделали фундамент: страницы, layout, MDX и Content Collections. Во второй — самое “астровское”: почему Astro быстрый и как не превратить сайт в SPA по привычке.
План:
- Islands architecture: что это и зачем
client:*директивы: когда и как грузить интерактивserver:defer: когда “динамику” лучше оставить на сервере- View Transitions: как сделать плавную навигацию без SPA
- SEO-минимум для блога: canonical + OG + sitemap + RSS
1) Islands architecture: главная идея Astro
Islands architecture — это подход, где основная часть страницы рендерится как быстрый статический HTML, а JavaScript добавляется маленькими “островами” только туда, где нужна интерактивность (например, карусель, поиск, комментарии). Это снижает “монолитный” JS-пакет, который обычно тормозит загрузку у SPA.
Что важно понять джуну (и полезно помнить сеньору)
- В Astro по умолчанию UI-компоненты рендерятся в HTML и CSS и не отправляют клиентский JS.
- Интерактивность включается явно, через
client:*директиву. - Ты сам задаёшь “приоритет”: что грузить сразу, что позже, что только если пользователь докрутил.
Это и есть “скорость по умолчанию”: пока ты не сказал «давай JS», Astro не тащит его в браузер просто потому что “так принято”.
2) Клиентские директивы client:*: как управлять гидратацией
Astro даёт набор template directives (спец-атрибутов), которые видит компилятор и меняет поведение компонента.
Нас интересуют client directives:
client:load— гидратация сразу при загрузкеclient:idle— когда браузер “подустал и освободился”client:visible— когда компонент попал в viewportclient:media— по media-queryclient:only— рендер только на клиенте (SSR/SSG не отдаст HTML-разметку компонента)
2.1) Правило большого пальца
- Интерактив выше фолда и критичен (меню, переключатель темы, поиск) →
client:load - Не критичен (виджеты, “похожие статьи”, графики) →
client:idle - Ниже фолда (комменты, тяжелый блок) →
client:visible - Нужно только на мобилке/десктопе →
client:media - Компонент вообще не должен SSR/SSG (например, жёсткая зависимость от
window) →client:only
3) Практика: подключаем интерактивный компонент как остров
3.1) Ставим UI-фреймворк (пример: React)
Если ты уже используешь React-компоненты, подключай интеграцию (официальный путь):
pnpm astro add react
# или npm/yarn аналогично
Дальше — создаём компонент и подключаем его как остров.
3.2) Пример: счётчик на React (минимально интерактивный)
src/components/Counter.jsx:
import { useState } from "react";
export default function Counter({ initial = 0 }) {
const [count, setCount] = useState(initial);
return (
<div style={{ display: "flex", gap: 12, alignItems: "center" }}>
<button onClick={() => setCount((c) => c - 1)}>-</button>
<strong>{count}</strong>
<button onClick={() => setCount((c) => c + 1)}>+</button>
</div>
);
}
Теперь подключаем на странице Astro.
src/pages/playground.astro:
---
import BaseLayout from "../layouts/BaseLayout.astro";
import Counter from "../components/Counter.jsx";
---
<BaseLayout title="Playground" description="Islands demo">
<h1>Islands demo</h1>
<!-- По умолчанию Counter отрендерится в HTML, но НЕ будет интерактивным -->
<!-- Делаем его островом: -->
<Counter client:visible initial={10} />
<p style="opacity:.7">
Пока пользователь не докрутил — JS не грузится.
</p>
</BaseLayout>
Вот это и есть “выборочная гидратация”: страница лёгкая, интерактив появляется ровно там и тогда, где он нужен. (docs.astro.build)
4) client:media: интерактив только для нужного устройства
Кейс: на мобилке меню должно быть интерактивным (бургер), а на десктопе — обычная разметка.
---
import MobileMenu from "../components/MobileMenu.jsx";
---
<MobileMenu client:media="(max-width: 768px)" />
В итоге:
- на десктопе этот JS вообще не нужен → и не будет грузиться (в рамках логики директив)
- на мобилке — загрузится, когда media-query совпадёт (docs.astro.build)
5) client:only: когда SSR/SSG противопоказан
client:only — это “я не хочу SSR/SSG HTML для этого компонента, пусть живёт только в браузере”.
Пример: компонент, который на старте обращается к window (и ты не хочешь/не можешь сделать graceful fallback).
---
import HeavyWidget from "../components/HeavyWidget.jsx";
---
<HeavyWidget client:only="react" />
Важно: это самый дорогой режим, потому что без SSR HTML будет более пустым, а контент появится позже. Используй только когда реально надо. (docs.astro.build)
6) Server islands: server:defer (когда динамика — на сервере, но не мешает странице)
Astro поддерживает “server islands”: можно вынести дорогую серверную часть так, чтобы основной HTML пришёл быстро, а серверная вставка догрузилась параллельно. Это делается директивой server:defer. (docs.astro.build)
Пример (условный): блок “Персональные рекомендации”.
src/components/Recommendations.astro:
---
const res = await fetch("https://example.com/api/reco"); // пример
const items = await res.json();
---
<section>
<h2>Рекомендации</h2>
<ul>
{items.map((x) => <li>{x.title}</li>)}
</ul>
</section>
На странице:
---
import Recommendations from "../components/Recommendations.astro";
---
<Recommendations server:defer />
Смысл: не блокировать основной контент “тяжёлой” серверной логикой. (docs.astro.build)
7) View Transitions: плавные переходы без SPA-цирка
View Transitions — это анимации между страницами. В Astro их надо включить явно: по умолчанию навигация обычная, “полная перезагрузка документа”. (docs.astro.build)
У Astro есть два подхода:
- Browser-native cross-document view transitions (MPA) — анимация, но без SPA-роутинга и без лишнего JS.
- Astro
<ClientRouter />— клиентская навигация + дополнительные возможности, но уже с JS. (docs.astro.build)
7.1) Вариант А: просто включить View Transitions (MPA-режим)
В layout (обычно в <head>) добавь компонент переходов.
src/layouts/BaseLayout.astro:
---
import { ViewTransitions } from "astro:transitions";
const { title = "Site", description = "" } = 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} />
<ViewTransitions />
</head>
<body>
<slot />
</body>
</html>
Это включит переходы (где поддерживается API) и сохранит сайт как MPA. (docs.astro.build)
7.2) Вариант B: <ClientRouter /> (когда нужна клиентская навигация)
Если тебе нужно поведение “почти SPA” (с нюансами), можно использовать <ClientRouter />. В документации прямо объясняют различия между нативными переходами и Astro-роутером. (docs.astro.build)
Я бы советовал:
- для блога/доков начать с нативных transitions
<ClientRouter />брать, когда реально упёрся в потребность клиентского роутинга
8) SEO-минимум для Astro-блога (без шаманства)
Сейчас соберём “обязательный набор”, который реально работает:
siteв конфиге- canonical + OpenGraph
- sitemap
- RSS + автообнаружение
9) site в astro.config.mjs: основа для sitemap и RSS
И sitemap, и RSS часто требуют знать боевой домен.
astro.config.mjs:
import { defineConfig } from "astro/config";
import sitemap from "@astrojs/sitemap";
export default defineConfig({
site: "https://example.com",
integrations: [sitemap()],
});
@astrojs/sitemap прямо требует site (с http/https) для генерации sitemap. (docs.astro.build)
Для RSS в рецепте тоже есть совет: убедись, что site настроен, чтобы генерировать ссылки. (docs.astro.build)
10) Sitemap через @astrojs/sitemap
Самый простой путь:
pnpm astro add sitemap
# или npm/yarn
Интеграция генерирует sitemap на build и умеет учитывать статические маршруты и динамические, построенные через getStaticPaths(). (docs.astro.build)
Важное ограничение: в SSR-режиме интеграция не сможет сама сгенерировать записи для динамических роутов. (docs.astro.build)
11) RSS через @astrojs/rss: делаем /rss.xml
Astro предлагает рецепт через API endpoint (файл в src/pages/ с расширением .xml.js). (docs.astro.build)
11.1) Установка
pnpm add @astrojs/rss
# или npm/yarn
11.2) Генерация RSS из Content Collections
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"))
.filter((p) => !p.data.draft)
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
return rss({
title: "VProger Blog",
description: "Заметки о разработке, DevOps и Astro",
site: context.site, // берётся из astro.config.mjs (site)
items: posts.map((post) => ({
title: post.data.title,
description: post.data.description,
pubDate: post.data.pubDate,
link: `/blog/${post.data.slug}/`,
})),
});
}
Рецепт @astrojs/rss официально описывает процесс установки, необходимость site и подход через .xml.js endpoint. (docs.astro.build)
11.3) Автообнаружение RSS (чтобы браузеры/ридеры нашли)
Добавь в <head>:
<link
rel="alternate"
type="application/rss+xml"
title="RSS"
href="/rss.xml"
/>
В рецепте RSS есть отдельный пункт про auto-discovery. (docs.astro.build)
12) Canonical + OpenGraph: делаем один компонент и забываем
Создай src/components/SeoHead.astro:
---
const {
title,
description,
canonical,
ogImage,
} = Astro.props;
const url = canonical ?? Astro.url?.href;
---
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:url" content={url} />
{ogImage ? <meta property="og:image" content={ogImage} /> : null}
Использование в layout:
---
import SeoHead from "../components/SeoHead.astro";
const { title = "Site", description = "", ogImage } = Astro.props;
---
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<SeoHead
title={title}
description={description}
ogImage={ogImage}
/>
</head>
Теперь на страницах ты просто передаёшь title/description/ogImage, и SEO не размазывается по проекту.
13) Итог части 2
Ты теперь понимаешь и умеешь:
- почему Astro быстрый: острова + выборочная гидратация (docs.astro.build)
- как управлять интерактивом через
client:*директивы (docs.astro.build) - как включить View Transitions без превращения сайта в SPA (docs.astro.build)
- как собрать SEO-базу:
site, sitemap, RSS (docs.astro.build)



Комментарии