Добавляем поиск по тегам в статический блог на Next.js

Инструкция по добавлению простейшего поиска по тегам в статическом Next.js блоге.

Добавляем поиск по тегам в статический блог на Next.js
Дата публикации
02.06.24
Дата обновления
16.05.26
Время чтения
5 мин.

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

Вот так он выглядит на момент написания этой статьи:

РезультатРезультат

Процесс можно разделить на несколько этапов:

Добавление тегов к постам

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

Например, для этой статьи массив выглядит следующим образом:

tags: ["улучшения блога", "nextjs"]

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

Генерация страниц для каждого тега

Генерация страниц для тегов очень похожа на генерацию страниц постов.

Алгоритм очень простой и выглядит следующим образом:

Сбор уникальных тегов

О том как получить все посты и их метаданные мы писали в статье о добавлении RSS фида.

Там же можно найти и полную реализацию метода getAllPosts.

А нам же остаётся только добавить все теги в массив и удалить оттуда дубли:

export function getAllTags(): string[] {
  const notUniqueTags = getAllPosts().flatMap((p) =>
    p.tags ? [...p.tags] : [],
  );

  return Array.from(new Set(notUniqueTags));
}

Генерация страниц

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

export function getAllPostsByTag(tag: string): Post[] {
  return getAllPosts().filter((p) => p.tags?.includes(tag));
}

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

type Params = {
  params: {
    tag: string;
  };
};

// Компонент для отображения страницы
export default async function TagPage({ params }: Params) {
  const decodedTag = getTagFromURI(params.tag);

  const posts = getAllPostsByTag(decodedTag);

  if (!posts.length) {
    return notFound();
  }

  return (
    <>
      // Отображаем тут наши посты
    </>
  );
}

И генерируем статические страницы:

// Генерируем статические страницы в build-time
export async function generateStaticParams() {
  const tags = getAllTags();

  return tags.map((t) => ({
    tag: getTagURI(t),
  }));
}

Неочевидные моменты

Вы наверняка заметили что мы используем функции getTagFromURI и getTagURI при работе с тегами.

Дело в том что на данный момент в Next.js существуют сложности с кириллическими путями.

export const getTagURI = (tag: string) => {
  return process.env.NODE_ENV === "development" ? encodeURIComponent(tag) : tag;
};

export const getTagFromURI = (uri: string) => {
  return decodeURIComponent(uri);
};

Тут сказать особо нечего, единственный момент, который следует учитывать - не забывайте оборачивать теги в encodeURIComponent.

const TagLinks: React.FC = () => {
  const tags = getAllTags();

  return (
    <div>
      {tags.map((item, i) => (
        <Link
          href={`/tags/${getTagURI(item)}`}
          key={i}
        >
          {`#${item}`}
        </Link>
      ))}
    </div>
  );
};

Работа над поисковой выдачей

Для улучшения индексирования сгенерированные страницы можно добавить в файл sitemap сайта.

Для этого, в файл sitemap.ts, лежащий в корне сайта добавляем следующий код:

import { MetadataRoute } from "next";

import { getAllTags, getTagURI } from "@/lib/api";

export default function sitemap(): MetadataRoute.Sitemap {
  const tags = getAllTags();
  return [
    // тут остальные страницы сайта
    ...tags.map((tag) => ({
      url: `https://baranov.guru/tags/${getTagURI(tag)}`,
      lastModified: new Date(),
      priority: 0.7,
    })),
  ];
}

На этом всё! 🎉

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

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

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

Когда постов много, удобнее открыть тему целиком — #nextjs, #seo и т.д. — чем листать архив. В статье теги ведут на отдельные URL /tags/… со списком статей.

В frontmatter каждого .md — массив tags, как у рекомендаций. Пример: tags: ["улучшения блога", "nextjs"]. Раздел добавление тегов.

Пройти getAllPosts(), взять tags из каждого поста и убрать дубли через Set — функция getAllTags(). Получение постов — как в статье про RSS.

Маршрут app/tags/[tag]/page.tsx: getAllPostsByTag, generateStaticParams по всем тегам, при пустом списке — notFound(). Подробнее — генерация страниц.

В Next.js кириллические сегменты пути ведут себя по-разному в dev и build. В статье теги кодируют через encodeURIComponent / getTagURI и getTagFromURI — см. неочевидные моменты.

Да: в href используйте getTagURI (или encodeURIComponent), иначе ссылки на теги с пробелами и спецсимволами могут ломаться. Пример TagLinks — в разделе ссылки.

Имеет смысл: в app/sitemap.ts допишите URL /tags/… для каждого тега — так поисковик быстрее найдёт hub-страницы. Пример кода — в разделе SEO и в статье про sitemap.

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

Добавляем пагинацию в статический блог на Next.js
Добавляем пагинацию в статический блог на Next.js

Добавляем пагинацию в статический блог на Next.js

4 мин.

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

Пост#туториалы#nextjs
Добавляем кнопку для сбора пожертвований (донатов) к блогу
Добавляем кнопку для сбора пожертвований (донатов) к блогу

Добавляем кнопку для сбора пожертвований (донатов) к блогу

6 мин.

Инструкция по добавлению кнопки для сбора пожертвований (донатов) к блогу...

Пост#туториалы
Добавляем рекомендации постов и блок "Поделиться в соц. сетях"
Добавляем рекомендации постов и блок "Поделиться в соц. сетях"

Добавляем рекомендации постов и блок "Поделиться в соц. сетях"

4 мин.

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

Пост#туториалы
Как посчитать время чтения текста (и добавить индикатор на сайт)
Как посчитать время чтения текста (и добавить индикатор на сайт)

Как посчитать время чтения текста (и добавить индикатор на сайт)

4 мин.

Инструкция по добавлению индикатора времени чтения текста на сайт...

Пост#туториалы
Добавляем RSS-фид к статическому Next.js приложению
Добавляем RSS-фид к статическому Next.js приложению

Добавляем RSS-фид к статическому Next.js приложению

5 мин.

Инструкция по добавлению RSS фида к статическому Next.js приложению...

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

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

9 мин.

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

Пост#nextjs#seo
Подключение счётчика Яндекс Метрики к Next.js приложению
Подключение счётчика Яндекс Метрики к Next.js приложению

Подключение счётчика Яндекс Метрики к Next.js приложению

13 мин.

Инструкция по добавлению счётчика Яндекс Метрики к Next.js блогу...

Пост#nextjs#yandex
Автопостинг в VK через RSS feed
Автопостинг в VK через RSS feed

Автопостинг в VK через RSS feed

4 мин.

Заметили в настройках сообщества в VK интересную настройку, связанную с фидами. И понеслось...

Пост#соцсети#фиды
Добавляем поддержку MDX в Next.js приложение
Добавляем поддержку MDX в Next.js приложение

Добавляем поддержку MDX в Next.js приложение

3 мин.

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

Пост#nextjs