Paginate with Sanity's GROQ and NEXT/NAVIGATION

Daniel SimonsonDaniel Simonson
3 min read

I was working on a project and learned some stuff while implement pagination which I'm passing along.

Today we'll be going through some of the details of Sanity's GROQ and Next's navigation methods.

For those who don't know or haven't heard, GROQ is a company that has made huge strides in increasing the speed of Artificial Intelligence language processing.

Here's the query Sanity's GROQ helped me return.

async function getData(lastPageNum: number = 1) {
  const query = `*[_type == "blog"] | order(_createdAt, desc) [${lastPageNum}...${lastPageNum + 3}] {
  _id, title, _createdAt, content, smallDescription, titleImage, "currentSlug": slug.current
}`;

  const data = await client.fetch(query, { lastId: lastPageNum });
  return data;
}

The asterisk symbol at the beginning tells sanity to fetch everything. But stopping there means we would be fetching a lot of unnecessary stuff and we can narrow things down by asking for only Blog types and specifying the fields we will be using.

The trick for the pagination is returning results in descending order with the createdAt field. And to keep track of our pagination with the last page number. This is, in fact, for those of you who are interested in computer science, a recursive function.

So GROQ has given us the ability to get, order and return our data.

Now we have to get that data into our next app.

For that, we turn to Next's new Navigation methods. We turn to searchParams which we pass to our Home page.

export default async function Home({
  searchParams,
}: {
  searchParams?: {
    query?: string;
    page?: string;
  };
}) {
  const page = Number(searchParams?.page ?? 0);
  const data: simpleBlogCard[] = await getData(page);

  ................

The searchParams has two elements: query and page. After being converted to a Number - our last page number - it is passed to our getData function.

The simple BlogCard[] interface keeps typescript happy.

export interface simpleBlogCard {
  title: string;
  smallDescription: string;
  currentSlug: string;
  titleImage: any;
}

Now we can get the data we need but still need to figure out a way to physically paginate.

Shadcn/ui's pagination element is a good way to do this and with the help of 'searchParams', 'usePathname and 'useRouter ' hooks we can move through the blog data.

I little simple logic allows us to stop paginating when reaching the end of our data by ending you pagination with the last of the last page numbers or when moving in the opposite direction, ending with 0.

The pagination happens here.

"use client";

import { usePathname, useRouter, useSearchParams } from "next/navigation";

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from "@/components/ui/pagination";

let page = 0;

function Paginate() {
  const pathname = usePathname();
  const router = useRouter();
  const searchParams = useSearchParams();

  const nextPage = () => {
    if (page === 3) {
      return;
    } else {
      page = page + 3;
    }
    const params = new URLSearchParams(searchParams);
    params.set("page", page.toString());
    router.push(`${pathname}?${params}`, { scroll: false });
  };
  const prevPage = () => {
    console.log("page: ", page);
    if (page === 0) {
      return;
    } else {
      page = page - 3;
    }
    const params = new URLSearchParams(searchParams);
    params.set("page", page.toString());
    router.push(`${pathname}?${params}`, { scroll: false });
  };

  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious onClick={prevPage} />
        </PaginationItem>
        <PaginationItem>
          <PaginationLink></PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationNext onClick={nextPage} />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  );
}

export default Paginate;

I have a demo and it's on my current profile page.

The code is in this github repository

0
Subscribe to my newsletter

Read articles from Daniel Simonson directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Daniel Simonson
Daniel Simonson

I am a software developer with a bachelor's degree in Computer Science. I've worked as a frontend React and Vue developer at various times as well as filling roles requiring full-stack development. I'm interested in full-stack / frontend JavaScript development using various frameworks and tools. I love coding and creating websites and apps. I focus on trying to get a little bit better than I was yesterday.