Structured Data (структурированные данные) — это разметка, которая объясняет Google, что именно находится на странице. В ответ Google показывает rich results: звёздочки рейтинга, цены, FAQ прямо в выдаче. Astro упрощает добавление JSON-LD до нескольких строк.
Что такое Rich Results и зачем они нужны
Rich results — расширенные результаты поиска с дополнительными элементами:
- Статья → дата, автор, обложка в Google Discover
- Продукт → цена, наличие, рейтинг прямо под ссылкой
- FAQ → раскрываемые вопросы и ответы в выдаче
- Хлебные крошки → путь к странице вместо URL
- Персона → фото и должность в Knowledge Panel
- Организация → логотип, контакты в выдаче
CTR у rich results на 20-30% выше обычных результатов.
Базовый компонент JsonLd
---
// src/components/JsonLd.astro
interface Props {
schema: Record<string, unknown> | Record<string, unknown>[];
}
const { schema } = Astro.props;
---
<script type="application/ld+json" set:html={JSON.stringify(schema, null, 0)} /> Используйте в любом месте страницы — лучше в <head> или в конце <body>:
---
import JsonLd from '../components/JsonLd.astro';
---
<JsonLd
schema={{ '@context': 'https://schema.org', '@type': 'WebSite', name: 'Мой сайт' }}
/> Схема: Article (статьи блога)
Самая нужная схема для контентных сайтов:
---
// src/pages/articles/[slug].astro
import JsonLd from '../../components/JsonLd.astro';
const { article } = Astro.props;
const articleSchema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: article.data.title,
description: article.data.description,
datePublished: article.data.date.toISOString(),
dateModified: article.data.date.toISOString(),
author: {
'@type': 'Person',
name: article.data.author ?? 'Редакция',
url: new URL('/about', Astro.site).href,
},
publisher: {
'@type': 'Organization',
name: 'Название сайта',
logo: {
'@type': 'ImageObject',
url: new URL('/logo.png', Astro.site).href,
},
},
image: article.data.cover
? new URL(`/og/${article.id.replace('.mdx', '')}.png`, Astro.site).href
: undefined,
url: Astro.url.href,
mainEntityOfPage: {
'@type': 'WebPage',
'@id': Astro.url.href,
},
};
---
<JsonLd schema={articleSchema} /> Схема: BreadcrumbList (хлебные крошки)
---
// src/components/Breadcrumbs.astro
interface Crumb {
label: string;
href: string;
}
interface Props {
crumbs: Crumb[];
}
const { crumbs } = Astro.props;
const schema = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: crumbs.map((crumb, i) => ({
'@type': 'ListItem',
position: i + 1,
name: crumb.label,
item: new URL(crumb.href, Astro.site).href,
})),
};
---
<nav aria-label="Навигация по сайту">
<ol>
{
crumbs.map((crumb, i) => (
<li>
{i < crumbs.length - 1 ? (
<a href={crumb.href}>{crumb.label}</a>
) : (
<span aria-current="page">{crumb.label}</span>
)}
{i < crumbs.length - 1 && <span aria-hidden="true">›</span>}
</li>
))
}
</ol>
</nav>
<script type="application/ld+json" set:html={JSON.stringify(schema)} /> Использование на странице статьи:
<Breadcrumbs
crumbs={[
{ label: 'Главная', href: '/' },
{ label: 'Статьи', href: '/articles/' },
{ label: article.data.title, href: Astro.url.pathname },
]}
/> Схема: FAQPage
FAQ-разметка раскрывает вопросы и ответы прямо в выдаче Google — мощный инструмент для информационных страниц:
---
// src/components/FAQ.astro
interface FAQItem {
question: string;
answer: string;
}
interface Props {
items: FAQItem[];
}
const { items } = Astro.props;
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: items.map((item) => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: {
'@type': 'Answer',
text: item.answer,
},
})),
};
---
<section>
<h2>Частые вопросы</h2>
{
items.map((item) => (
<details>
<summary>{item.question}</summary>
<p>{item.answer}</p>
</details>
))
}
</section>
<script type="application/ld+json" set:html={JSON.stringify(schema)} /> Использование в MDX:
import FAQ from '../../components/FAQ.astro';
<FAQ
items={[
{
question: 'Нужен ли Node.js для Astro?',
answer:
'Для разработки — да. В продакшне Astro генерирует статические HTML-файлы, Node.js не нужен.',
},
{
question: 'Можно ли использовать React в Astro?',
answer:
'Да, React-компоненты работают как острова через @astrojs/react интеграцию.',
},
]}
/> Схема: Product (интернет-магазин)
---
const productSchema = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.data.title,
description: product.data.description,
sku: product.data.sku,
brand: {
'@type': 'Brand',
name: 'Название бренда',
},
image: product.data.cover ? [coverUrl] : undefined,
offers: {
'@type': 'Offer',
priceCurrency: 'RUB',
price: (product.data.price / 100).toFixed(2),
availability: product.data.inStock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
seller: {
'@type': 'Organization',
name: 'Название магазина',
},
url: Astro.url.href,
},
aggregateRating: product.data.rating
? {
'@type': 'AggregateRating',
ratingValue: product.data.rating.value,
reviewCount: product.data.rating.count,
bestRating: 5,
worstRating: 1,
}
: undefined,
};
--- Звёздочки рейтинга в выдаче Google — прямой рост CTR на 15-25%.
Схема: Organization (главная страница)
---
// src/pages/index.astro или src/layouts/BaseLayout.astro
const orgSchema = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Название компании',
url: Astro.site?.href,
logo: {
'@type': 'ImageObject',
url: new URL('/logo.png', Astro.site).href,
width: 200,
height: 60,
},
contactPoint: {
'@type': 'ContactPoint',
telephone: '+7-XXX-XXX-XX-XX',
contactType: 'customer service',
availableLanguage: 'Russian',
},
address: {
'@type': 'PostalAddress',
addressLocality: 'Москва',
addressCountry: 'RU',
},
sameAs: [
'https://vk.com/yourpage',
'https://t.me/yourchannel',
'https://github.com/yourorg',
],
};
--- Схема: WebSite + SearchAction
Добавьте sitelinks searchbox (поисковая строка прямо в выдаче):
---
const websiteSchema = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Название сайта',
url: Astro.site?.href,
potentialAction: {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: `${Astro.site}search?q={search_term_string}`,
},
'query-input': 'required name=search_term_string',
},
};
--- Схема: Person (портфолио / автор)
---
const personSchema = {
'@context': 'https://schema.org',
'@type': 'Person',
name: 'Имя Фамилия',
jobTitle: 'Frontend Developer',
url: Astro.site?.href,
image: new URL('/photo.jpg', Astro.site).href,
email: 'hello@example.com',
sameAs: [
'https://github.com/username',
'https://linkedin.com/in/username',
'https://t.me/username',
],
knowsAbout: ['Astro', 'React', 'TypeScript', 'Web Performance'],
alumniOf: {
'@type': 'CollegeOrUniversity',
name: 'Название университета',
},
};
--- Многосхемный компонент
Для удобства объедините несколько схем в одном блоке:
---
// src/components/ArticleSchemas.astro
// Объединяет Article + BreadcrumbList + Author Person
---
<script
type="application/ld+json"
set:html={JSON.stringify([articleSchema, breadcrumbSchema, authorSchema])}
/> Google поддерживает массив схем в одном теге <script>.
Проверка разметки
1. Rich Results Test (официальный инструмент Google):
https://search.google.com/test/rich-results Показывает, какие rich results доступны для страницы.
2. Schema Markup Validator:
https://validator.schema.org/ Проверяет валидность структуры схемы.
3. В коде: проверить при сборке
// src/lib/validateSchema.ts (для CI)
import Ajv from 'ajv';
export function validateArticleSchema(schema: unknown) {
// Базовая проверка обязательных полей
const s = schema as Record<string, unknown>;
const required = ['@context', '@type', 'headline', 'datePublished', 'author'];
const missing = required.filter((f) => !s[f]);
if (missing.length) {
console.warn('Missing schema fields:', missing);
}
} Распространённые ошибки
| Ошибка | Последствие | Решение |
|---|---|---|
Нет @context | Схема не распознаётся | Всегда добавлять 'https://schema.org' |
| URL не абсолютный | Предупреждение в валидаторе | Использовать new URL(path, Astro.site) |
datePublished без ISO 8601 | Дата не распознаётся | date.toISOString() |
| Схема в комментарии или без type=“application/ld+json” | Игнорируется | Проверить атрибуты тега |
| Дублирование схем одного типа | Конфликт | Одна схема каждого типа на страницу |
Итог
JSON-LD structured data — самый эффективный SEO-инструмент с точки зрения соотношения усилий и результата. В Astro добавление занимает час: создайте компонент JsonLd.astro и используйте нужные схемы на каждом типе страниц. Практический результат — rich snippets в Google, рост CTR на 15-30% и лучшее понимание контента поисковыми ботами. Начните с Article, BreadcrumbList и Organization — это покрывает 80% потребностей контентного сайта.