Как организовать контент и маршруты блога на React

    Блог на React должен разделять содержание статьи, адрес страницы, правила маршрутизации и способ формирования HTML. Для каждой публикации нужен устойчивый slug.

    Виктор С.·5 сентября 2026 г.·12 мин чтенияРазработка
    Виктор Сорокин — технический директор и основатель Like SitesАвтор статьиВиктор С.Технический директор · Основатель Like Sites

    Как организовать контент и маршруты блога на React

    Блог на React должен разделять содержание статьи, адрес страницы, правила маршрутизации и способ формирования HTML. Для каждой публикации нужен устойчивый slug, уникальный URL, источник данных, заголовок, описание, основной текст и метаданные. Маршрут должен быть доступен при прямом открытии, а содержание публичной статьи — присутствовать в исходном HTML или в заранее созданном HTML, если сайт использует статическую генерацию или пререндеринг. Иначе приложение может работать после перехода из списка, но терять страницу при перезагрузке, создавать дубли URL или отдавать поисковому роботу пустой документ.

    Что нужно хранить для статьи

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

    • заголовок;
    • slug;
    • краткое описание;
    • дата публикации;
    • дата обновления, если материал изменялся;
    • автор или редактор;
    • основной текст;
    • изображение обложки и альтернативное описание;
    • статус публикации;
    • категории или другие таксономические признаки.

    Заголовок используется в интерфейсе и метаданных, а slug — в URL. Эти значения не следует смешивать. Изменение заголовка не всегда должно менять публичный адрес, потому что смена URL создаёт необходимость в перенаправлении и обновлении внутренних ссылок.

    Вместо хранения готового HTML в каждом материале можно хранить Markdown, структурированный объект данных или запись во внешней системе. Важно, чтобы приложение одинаково получало данные при построении списка, отдельной страницы, sitemap и пререндеринга.

    Источник данных

    Для небольшого блога публикации можно хранить в Markdown-файлах внутри репозитория. Такой вариант связывает текст статьи с кодом и позволяет публиковать изменения через Git. Метаданные хранятся во frontmatter или в отдельной структуре, а тело статьи преобразуется в HTML или React-дерево во время сборки.

    Другой вариант — внешняя CMS или API. В этом случае приложение получает список статей и данные отдельной публикации во время сборки, на сервере или в браузере. Для публичных SEO-страниц нужно учитывать момент получения данных: если основной текст появляется только после клиентского запроса, исходный HTML может не содержать содержание статьи.

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

    Для каждой записи следует проверять:

    • есть ли обязательный заголовок;
    • есть ли непустой slug;
    • не повторяется ли slug;
    • корректна ли дата;
    • не является ли запись черновиком;
    • содержит ли текст минимально необходимое содержание;
    • не конфликтует ли URL с другим маршрутом.

    Slug и URL

    Slug — часть адреса, которая однозначно связывает URL с публикацией. Для статьи можно использовать путь вида /blog/kak-organizovat-kontent-i-marshruty-react. Конкретный формат зависит от политики сайта, но он должен быть постоянным, читаемым и одинаково формироваться при генерации ссылок, маршрутов, canonical, sitemap и HTML.

    Не следует создавать slug в нескольких независимых функциях. Если список статей использует одну транслитерацию, а пререндеринг другую, часть ссылок будет вести на адреса без соответствующего HTML. Общая функция или поле, являющееся источником истины, уменьшает риск такого рассинхрона.

    Возможны два подхода:

    1. Slug явно хранится в метаданных статьи и используется без автоматического пересчёта.
    2. Slug вычисляется из заголовка по одной детерминированной функции на этапе подготовки данных.

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

    Нужно заранее определить правила завершающего слеша, протокола и домена. Внутренние ссылки, sitemap и canonical должны использовать одну нормализованную версию URL. Google рекомендует логичную структуру адресов и согласованные сигналы канонической страницы. [web:59]

    Маршрутизация блога

    Маршрут связывает URL с компонентом и параметрами публикации. Для блога обычно нужен маршрут списка, маршрут отдельной статьи и обработчик отсутствующего материала. В React Router маршрут может содержать динамический параметр, например :slug, а компонент получает его значение и использует его для поиска записи.

    Логика отдельной страницы должна различать три состояния:

    • публикация найдена и доступна;
    • публикация существует, но ещё не опубликована;
    • публикация с таким slug отсутствует.

    Черновик не должен случайно отображаться по публичному URL. Отсутствующая статья не должна превращаться в страницу с заголовком другой публикации. Если сервер отдаёт HTML для неизвестного URL, приложение должно сформировать корректный экран ошибки и, по возможности, соответствующий HTTP-статус.

    React Router описывает маршруты как сочетание URL-шаблона и модуля, который определяет поведение маршрута. В маршрутах с данными загрузчик может получить данные до отображения компонента. [web:64][web:67]

    Список блога также должен вести на отдельные статьи обычными ссылками с href. Кнопка, которая только вызывает изменение состояния или программную навигацию, не заменяет ссылку для публичного URL. Google указывает, что для обнаружения ссылок надёжнее использовать элемент <a> с атрибутом href. [web:62]

    Список публикаций

    Страница блога получает набор опубликованных записей, сортирует их по заранее определённому правилу и выводит ссылки на отдельные URL. Самое простое правило — сортировка по дате публикации от новых материалов к старым. Если используется дата обновления, это правило должно быть явно определено: дата обновления не всегда означает, что статья должна переместиться в начало архива.

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

    Список необходимо проверять на нескольких уровнях:

    • все отображаемые записи опубликованы;
    • у каждой записи есть уникальный URL;
    • ссылка открывается напрямую;
    • title карточки совпадает с заголовком страницы;
    • изображение имеет подходящий alt;
    • порядок публикаций стабилен;
    • записи не дублируются при повторной загрузке данных.

    Если список строится из API, нужно определить поведение при ошибке запроса. Пустой результат и ошибка получения данных — разные состояния. Нельзя показывать посетителю, что публикаций нет, если запрос завершился ошибкой.

    Страница статьи

    Страница статьи должна получать запись по slug и отображать её содержание в стабильной структуре. Минимальный набор включает заголовок, дату, автора или редактора, основной текст, навигацию и ссылки на связанные материалы, если они действительно существуют.

    Отдельно формируются метаданные документа:

    • title с названием статьи и, при необходимости, названием сайта;
    • description на основе содержания, а не случайного фрагмента;
    • canonical с нормализованным URL статьи;
    • Open Graph-данные, если сайт использует предпросмотр в социальных сетях;
    • структурированные данные только для информации, видимой на странице.

    После клиентского перехода метаданные должны соответствовать новой статье. При статическом или серверном формировании они должны попасть в HTML конкретного маршрута до выполнения клиентского кода. Google описывает обработку JavaScript-страниц как последовательность обхода, рендеринга и индексации; поисковая система использует HTML и результат рендеринга, поэтому критически важное содержание не стоит без необходимости откладывать только до браузерного JavaScript. [web:61]

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

    Markdown и преобразование текста

    Markdown удобен для хранения статей, потому что текст можно редактировать в репозитории и просматривать как обычный файл. Перед отображением его нужно преобразовать в HTML или React-элементы с предсказуемыми правилами для заголовков, списков, ссылок, цитат, кода и изображений.

    При преобразовании нужно контролировать:

    • допустимые HTML-элементы;
    • обработку ссылок;
    • экранирование пользовательского текста;
    • заголовки и их иерархию;
    • идентификаторы заголовков для якорей;
    • изображения и alt;
    • блоки кода;
    • внешние и внутренние URL.

    Заголовки Markdown не должны автоматически создавать несколько конкурирующих H1. Для статьи обычно задаётся один основной H1, а внутренние разделы получают H2 и H3 по смысловой структуре.

    Нельзя считать преобразование успешным только потому, что компонент отобразился. Нужно проверить итоговый HTML: есть ли текст, ссылки, заголовки, изображения и метаданные. При пререндеринге дополнительно проверяется HTML-файл каждого маршрута.

    Пререндеринг статей

    Если блог использует статическую генерацию или пререндеринг, список маршрутов должен строиться из тех же опубликованных записей, которые используются для страницы списка. Для каждой записи сборочный процесс создаёт HTML по её URL.

    Цепочка может выглядеть так:

    1. приложение считывает исходные записи;
    2. отбрасывает черновики;
    3. проверяет обязательные поля и уникальность slug;
    4. строит список публичных URL;
    5. запускает сборку или пререндеринг;
    6. формирует HTML для каждой статьи;
    7. проверяет title, H1, текст, ссылки и canonical;
    8. публикует результат.

    При изменении статьи пререндеринг должен использовать обновлённую запись. Если список маршрутов кешируется, нужно убедиться, что новая публикация не теряется из-за старого кеша. При удалении или снятии публикации необходимо определить судьбу прежнего URL: 404, 410 или перенаправление на действительно подходящую замену.

    Google рекомендует проверять JavaScript-страницы с помощью инструментов, которые позволяют увидеть загруженные ресурсы, ошибки, отрендеренный DOM и исключения. [web:65]

    Пагинация

    Пагинация нужна, когда список публикаций разделяется на несколько страниц. Для неё следует определить формат URL: например, /blog/page/2 или /blog?page=2. Выбранные адреса должны быть стабильными, а переходы между страницами — доступными через обычные ссылки.

    Каждая страница архива должна иметь собственный URL, если она нужна пользователю и должна быть доступна для обхода. Нельзя полагаться только на кнопку «Показать ещё», если без выполнения JavaScript невозможно получить следующие материалы.

    Пагинация должна учитывать:

    • количество публикаций на странице;
    • сортировку;
    • ссылки на предыдущую и следующую страницу;
    • ссылку на первую страницу;
    • отсутствие дублирования записей;
    • корректное поведение при пустой или слишком большой странице.

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

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

    Обновления и даты

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

    Дата в интерфейсе и дата в метаданных должны быть согласованы. Если статья содержит дату обновления, её нужно показывать в видимом содержании или использовать только в соответствии с редакционной политикой сайта. Структурированные данные не должны сообщать даты, которых нет на странице.

    При обновлении текста нужно проверить:

    • не изменился ли публичный slug;
    • не сломались ли якорные ссылки;
    • сохранился ли canonical;
    • обновился ли HTML после сборки;
    • изменились ли lastmod и sitemap, если это предусмотрено системой;
    • не появился ли старый текст из кеша.

    Внутренняя перелинковка

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

    Перелинковка строится на обычных URL и не должна зависеть от клика по произвольной кнопке. Для каждой ссылки проверяется, что адрес существует, не ведёт на черновик и соответствует анкору.

    Ссылки помогают пользователю перемещаться по материалам и дают поисковому роботу пути к связанным URL. При этом сама ссылка не заменяет качественное содержание страницы и не исправляет неправильный статус или пустой HTML.

    Когда схема не подходит

    Хранение всех статей в Markdown внутри репозитория неудобно, если редакторы не работают с Git, публикации меняются очень часто или контент должен редактироваться несколькими пользователями с ролями и согласованиями. В таком случае CMS может упростить редакционный процесс.

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

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

    Практический чек-лист

    • У каждой статьи есть обязательные метаданные.
    • Slug уникален и не меняется случайно после публикации.
    • Список, отдельная страница, sitemap и пререндеринг используют один источник данных.
    • Черновики не попадают в публичные маршруты.
    • Публичные ссылки используют href.
    • Прямая загрузка URL статьи работает после перезагрузки.
    • Неизвестный slug не открывает текст другой статьи.
    • Страница статьи имеет собственные title, description, H1 и canonical.
    • Основной текст доступен в исходном или заранее созданном HTML.
    • Markdown преобразуется с предсказуемой структурой заголовков и ссылок.
    • У изображений есть корректный alt.
    • Пагинация использует доступные и постоянные URL, если она нужна.
    • Обновление статьи не меняет дату автоматически без содержательной причины.
    • Внутренние ссылки ведут только на существующие релевантные страницы.
    • После публикации проверены HTML, DOM, статусы, sitemap и мобильная версия.

    Источники

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

    Где хранить статьи React-блога?

    Небольшой блог можно хранить в Markdown-файлах репозитория. Для частого редактирования, ролей и согласований может подойти CMS или внешний источник данных.

    Нужно ли хранить slug отдельно от заголовка?

    Для стабильных публичных URL отдельное поле slug предпочтительно. Оно позволяет менять заголовок без обязательного изменения адреса.

    Что произойдёт при дублирующемся slug?

    Две записи будут претендовать на один URL. Сборка должна обнаруживать дубликат и завершаться ошибкой до публикации, а не выбирать запись случайно.

    Можно ли загружать текст статьи только через JavaScript?

    Для закрытого интерфейса это допустимо. Для публичной SEO-страницы необходимо проверить, что важное содержание доступно поисковому роботу и присутствует в исходном или заранее созданном HTML.

    Как исключить черновики из блога?

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

    Нужен ли отдельный маршрут для каждой статьи?

    Да, каждая публичная статья должна иметь постоянный URL, по которому её можно открыть напрямую и отличить от других материалов.

    Как реализовать страницу отсутствующей статьи?

    Маршрут должен определить, что запись не найдена, и отобразить ошибку. Серверная часть или слой публикации также должны вернуть корректное состояние отсутствующего ресурса, а не страницу другой статьи со статусом 200.

    Нужна ли пагинация небольшому блогу?

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

    Когда менять дату обновления?

    При содержательном пересмотре материала. Автоматическая смена даты при каждой сборке и изменение только форматирования не являются достаточным основанием.

    Как проверить готовность статьи?

    Открыть URL напрямую, проверить статус, исходный HTML, title, description, H1, основной текст, canonical, ссылки, мобильную версию и результат проверки в инструментах поисковой системы.

    Нужен сайт для вашего бизнеса?

    Уникальная разработка или быстрый старт — выберите свой путь