How to Crop PDF Pages in the Browser with Vue 3 and pdf-lib
Why Client-Side?
Traditional PDF croppers upload your file, crop it on a server, and send it back. That works, but it introduces latency, bandwidth costs, and privacy risk. A browser-based cropper:
- Keeps files on the user's device
- Works offline after the app loads
- Avoids server-side processing entirely
The Stack
- Vue 3 with Composition API
- pdf-lib for PDF manipulation
- PDF.js for page preview rendering
- Canvas for interactive crop region selection
- Vite for bundling
Minimal Implementation
< script setup lang= "ts" >
import { ref } from ' vue '
import { PDFDocument , rgb } from ' pdf-lib '
import * as pdfjs from ' pdfjs-dist '
pdfjs . GlobalWorkerOptions . workerSrc = ' /pdf.worker.min.js '
const file = ref < File | null > ( null )
const cropping = ref ( false )
const margins = ref ({ top : 50 , bottom : 50 , left : 50 , right : 50 })
async function handleFileUpload ( selected : File ) {
file . value = selected
}
async function cropPdf () {
if ( ! file . value ) return
cropping . value = true
try {
const arrayBuffer = await file . value . arrayBuffer ()
const pdf = await PDFDocument . load ( arrayBuffer )
const pages = pdf . getPages ()
for ( const page of pages ) {
const { width , height } = page . getSize ()
const m = margins . value
// Clamp margins to page dimensions
const cropLeft = Math . min ( m . left , width / 2 - 1 )
const cropRight = Math . min ( m . right , width / 2 - 1 )
const cropTop = Math . min ( m . top , height / 2 - 1 )
const cropBottom = Math . min ( m . bottom , height / 2 - 1 )
page . setCropBox (
cropLeft ,
cropBottom ,
width - cropLeft - cropRight ,
height - cropTop - cropBottom
)
}
const croppedBytes = await pdf . save ()
const blob = new Blob ([ croppedBytes ], { type : ' application/pdf ' })
downloadBlob ( blob , ' cropped.pdf ' )
} finally {
cropping . value = false
}
}
function downloadBlob ( blob : Blob , name : string ) {
const url = URL . createObjectURL ( blob )
const a = document . createElement ( ' a ' )
a . href = url
a . download = name
a . click ()
URL . revokeObjectURL ( url )
}
</ script >
The key method is page.setCropBox(left, bottom, width, height) . It modifies the crop box - the visible area when the page is displayed. Unlike the media box (physical page size), the crop box doesn't change the underlying content; it just hides what's outside the visible region.
Interactive Crop Preview
For a better UX, render the page with PDF.js on a canvas and draw an overlay rectangle showing the crop region. Users can drag the edges of the rectangle to adjust margins visually:
function drawCropOverlay ( canvas : HTMLCanvasElement , page : pdfjs . PageViewport ) {
const ctx = canvas . getContext ( ' 2d ' ) !
ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
// Draw the page
page . draw ( ctx ). promise . then (() => {
// Draw semi-transparent overlay outside crop region
ctx . fillStyle = ' rgba(0, 0, 0, 0.5) '
const m = margins . value
// Top strip
ctx . fillRect ( 0 , 0 , canvas . width , canvas . height * ( m . top / page . height ))
// Bottom strip
ctx . fillRect ( 0 , canvas . height * (( page . height - m . bottom ) / page . height ),
canvas . width , canvas . height * ( m . bottom / page . height ))
// Left strip
ctx . fillRect ( 0 , canvas . height * ( m . top / page . height ),
canvas . width * ( m . left / page . width ),
canvas . height * ( 1 - ( m . top + m . bottom ) / page . height ))
// Right strip
ctx . fillRect ( canvas . width * (( page . width - m . right ) / page . width ),
canvas . height * ( m . top / page . height ),
canvas . width * ( m . right / page . width ),
canvas . height * ( 1 - ( m . top + m . bottom ) / page . height ))
})
}
UX Tips from a Live Tool
At en.sotool.top/crop-pdf, we learned a few things from real users:
- Provide presets. Most users just want to remove scan borders. Give them a one-click solution.
- Allow page-specific settings. Not every page in a document has the same margins.
- Show a before/after preview. Users need to see the result before downloading.
- Explain the difference between crop and trim. Crop hides content; trim destroys it.
Going Further
For simple PDF cropping, pdf-lib plus a canvas preview is enough. If you need batch processing, OCR, or conversion to editable formats, you'll want a desktop tool.
Want to see the full source? The site is built in public at github.com/sunshey/pdf-tool. If you need desktop-grade PDF editing - OCR, batch cropping, or advanced export formats - check out Wondershare PDFelement.
Comments
No comments yet. Start the discussion.