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)
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>
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)
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)
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' })
}
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
}
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:
- Loading the PDF and getting total page count
- Computing the number of output files:
ceil(totalPages / N) - For each file, extracting the correct page range with
copyPages() - Packaging all outputs as a ZIP
- 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)