Splitting a PDF into multiple files requires careful handling of page ranges and document structure.
Here's how to build a browser-based PDF split tool with Vue 3 and pdf-lib.
The challenge: Flexible page splitting
PDF splitting involves:
- Loading the source PDF
- Understanding page structure and count
- Dividing pages based on user criteria
- Creating separate PDFs for each split
The stack
- Vue 3 with Composition API
- pdf-lib for PDF manipulation
- JSZip for ZIP bundling
- Vite for bundling
The core implementation
<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'
import JSZip from 'jszip'
const file = ref<File | null>(null)
const totalPages = ref(0)
const splitMethod = ref<'range' | 'every'>('range')
const startPage = ref(1)
const endPage = ref(1)
const everyNPages = ref(10)
const splitting = ref(false)
const results = ref<Blob[]>([])
async function handleFile(e: Event) {
const input = e.target as HTMLInputElement
if (!input.files?.[0]) return
file.value = input.files[0]
const arrayBuffer = await file.value.arrayBuffer()
const pdfDoc = await PDFDocument.load(arrayBuffer)
totalPages.value = pdfDoc.getPageCount()
}
async function splitPdf() {
if (!file.value) return
splitting.value = true
const arrayBuffer = await file.value.arrayBuffer()
const pdfDoc = await PDFDocument.load(arrayBuffer)
if (splitMethod.value === 'range') {
const start = Math.max(1, startPage.value - 1)
const end = Math.min(totalPages.value, endPage.value - 1)
const pagesToCopy = Array.from({ length: end - start + 1 }, (_, i) => start + i)
const newPdf = await PDFDocument.create()
const copiedPages = await newPdf.copyPages(pdfDoc, pagesToCopy)
copiedPages.forEach(page => newPdf.addPage(page))
results.value = [await newPdf.save()]
} else {
// Split every N pages
const chunks = []
for (let i = 0; i < totalPages.value; i += everyNPages.value) {
const chunkPages = Array.from(
{ length: Math.min(everyNPages.value, totalPages.value - i) },
(_, j) => i + j
)
chunks.push(chunkPages)
}
results.value = await Promise.all(
chunks.map(async (pageIndices) => {
const newPdf = await PDFDocument.create()
const copiedPages = await newPdf.copyPages(pdfDoc, pageIndices)
copiedPages.forEach(page => newPdf.addPage(page))
return await newPdf.save()
})
)
}
splitting.value = false
}
</script>
Key implementation details
1. Page indexing
pdf-lib uses zero-based indexing internally, but users expect one-based:
const start = Math.max(1, startPage.value - 1) // Convert to zero-based
2. Range validation
Ensure page ranges are within valid bounds:
const end = Math.min(totalPages.value, endPage.value - 1)
3. ZIP bundling for multiple splits
When splitting into many files, bundle them into a ZIP:
const zip = new JSZip()
results.value.forEach((blob, i) => {
zip.file(`page-${i + 1}.pdf`, blob)
})
const zipBlob = await zip.generateAsync({ type: 'blob' })
4. Progress tracking
For large PDFs, show progress during splitting:
// Use async/await with small delays for UI updates
for (const chunk of chunks) {
await processChunk(chunk)
progress.value = (i + 1) / chunks.length
}
Limitations
No interactive page selection
Users must specify ranges, not click individual pages.
Solution: Add a visual page selector UI.
Single file per operation
Can only split one PDF at a time.
Solution: Add batch processing support.
Memory constraints
Large PDFs may exhaust browser memory during splitting.
Solution: Process in smaller chunks and show progress.
Summary
Building a browser-based PDF split tool involves:
- Using pdf-lib to load and analyze PDFs
- Providing flexible split options (range or N pages)
- Creating new PDFs from page subsets
- Offering ZIP download for multiple splits
Try it at en.sotool.top/split.