foliate-js is a free, open source publishing project written in JavaScript and released under MIT. It has 1,112 GitHub stars, 265 forks and 61 open issues, and was last pushed 5 months ago. On this registry it ranks #33 of 46 tracked projects in Publishing, with 5 head-to-head comparisons available.

What is foliate-js?

foliate-js is a pure-JavaScript library for rendering e-books directly in the browser, built for developers who want to embed a reader in a web application without a backend conversion step or a native rendering stack.

What it is

foliate-js is a library that parses e-book files and paginates them inside the browser. It ships as native ES modules with no build step, so the modules can be imported directly. It is licensed under MIT, written in JavaScript, and has no hard dependencies. The design is deliberately modular: parser modules implement a "book" interface, renderer modules implement a "renderer" interface, and the modules generally do not depend on each other directly. They instead depend on those interfaces, which is what makes it possible to swap in a new format or a different pagination strategy without touching the rest of the stack.

The concrete problem it solves is the format and pagination layer that a browser-based reader would otherwise have to write from scratch. Handling EPUB, Mobipocket, KF8, FB2, and comic archives means handling several unrelated container and markup conventions, plus reflowable and fixed-layout pagination, plus reading positions that survive a re-render. foliate-js supplies those pieces as separate modules rather than one monolith, and it does so without requiring the whole file to be loaded into memory. It lives in the JavaScript ecosystem, and it replaces the format-by-format parsing and pagination code that a reader application would have to build and maintain itself.

Key capabilities

  • Parses EPUB through epub.js and epubcfi.js, FictionBook 2 through fb2.js, comic book archives through comic-book.js, and both Mobipocket and KF8 (AZW3) through mobi.js.
  • Renders fixed-layout books with fixed-layout.js and reflowable books with paginator.js.
  • Accepts new formats by implementing the "book" interface.
  • Exposes view.js as the higher-level renderer and main entry point, which wires most of the other modules together.
  • Registers a foliate-view custom element whose open() method accepts a File, a Blob, a URL, or any object implementing the "book" interface.
  • Navigates with view.goTo(), taking a path, a section index, or a CFI, and emits a relocate event when the reading location changes.
  • Adds functionality through auxiliary modules, including overlayer.js for annotations, progress.js for reading progress, and search.js for searching.
  • Supports PDF experimentally, with PDF.js required.

Who uses it and how

  • The library has been used in several stable releases of Foliate, the desktop e-book reader, so it is exercised in production code even though the library's own API is not stable.
  • Developers who adopt it before a release exists are advised to include it as a git submodule, which makes updating to a new revision straightforward.
  • The repository includes a demo viewer for opening local files: serve the repository with a server and navigate to reader.html, or use the online demo hosted on GitHub.
  • Applications that need a custom interface are expected to modify reader.html or replace it with their own code, since the reader, along with reader.js and the files in ui/, is not considered part of the library itself.
  • Because the library does not care about older browsers, adopters can target modern browser environments without legacy fallbacks.

Getting started

Clone the repository and import view.js, then create a foliate-view element and call open() on it; there is no build step and no release yet, so the recommended installation method is a git submodule. For a quick look, serve the repository and open reader.html, or visit the online demo at https://johnfactotum.github.io/foliate-js/reader.html.

How it compares

Among the tools named in its own documentation, foliate-js is the rendering layer underneath Foliate, while reader.html is described as akin to Epub.js Reader, a reference interface rather than a supported part of the library. It also depends on PDF.js for its experimental PDF support, so that format is handled by an external project rather than by foliate-js itself.

When to use it — and when not

Adopting foliate-js means accepting that the library is explicitly not stable, that there is no release to pin, and that the API may change at any time, so teams that need a versioned dependency with compatibility guarantees should look elsewhere. A self-hoster also inherits specific operational requirements: EPUB scripted content is not supported, so a Content Security Policy must block all scripts except 'self', and deobfuscating fonts with the IDPF algorithm needs a SHA-1 function, which by default comes from Web Crypto and therefore requires a secure context. Without HTTPS, reader.js must be modified to pass a custom SHA-1 implementation. The bundled demo viewer is also very incomplete and lacks basic features such as keyboard shortcuts, so anyone wanting a finished reader should expect to write the interface themselves.

project readme (upstream, from github) — read inline

foliate-js

Library for rendering e-books in the browser.

Features:

  • Supports EPUB, MOBI, KF8 (AZW3), FB2, CBZ, PDF (experimental; requires PDF.js)
  • Add support for other formats yourself by implementing the book interface
  • Pure JavaScript
  • Small and modular
  • No hard dependencies
  • Does not require loading whole file into memory
  • Does not care about older browsers

Demo

The repo includes a demo viewer that can be used to open local files. To use it, serve the files with a server, and navigate to reader.html. Or visit the online demo hosted on GitHub. Note that it is very incomplete at the moment, and lacks many basic features such as keyboard shortcuts.

Also note that deobfuscating fonts with the IDPF algorithm requires a SHA-1 function. By default it uses Web Crypto, which is only available in secure contexts. Without HTTPS, you will need to modify reader.js and pass your own SHA-1 implementation.

Current Status

It works reasonably well, and has been used in several stable releases of Foliate. This library itself is, however, not stable. Expect it to break and the API to change at any time. Use it at your own risk.

If you do decide to use it, since there's no release yet, it is recommended that you include the library as a git submodule in your project so that you can easily update it.

Documentation

Overview

This project uses native ES modules. There's no build step, and you can import them directly.

There are mainly three kinds of modules:

  • Modules that parse and load books, implementing the "book" interface
    • comic-book.js, for comic book archives (CBZ)
    • epub.js and epubcfi.js, for EPUB
    • fb2.js, for FictionBook 2
    • mobi.js, for both Mobipocket files and KF8 (commonly known as AZW3) files
  • Modules that handle pagination, implementing the "renderer" interface
    • fixed-layout.js, for fixed layout books
    • paginator.js, for reflowable books
  • Auxiliary modules used to add additional functionalities
    • overlayer.js, for rendering annotations
    • progress.js, for getting reading progress
    • search.js, for searching

The modules are designed to be modular. In general, they don't directly depend on each other. Instead they depend on certain interfaces, detailed below. The exception is view.js. It is the higher level renderer that strings most of the things together, and you can think of it as the main entry point of the library. See "Basic Usage" below.

The repo also includes a still higher level reader, though strictly speaking, reader.html (along with reader.js and its associated files in ui/) is not considered part of the library itself. It's akin to Epub.js Reader. You are expected to modify it or replace it with your own code.

Basic Usage

To get started, clone the repo and import view.js:

import './foliate-js/view.js'

const view = document.createElement('foliate-view')
document.body.append(view)

view.addEventListener('relocate', e => {
    console.log('location changed')
    console.log(e.detail)
})

// can open a File/Blob object or a URL
// or any object that implements the "book" interface
await view.open('example.epub')
await view.goTo(/* path, section index, or CFI */)

See the online demo for a more advanced example.

Security

EPUB books can contain scripted content (i.e. JavaScript in the e-book), which is potentially dangerous, and is not supported by this library because

  • It is currently impossible to do so securely due to the content being served from the same origin (using blob: URLs).
  • Due to WebKit Bug 218086, the allow-scripts attribute is required on iframes, which renders iframe sandbox useless.

It is therefore imperative that you use Content Security Policy (CSP) to block all scripts except 'self'. An EPUB file for testing can be found at https://github.com/johnfactotum/epub-test.

[!CAUTION] Do NOT use this library (or any other e-book library, for that matter) without CSP unless you completely trust the content you're rendering or can block scripts by other means.

The Main Interface for Books

Processors for each book format return an object that implements the following interface:

  • .sections: an array of sections in the book. Each item has the following properties:
    • .load(): returns a string containing the URL that will be rendered. May be async.
    • .unload(): returns nothing. If present, can be used to free the section.
    • .createDocument(): returns a Document object of the section. Used for searching. May be async.
    • .size: a number, the byte size of the section. Used for showing reading progress.
    • .linear: a string. If it is "no", the section is not part of the linear reading sequence (see the linear attribute in EPUB).
    • .cfi: base CFI string of the section. The part that goes before the ! in CFIs.
    • .id: an identifier for the section, used for getting TOC item (see below). Can be anything, as long as they can be used as keys in a Map.
  • .dir: a string representing the page progression direction of the book ("rtl" or "ltr").
  • .toc: an array representing the table of contents of the book. Each item has
    • .label: a string label for the item
    • .href: a string representing the destination of the item. Does not have to be a valid URL.
    • .subitems: a array that contains TOC items
  • .pageList: same as the TOC, but for the page list.
  • .metadata: an object representing the metadata of the book. Currently, it follows more or less the metadata schema of Readium's webpub manifest. Note that titles and names can be a string or an object like { ja: "草枕", en: 'Kusamakura' }, and authors, etc. can be a string, an object, or an array of strings or objects.
  • .rendition: an object that contains properties that correspond to the rendition properties in EPUB. If .layout is "pre-paginated", the book is rendered with the fixed layout renderer.
  • .resolveHref(href): given an href string, returns an object representing the destination referenced by the href, which has the following properties:
    • .index: the index of the referenced section in the .section array
    • .anchor(doc): given a Document object, returns the document fragment referred to by the href (can either be an Element or a Range), or null
  • .resolveCFI(cfi): same as above, but with a CFI string instead of href
  • .isExternal(href): returns a boolean. If true, the link should be opened externally.

The following methods are consumed by progress.js, for getting the correct TOC and page list item when navigating:

  • .splitTOCHref(href): given an href string (from the TOC), returns an array, the first element of which is the id of the section (see above), and the second element is the fragment identifier (can be any type; see below). May be async.
  • .getTOCFragment(doc, id): given a Document object and a fragment identifier (the one provided by .splitTOCHref(); see above), returns a Node representing the target linked by the TOC item

In addition, the .transformTarget, if present, can be used to transform the contents of the book as it loads. It is an EventTarget with a custom event "data", whose .detail is { data, type, name }, where .data is either a string or Blob, or a Promise thereof, .type the content type string, and .name the identifier of the resource. Event handlers should mutate .data to transform the data.

Almost all of the properties and methods are optional. At minimum it needs .sections and the .load() method for the sections, as otherwise there won't be anything to render.

Archived Files

Reading Zip-based formats requires adapting an external library. Both epub.js and comic-book.js expect a loader object that implements the following interface:

  • .entries: (only used by comic-book.js) an array, each element of which has a filename property, which is a string containing the filename (the full path).
  • .loadText(filename): given the path, returns the contents of the file as string. May be async.
  • .loadBlob(filename): given the path, returns the file as a Blob object. May be async.
  • .getSize(filename): returns the file size in bytes. Used to set the .size property for `.sections

readme truncated — read the full docs on github

Frequently asked questions

Is foliate-js free to use?

foliate-js is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does foliate-js do?

Render e-books in the browser

What is foliate-js written in?

foliate-js is primarily written in JavaScript. Its source is publicly available at https://github.com/johnfactotum/foliate-js, and it has 1,112 GitHub stars.