DEV Community

sunshey
sunshey

Posted on

How I Rotate PDFs in the Browser with Vue 3 and pdf-lib

I needed a dead-simple way to fix rotated PDFs without sending them to a server. Most tools want you to upload the file, wait for processing, then download it. For scans and contracts that's a dealbreaker.

So I built en.sotool.top/rotate — pick a PDF, choose the pages and angle, get a correctly oriented file. All in the browser. No server involved.

Here's how it works under the hood with Vue 3 and pdf-lib.


Why Client-Side?

The obvious reason is privacy. Scans, contracts, medical records — people don't want them on a stranger's server. Beyond that:

  • No upload bandwidth limits
  • No file size caps from your backend
  • Nothing to clean up on a server
  • Works offline after the page loads

The catch? You're limited by what the browser can do. For rotation, pdf-lib is perfect because it can set the rotation angle on individual pages without re-rendering the entire PDF.


The Stack

  • Vue 3 — UI and state
  • pdf-lib — Load, modify, save PDFs
  • File API — Read the uploaded PDF
npm install pdf-lib
Enter fullscreen mode Exit fullscreen mode

Loading the PDF

First, read the file into an ArrayBuffer and load it with pdf-lib.

import { PDFDocument } from 'pdf-lib';

async function loadPdf(file) {
  const arrayBuffer = await file.arrayBuffer();
  const pdfDoc = await PDFDocument.load(arrayBuffer);
  return pdfDoc;
}
Enter fullscreen mode Exit fullscreen mode

Rotating Pages

Each page in pdf-lib has a setRotation method that takes a degrees value. Common angles are 90, 180, and 270.

function rotatePages(pdfDoc, angle, selectedPages = null) {
  const pages = pdfDoc.getPages();
  const targets = selectedPages ?? pages.map((_, i) => i);

  targets.forEach((index) => {
    const page = pages[index];
    if (!page) return;

    const currentRotation = page.getRotation().angle;
    const newRotation = (currentRotation + angle) % 360;
    page.setRotation({ angle: newRotation });
  });

  return pdfDoc;
}
Enter fullscreen mode Exit fullscreen mode

The key detail is reading the existing rotation first. Some PDFs already have a rotation value baked in, and you want to add to it, not overwrite it.


Saving and Downloading

Once the pages are rotated, save the document and trigger a download.

async function saveAndDownload(pdfDoc, filename) {
  const pdfBytes = await pdfDoc.save();
  const blob = new Blob([pdfBytes], { type: 'application/pdf' });

  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}
Enter fullscreen mode Exit fullscreen mode

Wiring It Up in Vue 3

A minimal component looks like this:

<template>
  <input type="file" accept="application/pdf" @change="handleFile" />
  <select v-model="angle">
    <option :value="90">90°</option>
    <option :value="180">180°</option>
    <option :value="270">270°</option>
  </select>
  <button @click="rotate" :disabled="!pdfDoc">Rotate PDF</button>
</template>

<script setup>
import { ref } from 'vue';
import { PDFDocument } from 'pdf-lib';

const pdfDoc = ref(null);
const angle = ref(90);
const originalName = ref('');

async function handleFile(event) {
  const file = event.target.files[0];
  if (!file) return;
  originalName.value = file.name.replace(/\.pdf$/i, '');
  const arrayBuffer = await file.arrayBuffer();
  pdfDoc.value = await PDFDocument.load(arrayBuffer);
}

async function rotate() {
  if (!pdfDoc.value) return;

  const pages = pdfDoc.value.getPages();
  pages.forEach((page) => {
    const current = page.getRotation().angle;
    page.setRotation({ angle: (current + angle.value) % 360 });
  });

  const bytes = await pdfDoc.value.save();
  const blob = new Blob([bytes], { type: 'application/pdf' });

  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${originalName.value}-rotated.pdf`;
  a.click();
  URL.revokeObjectURL(url);
}
</script>
Enter fullscreen mode Exit fullscreen mode

Handling Mixed Orientations

In the real tool, users can rotate all pages or pick specific ones. The logic is the same — you just pass a list of page indexes instead of rotating every page.

const selectedPages = [0, 2, 5]; // zero-based indexes
selectedPages.forEach((index) => {
  const page = pages[index];
  const current = page.getRotation().angle;
  page.setRotation({ angle: (current + angle) % 360 });
});
Enter fullscreen mode Exit fullscreen mode

Why Not Just Rotate the Canvas?

You could render each page to a canvas, rotate the canvas, and export a new PDF. That works, but it re-enders the content, which can lose vector quality, bloat file size, and break text selection. pdf-lib updates the page's rotation metadata directly, so the original content stays intact.


Try It

If you want to see it in action:

👉 en.sotool.top/rotate

Free, no signup, and your file never leaves the browser.


Built with Vue 3 and pdf-lib. If you need desktop-grade editing, check out Wondershare PDFelement.

Top comments (0)