Skip to content

Build1 publisher2 min readPublished

Storyteller's EPUB library 1.0 can read books in a browser but edits them only on disk

Shane Friedman released version 1.0.0 of Storyteller's EPUB library on October 1, adding browser reads, EPUB 2-to-3 upgrades and a namespace-aware DOM. Teams building EPUB tools can now use code shaped by one product's needs, though adoption beyond Storyteller is unproven.

The Engineer · Build desk

Illustration accompanying Storyteller's EPUB library 1.0 can read books in a browser but edits them only on disk
Generated illustration

What happened

  • Friedman first pulled the code out of Storyteller in December 2024 and published it under the name @smoores/epub; a colleague had asked him how to produce EPUB files from code.
  • In the browser the library runs only on its read-only MemoryAdapter, so a page can open a book and fetch its cover image but cannot save changes to it.
  • Storyteller credits Thomas F. K. Jorna with building the EPUB 2 upgrade path, the browser support and both storage adapters.
  • Storyteller says its users have load-tested the library on "hundreds of thousands" of EPUB publications.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint A web app that needs to edit books has to send every write to a server-side runtime, because the only writable adapter unpacks the book into a temporary directory.
  • cost Code written against the @smoores/epub parser helpers has to be ported to standard DOM calls before it can move to the renamed 1.0 package.
  • decision Teams holding EPUB 2 files must add a conversion stage before any edit and pick in-place or new-path output; new-path output keeps the original if a conversion goes wrong.

Opening a book in 1.0 starts with choosing a storage adapter. The default, TmpFsAdapter, unpacks the EPUB into a temporary directory and permits changes [7]. MemoryAdapter reads the contents lazily into memory and is read-only [7].

A temporary directory has to be cleaned up. For Node.js 24 and later, the release post recommends JavaScript's `using` syntax, so the directory is removed when the EPUB object leaves scope [8]. Safari does not support `using`, so the browser example closes the EPUB resource explicitly [10]. In code that targets more than one runtime, I would write the explicit close and treat `using` as optional.

The XML rewrite is the best engineering in the release. EPUB files rely heavily on XML namespaces [11]. The earlier package parsed with fast-xml-parser and added helper methods to make the parsed data easier to walk [12]. Version 1 switches to @xmldom/xmldom and exposes ordinary DOM methods, including namespace-aware queries such as getAttributeNS() [12]. New trees can be built with a hyperscript-style `x` function or written as JSX [13]. Anyone who has debugged a namespace prefix inside a template literal will see the appeal. RuntimeWire expects the builders to make creating or adjusting metadata and chapter markup less dependent on string templates and custom parser helpers [14].

The package works at a lower level than an ebook reader. It inspects and modifies metadata, reading order, text and associated files, and it creates publications [6]. Its documentation expects developers to know at least some of the EPUB specification, and it offers higher-level methods for common changes [6]. That scope comes from the job the code was first written for. Storyteller had to change books that already existed, mark them up sentence by sentence and tie chapters to media overlays, and Friedman found the available libraries too limited or inflexible for that work [5].

The only evidence of scale comes from Storyteller. RuntimeWire calls the load-testing count a company-reported cumulative figure, not a performance benchmark or a measure of independent adoption [18]. It also reports that adoption beyond Storyteller's own use remains unproven [19]. A publisher running bulk metadata rewrites across a backlist is a different case. For the count to transfer there, that publisher's books would have to resemble the ones Storyteller users process. Its edits would also have to exercise the same code paths. Storyteller's own workload writes audio timing and text markup into EPUB 3 files [4].

What to watch

  • Whether a later release adds a writable adapter for browsers, which would allow client-side EPUB editors.
  • Whether packages outside Storyteller start listing @storyteller-platform/epub as an npm dependency.
  • Independent reports of how Epub.upgrade handles real EPUB 2 backlists.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories