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
- 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.