DEV Community

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.