Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/johnfactotum/foliate-js/llms.txt

Use this file to discover all available pages before exploring further.

All book parsers in foliate-js — whether for EPUB, MOBI, FictionBook, or comic archives — return an object that implements the same interface. This shared contract is what lets the renderers remain format-agnostic: they only need to know how to call .sections[n].load() and respond to events, not how ZIP decompression or package document parsing works. You can also implement this interface yourself to add support for custom formats without touching any renderer code.

The book object

At minimum, a book object needs .sections and a .load() method on each section item. Everything else is optional and progressively enables features like reading progress, TOC tracking, and CFI-based navigation.

.sections

An array of section objects representing the spine of the book. Each item in the array has the following properties:
sections
object[]
required
Array of section objects. Each section represents one loadable unit of content (typically one HTML document in an EPUB spine).
dir
"rtl" | "ltr"
Page progression direction of the book. The view element uses this to determine which direction .goLeft() and .goRight() should navigate.
toc
object[]
Table of contents as a flat or nested array. Each item has the following shape:
pageList
object[]
Same structure as .toc, but represents the page list — the logical page numbers corresponding to a print edition.
metadata
object
Metadata about the book. Follows the schema of Readium’s webpub manifest. String fields like title, author, and language can be plain strings or localized objects:
// Plain string
metadata.title = "Kusamakura"

// Localized object — keys are BCP 47 language tags
metadata.title = { ja: "草枕", en: "Kusamakura" }

// Authors and contributors can be strings, objects, or arrays
metadata.author = "Natsume Soseki"
metadata.author = [{ name: "Natsume Soseki" }]
rendition
object
Corresponds to EPUB rendition properties. The most important property is .layout:

Resolution methods

resolveHref(href)
(href: string) => { index: number, anchor: (doc: Document) => Element | Range | null }
Given an href string (e.g., from a hyperlink inside the book content), returns an object describing the destination:
  • .index — the index of the target section in .sections
  • .anchor(doc) — given the loaded Document, returns the target Element or Range, or null if not found
resolveCFI(cfi)
(cfi: string) => { index: number, anchor: (doc: Document) => Element | Range | null }
Same return shape as .resolveHref(), but accepts an EPUB CFI string instead of an href.
isExternal(href)
(href: string) => boolean
Returns true if the given href should be opened in an external browser rather than navigated to inside the reader. The view element uses this to decide whether to fire an external-link event instead of calling .goTo().

Progress tracking methods

The following methods are consumed by progress.js to track which TOC item corresponds to the current reading position. You only need to implement them if you want TOC and page-list progress reporting.
splitTOCHref(href)
(href: string) => Promise<[sectionId, fragment]> | [sectionId, fragment]
Given an href from the TOC, splits it into two parts:
  1. The section identifier (matching .id on a section in .sections)
  2. A fragment identifier used to locate the specific node within that section
May be async.
getTOCFragment(doc, id)
(doc: Document, id: any) => Node
Given a loaded Document and the fragment identifier returned by .splitTOCHref(), returns the Node that the TOC item points to. Used to determine which TOC item is active based on the current scroll position.

Content transformation

transformTarget
EventTarget
If present, an EventTarget that fires "data" events as the book’s resources are loaded. Event handlers can intercept and transform content before it is rendered.
book.transformTarget.addEventListener('data', ({ detail }) => {
  // detail.data  — string, Blob, or Promise of either
  // detail.type  — MIME type string (e.g., "text/css", "text/html")
  // detail.name  — resource identifier

  // Mutate detail.data to transform the content
  if (detail.type === 'text/css') {
    detail.data = Promise.resolve(detail.data).then(css =>
      css.replace(/font-family:[^;]+/g, 'font-family: inherit')
    )
  }
})
The paginator uses this internally to strip -epub- prefixes and replace vw/vh units.

Implementing a custom format

Because the book object is a plain JavaScript object (not a class instance), you can implement it yourself for any content source:
const book = {
  sections: [
    {
      id: 'chapter-1',
      size: 12400,
      async load() {
        const html = await fetch('/chapters/1.html').then(r => r.text())
        const blob = new Blob([html], { type: 'text/html' })
        return URL.createObjectURL(blob)
      },
      unload() {
        // URL.revokeObjectURL(url) if you stored the URL
      },
      async createDocument() {
        const html = await fetch('/chapters/1.html').then(r => r.text())
        return new DOMParser().parseFromString(html, 'text/html')
      },
    },
  ],
  metadata: {
    title: 'My Custom Book',
    author: 'Author Name',
  },
  toc: [
    { label: 'Chapter 1', href: 'chapter-1' },
  ],
}

import './foliate-js/view.js'
const view = document.createElement('foliate-view')
document.body.append(view)
await view.open(book)
Only .sections and .load() are strictly required. All other properties are optional and unlock additional features like progress tracking, TOC navigation, and CFI-based bookmarks.

The archived files loader interface

EPUB and CBZ formats are ZIP-based and need an external loader object to handle decompression. Both epub.js and comic-book.js expect a loader argument that implements this interface:
entries
object[]
Array of archive entry objects, each with a .filename string property containing the full path within the archive. Used by comic-book.js to enumerate images.
loadText(filename)
(filename: string) => Promise<string> | string
Given the full path within the archive, returns the file contents as a string. May be async.
loadBlob(filename)
(filename: string) => Promise<Blob> | Blob
Given the full path, returns the file as a Blob object. May be async.
getSize(filename)
(filename: string) => number
Returns the uncompressed byte size of the file. Used to populate the .size property on section objects.
view.js implements this using zip.js, which supports random access for File objects and HTTP range requests. The same loader interface also works for unarchived (directory-based) EPUBs — view.js includes a makeDirectoryLoader that satisfies it using the File System Access API.
// Example: a minimal in-memory loader for testing
const loader = {
  entries: [{ filename: 'content.opf' }, { filename: 'chapter1.html' }],
  loadText: async filename => {
    const res = await fetch(`/unpacked-epub/${filename}`)
    return res.text()
  },
  loadBlob: async filename => {
    const res = await fetch(`/unpacked-epub/${filename}`)
    return res.blob()
  },
  getSize: filename => fileSizeMap.get(filename) ?? 0,
}

import { EPUB } from './foliate-js/epub.js'
const book = await new EPUB(loader).init()

Build docs developers (and LLMs) love