DEV Community

sunshey
sunshey

Posted on

How to Split a PDF Every N Pages in the Browser with Vue 3 and pdf-lib

Splitting a PDF at regular intervals is one of the simplest yet most useful operations. Whether you're extracting individual pages from a presentation, dividing a compiled report into sections, or preparing files for batch processing, splitting every N pages is a recurring need.

Unlike bookmark-based splitting (which respects document structure) or size-based splitting (which equalizes file sizes), interval-based splitting uses a pure mathematical rule: every N pages becomes one file.

Here's how to build it with Vue 3 and pdf-lib.

The core logic

The splitting is essentially integer division with remainder handling:

files = ceil(totalPages / N)
file[i] contains pages from (i * N) to min((i + 1) * N, totalPages)
Enter fullscreen mode Exit fullscreen mode

The stack

  • Vue 3 with Composition API
  • pdf-lib for PDF manipulation
  • Vite for bundling

The implementation

<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'

const file = ref<File | null>(null)
const interval = ref<number>(4)
const splitting = ref(false)
const results = ref<Record<string, Uint8Array>>({})

async function splitPdf() {
  if (!file.value) return
  splitting.value = true

  const arrayBuffer = await file.value.arrayBuffer()
  const pdf = await PDFDocument.load(arrayBuffer)
  const totalPages = pdf.getPageCount()
  const n = interval.value
  const fileCount = Math.ceil(totalPages / n)

  const results: Record<string, Uint8Array> = {}

  for (let i = 0; i < fileCount; i++) {
    const startIdx = i * n
    const endIdx = Math.min((i + 1) * n, totalPages)
    const pageIndices = Array.from(
      { length: endIdx - startIdx },
      (_, j) => startIdx + j
    )

    const newPdf = await PDFDocument.create()
    const [copiedPages] = await newPdf.copyPages(pdf, pageIndices)
    copiedPages.forEach(page => newPdf.addPage(page))

    const filename = `pages-${startIdx + 1}-${endIdx}.pdf`
    results[filename] = await newPdf.save()
  }

  // Package as ZIP and download
  const zip = await createZip(results)
  downloadZip(zip, `split-every-${n}-pages.zip`)
  splitting.value = false
}
</script>
Enter fullscreen mode Exit fullscreen mode

Key details

1. Page index handling

pdf-lib uses 0-based page indices internally, but users think in 1-based terms. The filename uses 1-based numbering for clarity:

const filename = `pages-${startIdx + 1}-${endIdx}.pdf`
// Pages 0-3 (0-based) → "pages-1-4.pdf" (user-friendly)
Enter fullscreen mode Exit fullscreen mode

2. Last file edge case

When totalPages isn't divisible by N, the last file naturally has fewer pages:

// 25 pages, interval = 4
// File 1: pages 1-4
// File 2: pages 5-8
// File 3: pages 9-12
// File 4: pages 13-16
// File 5: pages 17-20
// File 6: pages 21-24
// File 7: pages 25-25 (only 1 page)
Enter fullscreen mode Exit fullscreen mode

This is correct behavior — the rule is "every N pages," not "all files must have N pages."

3. Batch file generation

For documents with many split files (e.g., 100 pages split by 1 = 100 files), generating and downloading individually would be overwhelming. Always package as a ZIP:

async function createZip(files: Record<string, Uint8Array>): Promise<Blob> {
  const zip = new JSZip()
  for (const [name, data] of Object.entries(files)) {
    zip.file(name, data)
  }
  return await zip.generateAsync({ type: 'blob' })
}
Enter fullscreen mode Exit fullscreen mode

4. Progress indication

Splitting large documents takes time in the browser. Show a progress indicator:

const progress = ref(0)
const progressTotal = ref(0)

// Inside the loop:
progressTotal.value = fileCount
for (let i = 0; i < fileCount; i++) {
  // ... split ...
  progress.value = i + 1
}
Enter fullscreen mode Exit fullscreen mode

When to use this vs. other split methods

Method Best when Example
Every N pages Regular interval pattern Every 10 pages = one section
By Bookmarks Document has chapter structure Chapter-by-chapter split
By Page Range You know exact page boundaries Pages 1-5, 6-20, 21-end
By Size Equal file sizes needed 5MB per file
By Blank Pages Sections separated by blank pages Manual or automated separators

Summary

Building a browser-based "split every N pages" tool involves:

  1. Loading the PDF and getting total page count
  2. Computing the number of output files: ceil(totalPages / N)
  3. For each file, extracting the correct page range with copyPages()
  4. Packaging all outputs as a ZIP
  5. Downloading the result

The logic is simple arithmetic, but the UX matters — clear filename conventions, progress indication, and ZIP packaging make it feel polished. Try it at en.sotool.top/split-every-n-pages.

Top comments (0)