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

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

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

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

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

Результат можно посмотреть в самом низу страницы с постами.

Разделение логики страницы на динамическую и статическую

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

Статические страницы в Next.js имеют ряд ограничений при работе с query string. По сути, единственный доступный в моём случае способ получить доступ к параметрам из URL - воспользоваться хуком useSearchParams из клиентского компонента.

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

import { Suspense } from "react";

export default function PostsPage() {
  const posts = getAllPostsMeta();
  const tags = getAllTags();

  return (
    <>
      // ...
      <Suspense fallback={<StaticPosts posts={posts} tags={tags} />}>
        <DynamicPosts posts={posts} tags={tags} />
      </Suspense>
      // ...
    </>
  );
}

Заметьте что компонент DynamicPosts обёрнут в Suspense. Фоллбек будет отрендерен в build-time. А компонент DynamicPosts будет отрендерен уже во время исполнения кода страницы на клиенте.

"use client"

// ...

const DynamicPage: React.FC<Props> = ({ posts, tags }) => {
  const searchParams = useSearchParams();

  const page = Number(searchParams.get("page")) || 1;
  const maxPage = Math.ceil(posts.length / POSTS_PER_PAGE);

  const pageOffset = (page - 1) * POSTS_PER_PAGE;

  const pagePosts = posts.slice(pageOffset, pageOffset + POSTS_PER_PAGE);

  if (!pagePosts.length) return notFound();

  return (
    <>
      <Posts posts={pagePosts} title={"Посты"} withFilter tags={tags} />
      <PageSelector page={page} maxPage={maxPage} />
    </>
  );
};

Для навигационного компонента, будем использовать компонент Link из встроенного пакета next/link и прокидывать в параметр href необходимую нам страницу.

<Link href={`./?page=${page}`}>{page}</Link>

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

Навигационный компонентНавигационный компонент

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

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

Добавление ссылки на каноническую страницу

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

<link rel="canonical" href="https://baranov.guru/posts" />

В Next.js есть специальное Metadata API, которое можно использовать как раз для этих целей.

Воспользуемся им:

export const metadata: Metadata = {
  title: "Посты | Алексей Баранов. Блог",
  description: "Страница со всеми опубликованными постами",
  alternates: {
    canonical: `/posts`,
  },
};

Добавление директивы Clean-Param в robots.txt

Яндекс поддерживает специальную директиву Clean-param в файлах robots.txt.

Давайте добавим её:

User-agent: Yandex
Clean-param: page
Allow: /

На этом всё! 🎉

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

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

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

Длинный список постов на одной странице тяжелее грузится и хуже воспринимается. Разбиение на страницы по ?page= сохраняет один URL /posts и даёт привычную навигацию — как в списке постов.

В статье не меняли структуру URL: ?page=2 проще внедрить в уже существующую статическую страницу. Для SSG с query нужен клиентский слой с useSearchParams — см. разделение логики.

На сервере в page.tsx вызывают getAllPostsMeta(), в Suspense рендерят DynamicPosts с useSearchParams и slice по POSTS_PER_PAGE. Фоллбек — статический список на этапе сборки.

Если после slice постов нет — вызывают notFound() (страница 404). Номер страницы берут из searchParams.get('page') с fallback-значением 1.

Компонент с Link из next/link и ссылками вида ./?page=2, ./?page=3 и т.д. Пример интерфейса — в разделе навигация.

Укажите canonical на /posts без параметра — через metadata.alternates.canonical в Next.js. Подробнее — каноническая страница.

Директива Clean-param: page для User-agent: Yandex — робот не будет считать ?page= разными страницами. Работает только в статическом robots; с app/robots.ts см. статью про robots. Раздел SEO.

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

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

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

5 мин.

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

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

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

6 мин.

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

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

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

4 мин.

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

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

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

4 мин.

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

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

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

9 мин.

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

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

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

5 мин.

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

Пост#nextjs#фиды
Добавляем Not Found (404) страницу в Next.js приложение
Добавляем Not Found (404) страницу в Next.js приложение

Добавляем Not Found (404) страницу в Next.js приложение

3 мин.

Инструкция по добавлению Not Found (404) страницы в Next.js приложение...

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

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

13 мин.

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

Пост#nextjs#yandex
Frontend разработка
Frontend разработка

Frontend разработка

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

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