DEV Community

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

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

Astro 2025–2026 (часть 2): islands, гидратация, View Transitions и SEO

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 — когда компонент попал в viewport
  • client:media — по media-query
  • client: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 аналогично

Enter fullscreen mode Exit fullscreen mode

Дальше — создаём компонент и подключаем его как остров.

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>

);

}

Enter fullscreen mode Exit fullscreen mode

Теперь подключаем на странице 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>

Enter fullscreen mode Exit fullscreen mode

Вот это и есть “выборочная гидратация”: страница лёгкая, интерактив появляется ровно там и тогда, где он нужен. (docs.astro.build)


4) client:media: интерактив только для нужного устройства

Кейс: на мобилке меню должно быть интерактивным (бургер), а на десктопе — обычная разметка.


---

import MobileMenu from "../components/MobileMenu.jsx";

---

<MobileMenu client:media="(max-width: 768px)" />

Enter fullscreen mode Exit fullscreen mode

В итоге:

  • на десктопе этот 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" />

Enter fullscreen mode Exit fullscreen mode

Важно: это самый дорогой режим, потому что без 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>

Enter fullscreen mode Exit fullscreen mode

На странице:


---

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

---

<Recommendations server:defer />

Enter fullscreen mode Exit fullscreen mode

Смысл: не блокировать основной контент “тяжёлой” серверной логикой. (docs.astro.build)


7) View Transitions: плавные переходы без SPA-цирка

View Transitions — это анимации между страницами. В Astro их надо включить явно: по умолчанию навигация обычная, “полная перезагрузка документа”. (docs.astro.build)

У Astro есть два подхода:

  1. Browser-native cross-document view transitions (MPA) — анимация, но без SPA-роутинга и без лишнего JS.
  2. Astro — клиентская навигация + дополнительные возможности, но уже с JS. (docs.astro.build)

7.1) Вариант А: просто включить View Transitions (MPA-режим)

В layout (обычно в

) добавь компонент переходов.

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>

Enter fullscreen mode Exit fullscreen mode

Это включит переходы (где поддерживается API) и сохранит сайт как MPA. (docs.astro.build)

7.2) Вариант B: (когда нужна клиентская навигация)

Если тебе нужно поведение “почти SPA” (с нюансами), можно использовать . В документации прямо объясняют различия между нативными переходами и Astro-роутером. (docs.astro.build)

Я бы советовал:

  • для блога/доков начать с нативных transitions
  • брать, когда реально упёрся в потребность клиентского роутинга

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()],

});

Enter fullscreen mode Exit fullscreen mode

@astrojs/sitemap прямо требует site (с http/https) для генерации sitemap. (docs.astro.build)

Для RSS в рецепте тоже есть совет: убедись, что site настроен, чтобы генерировать ссылки. (docs.astro.build)


10) Sitemap через @astrojs/sitemap

Самый простой путь:


pnpm astro add sitemap

или npm/yarn

Enter fullscreen mode Exit fullscreen mode

Интеграция генерирует 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

Enter fullscreen mode Exit fullscreen mode

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: "Блог Андрея Викулова",

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}/,

})),

});

}

Enter fullscreen mode Exit fullscreen mode

Рецепт @astrojs/rss официально описывает процесс установки, необходимость site и подход через .xml.js endpoint. (docs.astro.build)

11.3) Автообнаружение RSS (чтобы браузеры/ридеры нашли)

Добавь в

:

<link

rel="alternate"

type="application/rss+xml"

title="RSS"

href="/rss.xml"

/>

Enter fullscreen mode Exit fullscreen mode

В рецепте 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}

Enter fullscreen mode Exit fullscreen mode

Использование в 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>

Enter fullscreen mode Exit fullscreen mode

Теперь на страницах ты просто передаёшь 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)

Read more on viku-lov.ru

Top comments (0)