All work

Document Editor & Pagination Engine

Diagnosed a pagination bug causing incorrect page splits and freezing on long documents, fixed the underlying rebuild-timing problem, then forked, documented and published my own maintained version of the package.

Client
Screenplay-writing product
Role
Pagination bug fix & package fork
Timeline
~1 month
Year
2025
View live project
300+
Pages handled per document
npm
Fork published and documented

01

The challenge

A screenplay is not a continuous document, it's a sequence of pages, and the page count is load-bearing: industry convention treats one formatted page as roughly one minute of screen time, so writers use it to estimate runtime.

I joined this product on an existing TipTap/ProseMirror pagination package that broke down on longer documents. Page-break calculations went wrong under load — some pages cramming in far more content than they should, others rendering blank — and the editor froze past a few hundred pages. Both symptoms traced back to the same root cause: how and when the pagination rebuild ran.

02

My role

I diagnosed and fixed a pagination bug in an existing TipTap/ProseMirror extension — I didn't design the original package.

An incremental-rebuild approach wasn't enough, so I changed how pagination rebuilds were scheduled and how DOM measurements were handled under load. I forked the package, documented the changes, and published my maintained version to npm.

I also built live page tracking for the editor UI and page-count extraction during file upload, including the backend and state changes needed to support both.

03

The engineering

Finding the real fix

The existing package rebuilt pagination in a way that broke down under load — the timing and frequency of rebuilds, combined with unsafe DOM measurement, produced both wrong page splits and input lag on longer documents.

My first attempt was an incremental rebuild — recomputing only the region a change affected instead of the whole document. It worked for smaller documents, but broke down again as page and word count grew: the same freezing returned, just at a higher threshold.

I replaced it with three changes instead: throttling how often rebuilds ran, deferring non-urgent rebuild work to idle time, and making DOM measurement safer so it stopped interleaving badly with layout changes. That combination held up at the document lengths that actually mattered.

Live page tracking

I built a page-tracking hook that locates paginated page nodes in the document, computes which page is in view on scroll, debounces the updates, and writes the current page into the editor's state store — shown in the editor footer as the current page of the total.

Page count on import

Creating a file from an upload needed its page count upfront. I added page-count extraction that reads PDF uploads via pdfjs-dist and Final Draft (FDX) uploads via the product's existing FDX parser, then passes that count through the create-file flow to be stored on the file record.

Backend and state plumbing

I extended the Express file-record helpers so the current page could persist, and extended the editor's client-side state and CSS so the pagination layout held together end-to-end.

04

What I shipped

  • Diagnosed and fixed a pagination bug causing incorrect page splits and freezing on long documents.
  • Forked, documented, and published a maintained pagination package to npm, replacing the original dependency.
  • Live current-page tracking in the editor UI.
  • Page-count extraction on file upload, reading PDF via pdfjs-dist and Final Draft via the product's existing parser.
  • Backend and state work to persist and support page tracking, plus editor CSS to keep the pagination layout intact.

05

Results

  • The fix held up at the document lengths screenplays actually reach (300+ pages), where the incremental-rebuild attempt had not.
  • Writers get accurate page counts on upload and live page position while editing, without the freezing or incorrect splits the previous implementation had.
  • My forked, documented package replaced the original dependency and is still a live dependency in the product today.

06

Stack

Editor
  • TipTap
  • ProseMirror
  • React
  • TypeScript
State & backend
  • Zustand
  • Express
Import
  • pdfjs-dist
Distribution
  • npm

Next breakdown

Role-based LMS

A learning and career-development platform for teams, combining courses, career ladders, skills assessments and reporting with separate trainee and manager experiences.