Work offline

A Qur’an app is expected to work on a plane. This page is the sizes of what each block ships, so you can decide what goes in the bundle, what is fetched once and cached, and what never belongs on a phone at all.

Platforms
Mobile · desktop · PWA
Time
About 8 minutes

The rule of thumb

One edition of the text plus its font is about 3.4 MB and answers every question about the words. A rendered page is 38 KB in the engine, 196–613 KB as SVG. Ship the text; ship pages by demand; never ship the 50 MB word index.

What one file weighs

Measured from the copies this site serves:

BlockFileWhat it isSize
Quran Textfonts/hafs.woff2the Ḥafṣ font, converted to woff2 for the web (the TTF the dataset ships is 301,396 bytes)105.1 KB
Quran Textfonts/warsh.woff2the Warsh font — a second riwayah means a second font86.7 KB
Quran SVGsvg/001.svgpage 1, glyph outlines plus the ayah hit-layer, uncompressed191.7 KB
Quran SVG Elementspages/001.svgpage 1 split into words and marks, uncompressed219.6 KB
Quran Engine001.qvppage 1 as the engine's binary37.4 KB
Quran Engine001.words.jsonpage 1's text sidecar, five forms per word4.2 KB
Quran Engineqvp_ffi.wasmthe engine itself, built from source for this site304.2 KB
Quran Assetsassets-surah-headers-mushaf-qalon-color.svgone scan-traced surah header, full colour26.2 KB
Quran Assetsassets-ayah-markers-font-001-regular-color.svgone font-derived ayah marker, full colour10.2 KB

The rest, from the source repositories at the commits pinned in public/demo/SOURCES.json, on 11 September 2026:

BlockArtefactBytesNotes
Quran Textdata/mushaf/hafs.json3,110,420one edition; the npm package bundles the same data minified at 2,529,558
data/mushaf/warsh.json1,912,438the seven range from 1.9 to 3.1 MB
data/mushaf/hafs.nested.json.gz1,756,658the same edition as surah → ayah → words, gzipped
data/fonts/UthmanicHafs-v-3.0.ttf301,396one font per edition; 247–301 KB each as TTF
data/ayah-map.json2,285,199converts references between the seven editions without loading them
data/word-index.json50,335,755servers and build steps — the repository says so
Quran SVGhafs/kfqc/svg/042.svg613,372a full page; page 001 is 196,304, page 300 is 591,875
hafs/kfqc/svg-br/042.svg.br106,450the same page Brotli-compressed — ship these and serve Content-Encoding: br
all of hafs/kfqc/svg-br/74,487,813722 files: 604 pages plus surah-specific variants; uncompressed 432,448,834
Quran SVG Elementsquran-svg-hafs-kfgqpc.tar.gz107,920,115the only release artefact: the whole corpus, to obtain one page
Quran Enginequran-engine-pages-hafs-kfgqpc.tar.gz44,093,442604 .qvp pages, sidecars and atlas; 92.5 MB unpacked, 38.9 MB under Brotli per the README
atlas.qva≈10 KBwhich page an ayah is on, surah metadata, juz and hizb — the README’s figure
Quran Tajweedtajweed-annotations-uthmani-hafs-v0.4.0.json1,806,369every span for the whole muṣḥaf, as positions
tajweed-rules-v0.4.0.json96,939the rule corpus
Qiraat Ayah Mapdata/counting-systems.json2,878the six systems; the boundary primitives are 51,714

Decide what goes where

Ship inside the appFetch once, cache by digestNever on a device
The default edition and its font (Ḥafṣ: 3.1 MB + 301 KB)Other editions the user turns on, each with its own fontword-index.json — 50 MB; use it at build time to precompute what you need
ayah-map.json if bookmarks must resolve across editions offlineEngine pages, by page or by juz, as the reader movesThe 108 MB Elements tarball — split it on a server and serve pages
The engine itself — 280 KB of wasm, or the native libraryTajwīd annotations, once, tied to the edition digest they were computed forUncompressed SVG pages — ship the .br copies
A second riwayah is a second font

Warsh, Qālūn and al-Sūsī use code points that almost no general font draws. Cache warsh.json without UthmanicWarsh-v-3.0.ttf and the download succeeds, the digest matches, and the words render as boxes. Each muṣḥaf file names its font in its own font block; fetch the two together.

  1. Choose the unit of download

    Quran Text has no per-surah file: a surah, a page and a juz are slices of one array, so the smallest download for one ayah is the whole edition, and at 1.9–3.1 MB that is the right unit. For rendered pages the unit is one page (NNN.qvp in the engine, NNN.svg.br for Quran SVG) and a juz is a sensible batch.

  2. Verify every download against its digest

    Each block publishes the digest differently, and the check is one hash either way:

    BlockWhere the digest is
    Quran Textdata/manifest.jsonfiles[].sha256 for every emitted file
    Quran Enginequran-engine-pages-hafs-kfgqpc.tar.gz.sha256 beside the release asset
    Quran SVG ElementsCHECKSUMS.txt inside the bundle — sha256sum -c CHECKSUMS.txt
    Quran Tajweededition.sha256 inside the annotations file names the text the spans belong to
    // After fetching data/mushaf/warsh.json into `bytes`, against data/manifest.json
    const expected = manifest.files.find((f) => f.path === "data/mushaf/warsh.json").sha256;
    const hash = await crypto.subtle.digest("SHA-256", bytes);
    const actual = [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("");
    if (actual !== expected) throw new Error("warsh.json: digest mismatch — do not cache");
    // expected: 2134d90016ecd26543ca6eaef21427227ec7641d667b0fc14fbb8e4c3f6dbd3a
    

    Cache under the digest, not under the URL — a cache key names everything the value depends on (Engineering, rule 8.1). A file that hashes the same is the same file; a file that hashes differently is a new edition of the text, and everything derived from the old one (search caches, tajwīd offsets, word-level bookmarks) is suspect.

  3. Keep the provenance with the cache

    Store provenance.text.sha256, the provenance of each cached muṣḥaf, beside your own data. It lets a bookmark written against one build be checked against another; see Store bookmarks and progress.

  4. Check for updates, and know what the check does today

    The JavaScript library has an update check built in. It never throws and never rejects: it resolves to null whenever it cannot get an answer.

    const status = await m.checkForUpdate();   // default url: Mushaf.VERSION_URL, 5 s timeout
    // { edition, upToDate, localSource, latestSource, dataset, downloadUrl } — or null
    
    It resolves to null in production right now

    Mushaf.VERSION_URL is https://quran.ws/version, and the result’s downloadUrl is built on https://quran.ws/files/. On 11 September 2026 both paths return an HTML page, not a format: "quran-version" document, so checkForUpdate() returns null for every edition. Treat null as “unknown”, not as “up to date”, and do not build an update flow on this until the endpoint exists. Recorded in docs/dx/quran-text.md.

    Until then, the working update check is the one in step 2: re-fetch data/manifest.json, compare the digest of the file you hold, and download when it differs.

What is not offline

Quran PNG is an HTTP service and has no offline form. Inside an app that already holds engine pages, page.cropSvg() gives the same artwork with no network.

Try it live

Page 001 as a 38 KB engine binary, drawn in your browser — the size in the table above, rendered.