Reorganizing PDF pages requires manipulating page order, inserting blank pages, and managing page subsets.
Here's how to build a browser-based PDF organization tool with Vue 3 and pdf-lib.
The challenge: Flexible page management
PDF organization involves:
- Loading the source PDF
- Displaying pages as draggable thumbnails
- Allowing reordering, deletion, and insertion
- Generating a new PDF with the modified page sequence
The stack
- Vue 3 with Composition API
- pdf-lib for PDF manipulation
- SortableJS for drag-and-drop reordering
- Vite for bundling
The core implementation
<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'
import Sortable from 'sortablejs'
const file = ref<File | null>(null)
const pages = ref<number[]>([])
const organizing = ref(false)
const result = ref<Uint8Array | null>(null)
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)
pages.value = Array.from({ length: pdfDoc.getPageCount() }, (_, i) => i)
}
function initDragDrop() {
const el = document.getElementById('page-list')
if (!el) return
Sortable.create(el, {
animation: 150,
onEnd: (evt) => {
const oldIndex = evt.oldIndex!
const newIndex = evt.newIndex!
const [moved] = pages.value.splice(oldIndex, 1)
pages.value.splice(newIndex, 0, moved)
}
})
}
async function organizePdf() {
if (!file.value || pages.value.length === 0) return
organizing.value = true
const arrayBuffer = await file.value.arrayBuffer()
const pdfDoc = await PDFDocument.load(arrayBuffer)
const newPdf = await PDFDocument.create()
for (const pageIndex of pages.value) {
const [copiedPage] = await newPdf.copyPages(pdfDoc, [pageIndex])
newPdf.addPage(copiedPage)
}
result.value = await newPdf.save()
organizing.value = false
}
</script>
Key implementation details
1. Page index tracking
Maintain an array of page indices that represents the current order:
const pages = ref<number[]>([])
// Initially: [0, 1, 2, 3, ...]
2. Drag-and-drop reordering
Use SortableJS to handle visual reordering and update the index array:
Sortable.create(el, {
onEnd: (evt) => {
const [moved] = pages.value.splice(evt.oldIndex, 1)
pages.value.splice(evt.newIndex, 0, moved)
}
})
3. Creating the organized PDF
Copy pages in the new order:
for (const pageIndex of pages.value) {
const [copiedPage] = await newPdf.copyPages(pdfDoc, [pageIndex])
newPdf.addPage(copiedPage)
}
4. Adding blank pages
Insert a blank page at a specific index:
pages.value.splice(insertIndex, 0, -1) // -1 represents blank page
Then handle it during export:
if (pageIndex === -1) {
newPdf.addPage() // Add blank page
} else {
const [copiedPage] = await newPdf.copyPages(pdfDoc, [pageIndex])
newPdf.addPage(copiedPage)
}
Limitations
No visual page preview
Generating thumbnail previews for all pages can be memory-intensive.
Solution: Generate thumbnails lazily or limit to first N pages.
Single file at a time
Can only organize one PDF per session.
Solution: Add support for multiple file uploads.
No undo after export
Once downloaded, changes cannot be undone.
Solution: Add an undo stack for recent operations.
Summary
Building a browser-based PDF organization tool involves:
- Using pdf-lib to load and analyze PDFs
- Implementing drag-and-drop reordering
- Copying pages in the desired order
- Supporting blank page insertion
Try it at en.sotool.top/organize.
Top comments (0)