Script Valley
Next.js: Full-Stack React Applications
Database Integration with PrismaLesson 4.4

How to implement pagination with Prisma and Next.js

offset pagination, cursor-based pagination, take and skip, cursor and after, total count, page params, Prisma transaction for count

Offset Pagination

Offset pagination uses take (limit) and skip (offset). Read the page number from the URL's search params:

// app/blog/page.tsx
const PAGE_SIZE = 10

export default async function BlogPage({
  searchParams,
}: {
  searchParams: { page?: string }
}) {
  const page = Number(searchParams.page ?? 1)
  const skip = (page - 1) * PAGE_SIZE

  const [posts, total] = await prisma.$transaction([
    prisma.post.findMany({
      where: { published: true },
      take: PAGE_SIZE,
      skip,
      orderBy: { createdAt: 'desc' },
    }),
    prisma.post.count({ where: { published: true } }),
  ])

  const totalPages = Math.ceil(total / PAGE_SIZE)

  return (
    <div>
      {posts.map(p => <PostCard key={p.id} post={p} />)}
      <Pagination currentPage={page} totalPages={totalPages} />
    </div>
  )
}

Cursor-Based Pagination

// Faster for large datasets, but no random page access
const posts = await prisma.post.findMany({
  take: PAGE_SIZE,
  skip: cursor ? 1 : 0,  // Skip the cursor record itself
  cursor: cursor ? { id: cursor } : undefined,
  orderBy: { createdAt: 'desc' },
})
const nextCursor = posts[posts.length - 1]?.id

Offset pagination is simple but slow on large tables. Cursor-based pagination is fast regardless of dataset size but can't jump to arbitrary pages. Use offset for admin lists, cursor for infinite scroll feeds.

Up next

How to handle database errors and transactions in Prisma

Sign in to track progress