Добавляем JSON-LD разметку к блогу на Next.js

Инструкция по добавлению JSON-LD разметки к блогу на Next.js.

Добавляем JSON-LD разметку к блогу на Next.js
Дата публикации
18.05.24
Дата обновления
16.05.26
Время чтения
9 мин.

В прошлом посте, мы рассказывали о том как сделать так, чтобы в выдаче Яндекса отображалась красивая галерея статей.

Пришло время сделать выдачу ещё лучше, а также поработать над выдачей в других поисковиках. Сделать это можно добавив на страницы разметку JSON-LD.

Эта статья показывает, как внедрить JSON-LD именно в блог на Next.js. Если вы только разбираетесь, зачем бизнесу структурированные данные и какие типы schema.org выбирать для сайта, сначала прочитайте обзорную статью JSON-LD для сайта, а затем возвращайтесь к этому техническому примеру.

Что такое JSON-LD?

JSON-LD (JSON Lightweight Linked Data format) — это формат метаданных для поисковых систем о типе контента на каждой странице. В теории, наличие подобной разметки на сайте приводит к более высоким результатам в поисковой выдаче.

JSON-LD выглядит так:

{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Добавляем JSON-LD разметку к блогу на Next.js",
  "description": "Инструкция по добавлению JSON-LD разметки к блогу на Next.js",
  "datePublished": "2024-05-18",
  "genre": "Technology",
  "author": {
    "@type": "Person",
    "name": "Алексей Баранов",
    "url": "https://baranov.guru"
  },
  "image": "https://baranov.guru/assets/posts/nextjs-json-ld/cover.webp"
}

Для чего нужен JSON-LD?

В поисковой выдаче Яндекса есть, как минимум, 2 элемента, которые напрямую зависят от JSON-LD:

  • Навигационные цепочки - они же хлебные крошки; Навигационные цепочкиНавигационные цепочки
  • Сниппет Вопрос-ответ; Вопрос-ответВопрос-ответ

С поисковой выдачей Google всё ещё интереснее. Они называют такую разметку Структурированными данными.

В документации для разработчиков есть целый раздел посвящённый Структурированным данным и JSON-LD.

От Структурированных данных зависят такие функции поисковой выдачи как:

  • Статья - название говорит само за себя;
  • Строка навигации - аналог Навигационных цепочек Яндекса;
  • Карусель - аналог Турбо карусели Яндекса;
  • Часто задаваемые вопросы - аналог Вопрос-ответ от Яндекса;
  • Товары - описание товаров и их характеристик;
  • Видео - описание и основные моменты видео;
  • Организация - название, часы работы, телефоны и прочее;
  • Мероприятие - где и когда состоится;

и многие другие (я насчитал 36 штук).

Для начала мне хватит просто Навигационных цепочек и теоретического улучшения позиций в выдаче 🙂

Итак, для добавления JSON-LD нам нужно:

Ссылка на конечный результат.

Формирование JSON-LD

Как не трудно догадаться из названия, JSON-LD - это JSON объект, составленный по определённой схеме. Для валидации схемы уже есть npm пакет schema-dts.

Если нужен быстрый черновик без кода — воспользуйтесь нашим генератором JSON-LD: выберите тип schema.org (BlogPosting, BreadcrumbList, FAQPage и др.), заполните поля и скопируйте готовый JSON или тег script.

Установим schema-dts:

npm i schema-dts

Далее создадим объект для поста этого блога.

import { BlogPosting, WithContext } from "schema-dts";

export const URL_BASE = "https://baranov.guru";
export const HOME_OG_IMAGE_URL = "https://baranov.guru/logo.webp";

const blogPosting: WithContext<BlogPosting> = {
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  name: post.title,
  headline: post.title,
  description: post.description,
  datePublished: new Date(post.date).toISOString(),
  genre: "Technology",
  author: {
    "@type": "Person",
    name: post.author.name,
    url: `${URL_BASE}/about`,
  },
  publisher: {
    "@type": "Organization",
    name: "Алексей Баранов. Блог",
    logo: {
      "@type": "ImageObject",
      url: HOME_OG_IMAGE_URL,
    },
  },
  image: [`${URL_BASE}${post.ogImage.url}`],
  mainEntityOfPage: {
    "@type": "WebPage",
    "@id": `${URL_BASE}/posts/${post.slug}`,
  },
  inLanguage: "ru-RU",
};

Теперь необходимо сформировать BreadcrumbList для Навигационных цепочек:

import { BreadcrumbList, WithContext } from "schema-dts";

export const URL_BASE = "https://baranov.guru";

const breadcrumbList: WithContext<BreadcrumbList> = {
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  itemListElement: [
    {
      "@type": "ListItem",
      position: 1,
      name: "Посты",
      item: `${URL_BASE}/posts`,
    },
    {
      "@type": "ListItem",
      position: 2,
      name: post.title,
      item: `${URL_BASE}/posts/${post.slug}`,
    },
  ],
};

Ну и на сладкое мы можем добавить информацию о видео содержащихся на странице:

import { VideoObject, WithContext } from "schema-dts";

const videoStructuredData: WithContext<VideoObject> = {
  "@context": "https://schema.org",
  "@type": "VideoObject",
  name: post.title,
  description: post.description,
  thumbnailUrl: `https://i.ytimg.com/vi/${post.youtubeId}/hqdefault.jpg`, // Миниатюра видео
  uploadDate: new Date(post.date).toISOString(),
  contentUrl: `https://www.youtube.com/watch?v=${post.youtubeId}`, // Ссылка на видео на Youtube
  embedUrl: `https://www.youtube.com/embed/${post.youtubeId}`, // Ссылка на встроенное видео с Youtube
};

Объединяем всё это вместе в один объект:

const jsonLd = [blogPosting, breadcrumbList, videoStructuredData];

Добавление на страницу

Для того чтобы добавить разметку на страницу, создадим компонент JsonLd:

import React from "react";

type Props = {
  data: unknown;
};

const JsonLd: React.FC<Props> = ({ data }) => (
  <script
    type="application/ld+json"
    dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
  />
);

export default JsonLd;

Далее просто добавляем этот компонент на страницу с постом и передаём в него данные:

const jsonLd = [blogPosting, breadcrumbList, videoStructuredData];

<JsonLd data={jsonLd} />

Теперь необходимо проверить что у нас всё получилось как надо.

Тестирование

Заходим на страницу и смотрим разметку:

Разметка страницыРазметка страницы

Итак, разметка появилась на странице.

Теперь необходимо проверить что разметка валидная. Для этого можно воспользоваться нашим инструментом:

Так же у Google есть специальный инструмент для проверки — вставьте URL страницы или HTML с уже сгенерированной разметкой (в том числе из генератора).

Инструмент для проверкиИнструмент для проверки

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

Результат проверкиРезультат проверки

Отлично, на этом всё! 🎉

Теперь выкладываем страницу и ждём индексации.

Готовое решение

Прикладываю полный код решения.

// types.ts
export type Author = {
  name: string;
  picture: string;
};

export type Post = {
  slug: string;
  title: string;
  description: string;
  date: string;
  coverImage: string;
  author: Author;
  excerpt: string;
  ogImage: {
    url: string;
  };
  keywords: string[];
  content: string;
  preview?: boolean;
  youtubeId?: string;
};
// src/lib/jsonLd.ts
import {
  BlogPosting,
  BreadcrumbList,
  VideoObject,
  WithContext,
} from "schema-dts";

import { Post } from "@/interfaces/post";

import { HOME_OG_IMAGE_URL, URL_BASE } from "./constants";

export const getPostJsonLd = (post: Post) => {
  const blogPosting: WithContext<BlogPosting> = {
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    name: post.title,
    headline: post.title,
    description: post.description,
    datePublished: new Date(post.date).toISOString(),
    genre: "Technology",
    author: {
      "@type": "Person",
      name: post.author.name,
      url: `${URL_BASE}/about`,
    },
    publisher: {
      "@type": "Organization",
      name: "Алексей Баранов. Блог",
      logo: {
        "@type": "ImageObject",
        url: HOME_OG_IMAGE_URL,
      },
    },
    image: [`${URL_BASE}${post.ogImage.url}`],
    mainEntityOfPage: {
      "@type": "WebPage",
      "@id": `${URL_BASE}/posts/${post.slug}`,
    },
    inLanguage: "ru-RU",
  };

  const breadcrumbList: WithContext<BreadcrumbList> = {
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    itemListElement: [
      {
        "@type": "ListItem",
        position: 1,
        name: "Посты",
        item: `${URL_BASE}/posts`,
      },
      {
        "@type": "ListItem",
        position: 2,
        name: post.title,
      },
    ],
  };

  if (post.youtubeId) {
    const videoStructuredData: WithContext<VideoObject> = {
      "@context": "https://schema.org",
      "@type": "VideoObject",
      name: post.title,
      description: post.description,
      thumbnailUrl: `https://i.ytimg.com/vi/${post.youtubeId}/hqdefault.jpg`, // this is the thumbnail for the video straight from youtube
      uploadDate: new Date(post.date).toISOString(),
      contentUrl: `https://www.youtube.com/watch?v=${post.youtubeId}`, // this is the URL for the video on youtube
      embedUrl: `https://www.youtube.com/embed/${post.youtubeId}`, // this is the URL for the video embed on youtube
    };
    return [blogPosting, breadcrumbList, videoStructuredData];
  }

  return [blogPosting, breadcrumbList];
};
// src/app/posts/[slug]/page.ts
export default async function Post({ params }: Params) {
  const jsonLd = getPostJsonLd(post);

  return (
    <>
      <JsonLd data={jsonLd} />
      ...
      </>
  );
}

На этом всё. Спасибо за внимание!

Расскажите о вашем проекте

Связаться иначе

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

Формат структурированных данных в виде JSON внутри страницы: поисковики понимают, что это статья, хлебные крошки, видео и т.д. Схема — schema.org, контекст @context. Пример и раздел что такое JSON-LD.

Чтобы в выдаче Яндекса и Google чаще появлялись расширенные сниппеты: хлебные крошки, блок вопрос–ответ, карточка статьи, видео. Это не прямой «буст» позиций, но улучшает вид ссылки. См. для чего нужен.

Пакет schema-dts: типы BlogPosting, BreadcrumbList, VideoObject и WithContext помогают не ошибиться в полях при сборке объекта. Установка — в разделе формирование.

Минимум: BlogPosting (заголовок, дата, автор, изображение) и BreadcrumbList (цепочка «Посты → статья»). Если в посте есть YouTube — добавьте VideoObject. Код — в статье и в готовом решении. Черновик можно собрать в генераторе JSON-LD.

Компонент со script type="application/ld+json" и JSON.stringify данных (в статье — JsonLd). Подключите его на page поста рядом с основным контентом. Раздел добавление на страницу.

В DevTools — блок application/ld+json на странице, затем Google Rich Results Test по URL или HTML. Раздел тестирование. Появление в выдаче — после переобхода и индексации.

Да, они дополняют друг друга. JSON-LD объясняет поисковику, что за страница (статья, крошки, видео). Sitemap — какие адреса есть на сайте (как сделать). Robots.txt — что роботу можно обходить (настройка). Лучше подключить все три.

Вам может быть интересно

JSON-LD для сайта: как структурированные данные помогают SEO и бизнесу
JSON-LD для сайта: как структурированные данные помогают SEO и бизнесу

JSON-LD для сайта: как структурированные данные помогают SEO и бизнесу

27 мин.

Разбираемся, что такое JSON-LD, почему структурированные данные важны для SEO, какие типы разметки нужны бизнес-сайту и как проверить, что поисковые системы понимают страницы правильно.

Пост#seo#json-ld
Генератор JSON-LD
Генератор JSON-LD

Генератор JSON-LD

Соберите JSON-LD для страницы: выберите тип schema.org, заполните поля — получите JSON и готовый тег script для вставки в разметку.

Инструмент#seo#json-ld
Валидатор JSON-LD
Валидатор JSON-LD

Валидатор JSON-LD

Проверка JSON-LD по URL страницы: извлечение блоков разметки, типы schema.org и замечания по обязательным полям.

Инструмент#seo#json-ld
Техническая SEO-оптимизация сайта
Техническая SEO-оптимизация сайта

Техническая SEO-оптимизация сайта

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

Услуга#seo#technical-seo
Frontend разработка
Frontend разработка

Frontend разработка

Разрабатываем интерфейсы, которые быстро работают, удобно используются и масштабируются вместе с продуктом.

Услуга#frontend#веб-разработка
Разработка вебсайтов
Разработка вебсайтов

Разработка вебсайтов

Сайт под ваш бизнес: от структуры и дизайна до запуска и масштабирования. Быстро, продуманно и с фокусом на результат.

Услуга#веб-разработка#сайты
Добавляем Google Analytics в Next.js приложение
Добавляем Google Analytics в Next.js приложение

Добавляем Google Analytics в Next.js приложение

2 мин.

Инструкция по добавлению Google Analytics в Next.js приложение...

Пост#seo#nextjs
Добавляем sitemap.xml в Next.js приложение
Добавляем sitemap.xml в Next.js приложение

Добавляем sitemap.xml в Next.js приложение

9 мин.

Инструкция по добавлению sitemap.xml в Next.js приложение...

Пост#nextjs#seo
Добавляем robots.txt в Next.js приложение
Добавляем robots.txt в Next.js приложение

Добавляем robots.txt в Next.js приложение

5 мин.

Инструкция по добавлению robots.txt в Next.js приложение...

Пост#nextjs#seo