Перейти к основному содержанию
Сайты и SEO3 мин чтения

SEO для сайтов на React: что нужно сделать, чтобы вас нашли

Сайт на React может быть невидим для поиска, даже если выглядит отлично. Разбираем причины и полный набор технических мер, которые это исправляют.

Лупа над строкой поиска и списком результатов выдачи

Регулярная ситуация: заказчик получил современный сайт на React, а через полгода обнаружил, что в поиске его нет. Причина техническая и вполне решаемая, но исправлять её после запуска дороже, чем учесть на старте.

Почему одностраничное приложение плохо индексируется

В классическом одностраничном приложении сервер отдаёт почти пустой документ, а содержимое собирает JavaScript в браузере. Поисковый робот умеет выполнять скрипты, но делает это с задержкой и не всегда полностью. В результате страница попадает в индекс без текста либо не попадает вовсе.

Что видит робот в одностраничном приложении
<!-- Ответ сервера: содержимого нет -->
<div id="root"></div>
<script src="/assets/app.js"></script>

<!-- Ответ при серверном рендеринге: текст есть сразу -->
<main>
  <h1>Разработка складского учёта</h1>
  <p>Автоматизируем приёмку, отгрузку и инвентаризацию…</p>
</main>

Серверный рендеринг как основа

Решение — отдавать готовый HTML с сервера. В Next.js это поведение по умолчанию: компоненты выполняются на сервере, и в браузер уходит документ с текстом. Важно не потерять это свойство, безосновательно помечая страницы как клиентские.

  • Директиву клиентского компонента ставьте только там, где нужны состояние и обработчики событий.
  • Выносите интерактивные части в отдельные небольшие компоненты, а текстовое содержимое оставляйте серверным.
  • Не загружайте основной текст страницы запросом из браузера: он должен быть в ответе сервера.

Метаданные для каждой страницы

Один общий заголовок на весь сайт — распространённая ошибка. Каждая страница должна иметь собственный заголовок и описание, отражающие её содержимое.

Метаданные динамической страницы
import type { Metadata } from "next"

export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>
}): Promise<Metadata> {
  const { slug } = await params
  const service = getService(slug)

  if (!service) return { title: "Страница не найдена" }

  return {
    title: service.metaTitle ?? service.title,
    // 140–165 символов: столько показывает поиск в сниппете
    description: service.description,
    alternates: { canonical: `/uslugi/${service.slug}` },
    openGraph: {
      title: service.title,
      description: service.description,
      type: "article",
      images: [{ url: service.cover, width: 1200, height: 630 }],
    },
  }
}

Карта сайта и файл robots

Карту сайта нужно генерировать из тех же данных, что и страницы. Написанная руками, она устаревает после первого добавления раздела и начинает вводить робота в заблуждение.

Карта сайта, собранная из данных проекта
import type { MetadataRoute } from "next"
import { services } from "@/lib/services"
import { posts } from "@/lib/blog"

export default function sitemap(): MetadataRoute.Sitemap {
  const base = "https://example.ru"

  return [
    { url: base, changeFrequency: "weekly", priority: 1 },
    ...services.map((service) => ({
      url: `${base}/uslugi/${service.slug}`,
      changeFrequency: "monthly" as const,
      priority: 0.8,
    })),
    ...posts.map((post) => ({
      url: `${base}/blog/${post.slug}`,
      // Дата изменения помогает роботу переобойти обновлённые страницы
      lastModified: post.updated ?? post.date,
      priority: 0.6,
    })),
  ]
}

Микроразметка

Структурированные данные не поднимают позиции напрямую, но дают расширенный сниппет: хлебные крошки в выдаче, блок вопросов и ответов, данные организации. На практике это заметно повышает долю переходов.

Разметка вопросов и ответов
const faqSchema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  mainEntity: faq.map((item) => ({
    "@type": "Question",
    name: item.q,
    acceptedAnswer: { "@type": "Answer", text: item.a },
  })),
}

// Разметка обязана соответствовать видимому тексту:
// расхождение считается нарушением
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
/>

Технический чек-лист

  1. Текст страницы присутствует в исходном коде ответа сервера.
  2. У каждой страницы уникальные заголовок и описание.
  3. Настроен канонический адрес, дубли страниц с параметрами не индексируются.
  4. Карта сайта генерируется автоматически и содержит даты изменения.
  5. Заголовки образуют иерархию: один H1 на страницу, дальше H2 и H3 по смыслу.
  6. Внутренние ссылки ведут на связанные страницы обычными ссылками, а не обработчиками нажатия.
  7. Настроена микроразметка организации, хлебных крошек и статей.
  8. Показатели Core Web Vitals в зелёной зоне на мобильных устройствах.

Техническое SEO не приносит трафик само по себе. Оно убирает препятствия, из-за которых хороший контент не доходит до выдачи.

Частые вопросы

Почему сайт на React не видно в поиске?
В классическом одностраничном приложении сервер отдаёт пустой документ, а содержимое собирает JavaScript в браузере. Робот выполняет скрипты с задержкой и не всегда полностью, поэтому страница попадает в индекс без текста. Проверить просто: откройте исходный код страницы — если текста в нём нет, робот его тоже не увидит.
Решает ли Next.js проблему индексации автоматически?
В значительной степени да: серверный рендеринг здесь поведение по умолчанию. Но свойство легко потерять, помечая страницы клиентскими без необходимости или загружая основной текст запросом из браузера. Интерактивные части следует выносить в отдельные небольшие компоненты, оставляя текст серверным.
Нужна ли микроразметка и что она даёт?
Структурированные данные не поднимают позиции напрямую, но обеспечивают расширенный сниппет: хлебные крошки в выдаче, блок вопросов и ответов, данные организации. Это заметно повышает долю переходов. Важно, чтобы разметка соответствовала видимому на странице тексту.
Как правильно сделать карту сайта?
Генерировать её из тех же данных, что и страницы, включая даты последнего изменения — так робот быстрее переобходит обновлённые разделы. Карта, написанная вручную, устаревает после первого добавления раздела и начинает вводить робота в заблуждение.
SEONext.jsReactиндексация

Похожая задача в вашем проекте?

Разберём вашу ситуацию и пришлём оценку по этапам — без обязательств и общих слов.

Обсудить задачу

Вопрос по статье
или по своему проекту

Отвечаем на заявки в течение одного рабочего дня. Если задача не наша — скажем прямо и подскажем, к кому обратиться.

Написать нам

Поля со звёздочкой обязательны для заполнения.