Display Qur'an text
Read by surah and ayah, and render what you read the way the printed muṣḥaf does: in its own font, right to left, with the pause marks and the ayah numbers in place. The whole thing is one import and about twenty lines. For the printed page as artwork, see Display a complete Muṣḥaf page.
By the end, al-Fātiḥa is on the page as seven separately addressable ayahs, set in the King Fahd Complex Ḥafṣ font, reading right to left, with each ayah's number drawn the way the muṣḥaf draws it.
Install, and load Ḥafṣ
The JavaScript package is named
quran-text, the name in itspackage.json. Ḥafṣ, the riwayah most of the world prints, is bundled with it, so the first call needs no file and no network.npm i quran-textimport { Mushaf } from "quran-text"; const m = await Mushaf.hafs(); m.key; // "hafs" m.ayahCount; // 6236 — read it, never write it m.pageCount; // 604 m.wordCount; // 77432Publishing is decided, not yet doneAt the commit this guide was read against,
quran-textis not on npm. Until it is, install from the repository’slib/jsdirectory — the manifest above is the one that will be published, so nothing in the code changes.Read one ayah
m.ayah(surah, n)takes a reference and returns the ayah as a span of words.nis this edition's own number; see the trap at the end of this step.const a = m.ayah(1, 4); a.key; // "1:4" a.text; // "مَٰلِكِ يَوۡمِ ٱلدِّينِ" — the words, joined by spaces, no marks a.words; // ["مَٰلِكِ", "يَوۡمِ", "ٱلدِّينِ"] a.render({ marks: true, ayahMarks: true }); // "مَٰلِكِ يَوۡمِ ٱلدِّينِ ٤" — with pause marks and the ayah numberمَٰلِكِ يَوۡمِ ٱلدِّينِ ٤1:4 · Ḥafṣ · a.render({ marks: true, ayahMarks: true }) textandrender()are different strings.textis the words and nothing else.render({ marks: true })adds the waqf marks the muṣḥaf prints above the line, andayahMarks: trueappends the end-of-ayah sign with its number in Arabic-Indic digits — the٤above. For a reading view you want the rendered form; for search, matching or storage you wanttext.Out-of-range numbers throw, and the message says what the real count is:
m.ayah(1, 8); // RangeError: Al-Fātiḥah has 7 āyāt in Ḥafṣ, not 8 m.ayah(2, 287); // RangeError: Al-Baqarah has 286 āyāt in Ḥafṣ, not 287An ayah number without its riwayah is not a referenceThe 255th ayah of al-Baqarah in Ḥafṣ is āyat al-Kursī. The 255th ayah of al-Baqarah in the Warsh edition is a different passage, because Warsh divides the surah into 285 ayat, not 286, and the boundaries before it fall elsewhere.
warsh.ayah(2, 255)returns real Qur’anic text, just not the text you meant. Every number you accept from a user or a database belongs to one riwayah; Support multiple riwayat covers carrying that fact around.Set the font — this is not optional
Each muṣḥaf file names the font its text was set in, and the Ḥafṣ package bundles the file.
m.fontFace()returns a ready@font-facerule;m.font.familyis the name to use in CSS.m.font.family; // "KFGQPC HAFS Uthmanic Script" m.font.file; // "UthmanicHafs-v-3.0.ttf" m.fontFace(); // '@font-face { font-family: "KFGQPC HAFS Uthmanic Script"; src: url("…/data/UthmanicHafs-v-3.0.ttf") format("truetype"); }'The URL inside
fontFace()is resolved relative to the module, so a bundler that copies the package'sdata/directory serves it as-is. If yours does not, host the.ttfyourself and pass its URL:m.fontFace("/fonts/UthmanicHafs-v-3.0.ttf").Blank or boxed text is a font problem, not missing dataQur’anic text uses code points that ordinary Arabic fonts do not draw — small signs above and below the line, and for Warsh, Qālūn and al-Sūsī a block of letters Unicode only added in 2021. In the wrong font those render as empty boxes or as nothing at all, while
text.lengthand a code-point dump show the string is intact. Ship the font the file names: one per edition, not one for all seven.One detail:
fontFace(url)always writesformat("truetype"), whatever URL you pass. Point it at a.ttf. If you convert the font to WOFF2 for the web, write the@font-facerule yourself withformat("woff2")and take only the family name fromm.font.family.Direction and language on the container
Arabic runs right to left, and the browser needs to be told twice:
dirfor layout,langfor font selection and hyphenation. Put both on the element that holds the text, and keep numbers and Latin labels outside it or in their own isolated element.<div id="quran" dir="rtl" lang="ar"></div>#quran { font-family: "KFGQPC HAFS Uthmanic Script"; font-size: 28px; line-height: 2; /* the marks above and below the line need room */ unicode-bidi: isolate; /* so a "1:4" label beside it cannot reorder it */ }A ḥarakah is a separate code point that renders on the letter before it, so never truncate an ayah at a character index for a preview, and never split one for a two-column layout. Cut between words (
a.wordsgives you them) or show the whole ayah.Render a whole surah, one element per ayah
A surah is a span too, and it hands you its ayahs in order. Give each one its own element with the reference on it, and you have something that can be highlighted, linked and scrolled to later without any string matching.
const root = document.querySelector("#quran"); for (const ayah of m.surah(112).ayahs) { const el = document.createElement("span"); el.dataset.key = ayah.key; // "112:1" el.textContent = ayah.render({ marks: true, ayahMarks: true }) + " "; root.append(el); } // قُلۡ هُوَ ٱللَّهُ أَحَدٌ ١ ٱللَّهُ ٱلصَّمَدُ ٢ لَمۡ يَلِدۡ وَلَمۡ يُولَدۡ ٣ وَلَمۡ يَكُن لَّهُۥ كُفُوًا أَحَدُۢ ٤If instead you want the printed page's own line breaks,
m.page(n).render({ lines: true })returns one line per printed line — page 1 of Ḥafṣ is seven lines. Lines are reconstructed, not read from the source, so treat them as a layout aid rather than a citable fact.Draw the basmalah yourself
In the Ḥafṣ file the basmalah is in
wordsonly where it is numbered — as ayah 1:1. For surahs 2 to 114 it is not in the array:m.words.lengthequalsm.wordCount, 77,432, andm.surah(2).wordsbegins atالٓمٓ. The file tells you whether to print one and leaves the printing to you.m.surah(2).hasBasmalah; // true — draw it above 2:1 m.surah(9).hasBasmalah; // false — never draw one here m.ayah(1, 1).text; // "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ" — the text to drawIn the editions that do not count it (Warsh, Qālūn, al-Dūrī, al-Sūsī) the four words sit before 1:1 as
m.surah(1).basmalah, an unnumbered span, and 1:1 is al-ḥamdu lillāh. Either way the rule is the same:hasBasmalahdecides, and surah 9 is alwaysfalse.Put it together
The complete module. It renders al-Fātiḥa into
#quranwith the font, the direction and the per-ayah elements from the steps above.import { Mushaf } from "quran-text"; const m = await Mushaf.hafs(); // The font, once. const style = document.createElement("style"); style.textContent = m.fontFace(); document.head.append(style); // The container: direction and language belong on it, not on the page. const root = document.querySelector("#quran"); root.dir = "rtl"; root.lang = "ar"; root.style.fontFamily = `"${m.font.family}"`; // One element per ayah, keyed by reference. const surah = m.surah(1); for (const ayah of surah.ayahs) { const el = document.createElement("span"); el.dataset.key = ayah.key; el.textContent = ayah.render({ marks: true, ayahMarks: true }) + " "; root.append(el); }Seven spans,
data-key="1:1"to"1:7", and the text is the package's own.
Look any ayah up by reference and see its text and word numbers, in any of the seven riwayat, from the real dataset.
Loading another riwayah
Mushaf.hafs() is the only edition that ships inside the package. The other six are files in the
dataset's data/mushaf/ directory, 1.9–3.1 MB each, and load with Mushaf.fromJson in a browser or
Mushaf.load in Node:
const warsh = Mushaf.fromJson(await (await fetch("/data/mushaf/warsh.json")).json());
warsh.ayahCount; // 6214 — not 6236
warsh.font.family; // "KFGQPC Warsh Uthmanic Script" — a different font
warsh.fontFace("/fonts/UthmanicWarsh-v-3.0.ttf");
Everything that changes with the riwayah (the count, the font, the boundaries, the spelling) is in Support multiple riwayat.
Related guides
| You want | Use instead |
|---|---|
| The printed page, exactly as typeset | Display a complete Muṣḥaf page |
| Colour by recitation rule | Add Tajwīd highlighting — its offsets are not into this text |
| A tappable word on a printed page | Make words clickable |
| Ḥafṣ numbers translated for another reading | Map ayah references between counts |
How this data is made
The text is extracted unedited from the packages the King Fahd Glorious Qurʾān Printing Complex publishes for each edition; every file names its source package and that package's SHA-256 in its provenance block, and the build fails if a letter moves. Word boundaries and the shared word numbering are derived, and checked by round-tripping every letter of every edition.