Quran metadata for JavaScript and TypeScript: surahs, ayahs, juz, hizb, rub' al-hizb, thumun al-hizb, mushaf pages, rukus, manzils and sajdas for eight riwayas, with conversion between their ayah numberings.
| Project | |
| Package | |
| Build & tests | |
| Usage | |
| Scores | |
| Size |
Live demo · Surah table · Playground · API reference · Changelog
- Eight riwayas: Hafs, Shuba, Warsh, Qalun, Douri, Sousi, Bazzi and Qunbul, each with its own tree-shakeable entry point.
- Every division of the mushaf: juz, hizb, rub' al-hizb, thumun al-hizb (Qalun), pages, rukus, manzils and sajdas.
- Riwaya conversion: find the ayah in Warsh that holds the text of Hafs 2:255, and the other way round.
- Iteration: walk every ayah of a page, juz, ruku or any other part.
- Parsing and formatting:
"2:255"and"1:1-7"to tuples and back. - Your own data: swap in a different page layout (Indo-Pak, 13/15/16 line mushafs) with
customizeRiwaya. - Surah names in 22 languages: English and Arabic in the main entry, the rest in
quran-meta/i18n, bundled one language at a time. - Typed to the range:
Surahis1 | 2 | ... | 114,Juzis1 ... 30, with type guards and assertion functions. - Zero dependencies, ESM, works in browsers, Node, Deno and Bun.
npm i quran-meta # or: pnpm add quran-meta / yarn add quran-meta / bun add quran-metaThe package is ESM-only and needs Node 22 or newer. On Node 22.12+ require("quran-meta") also works, since Node can require() ES modules.
For CommonJS on older Node versions, stay on v6: npm i quran-meta@6 installs 6.1.1, which has a CommonJS build of its main entry, so const { quran, createWarsh } = require("quran-meta") works there. Its riwaya subpaths (quran-meta/hafs, ...) are ESM-only, so use createWarsh() and friends from the main entry instead.
In the browser without a bundler:
<!-- ES module -->
<script type="module">
import { quran } from "https://cdn.jsdelivr.net/npm/quran-meta/+esm"
console.log(quran.getSurahMeta(2).ayahCount) // 286
</script>
<!-- Classic script, exposes window.quranMeta -->
<script src="https://cdn.jsdelivr.net/npm/quran-meta"></script>
<script>
console.log(quranMeta.meta.numAyahs) // 6236
</script>
<!-- Optional: surah names in every language, exposes window.quranMetaI18n -->
<script src="https://cdn.jsdelivr.net/npm/quran-meta/dist/quran-meta-i18n.iife.js"></script>
<script>
console.log(quranMetaI18n.getSurahName(2, quranMetaI18n.surahNamesFr)) // ["Al Baqarah", "La vache"]
</script>Import from the entry point of the riwaya you need. Functions come with that riwaya's data already bound, and only that riwaya ends up in your bundle.
import { findJuz, findPage, getAyahMeta, getSurahMeta, meta, nextAyah, quran } from "quran-meta/hafs"
meta.numAyahs // 6236
findJuz(2, 142) // 2
findPage(2, 255) // 42
getSurahMeta(2).ayahCount // 286
getAyahMeta(262) // { surah: 2, ayah: 255, juz: 3, page: 42, hizbId: 5, rubAlHizbId: 17, ruku: 35, isSajdahAyah: false, ... }
nextAyah(1, 7) // [2, 1]
nextAyah(114, 6) // [1, 1], wraps around by default
nextAyah(114, 6, { wrap: false }) // undefined
// The same API as a class
quran.getSurahMeta(2).name // "البَقَرَة"The entry points are quran-meta/hafs, /shuba, /warsh, /qalun, /douri, /sousi, /bazzi and /qunbul. Each exports meta, the raw lists (PageList, JuzList, ...), all functions, create<Riwaya>() and a ready-made quran instance.
The root quran-meta entry has the riwaya-agnostic functions, which take the riwaya data as their last argument. Use it when you work with several riwayas at once:
import { QuranRiwaya, findJuz, getListsOfRiwaya } from "quran-meta"
const warsh = getListsOfRiwaya("Warsh")
findJuz(2, 142, warsh)
const qalun = QuranRiwaya.create(getListsOfRiwaya("Qalun"))
qalun.findThumunAlHizb(2, 1) // thumun al-hizb is Qalun-only, other riwayas get a type error| Riwaya | Narrated from (qari) | Entry point | Ayahs | Sajdas |
|---|---|---|---|---|
| Hafs | 'Asim | quran-meta/hafs |
6236 | 15 |
| Shuba | 'Asim | quran-meta/shuba |
6236 | 15 |
| Warsh | Nafi' | quran-meta/warsh |
6214 | 14 |
| Qalun | Nafi' | quran-meta/qalun |
6214 | 12 |
| Douri | Abu 'Amr | quran-meta/douri |
6217 | 15 |
| Sousi | Abu 'Amr | quran-meta/sousi |
6217 | 15 |
| Bazzi | Ibn Kathir | quran-meta/bazzi |
6221 | 15 |
| Qunbul | Ibn Kathir | quran-meta/qunbul |
6221 | 15 |
All riwayas have surah, juz, hizb, rub' al-hizb, page, ruku, manzil and sajda data. Qalun also has thumun al-hizb (480 eighths). See VisualQuran for background on the riwayas.
Riwayas follow different ayah counting traditions, so the same text can have different numbers. In Hafs الم is ayah 2:1 on its own; in Warsh it opens 2:1 together with what Hafs numbers 2:2. Ayat al-Kursi (Hafs 2:255) is two ayahs in Warsh.
import { convertAyah, convertAyahSpan } from "quran-meta"
convertAyah(2, 255, "Hafs", "Warsh") // [2, 253], the Warsh ayah where Hafs 2:255 starts
convertAyahSpan(2, 255, "Hafs", "Warsh") // [2, [253, 254]], every Warsh ayah sharing its text
convertAyah(1, 1, "Warsh", "Hafs") // [1, 2], Warsh does not count the basmala
// From a riwaya entry point the source riwaya is implied
import { convertAyahTo } from "quran-meta/warsh"
convertAyahTo(2, 253, "Hafs") // [2, 255]The conversion table is generated by aligning the text of each riwaya with the Hafs text (KFQC data, quranpedia for Bazzi and Qunbul) and is checked against the ayah counts of every riwaya. Riwayas from the same qari (Warsh/Qalun, Douri/Sousi, Bazzi/Qunbul, Hafs/Shuba) come out identical, which is a useful cross-check since Warsh and Qalun come from separate source files.
import { ayahsInPage, ayahsInPart, getPartRange } from "quran-meta/hafs"
for (const [surah, ayah] of ayahsInPage(604)) {
// 112:1 ... 114:6
}
const juzAmma = [...ayahsInPart("juz", 30)] // 564 [surah, ayah] pairs
getPartRange("ruku", 1) // [1, 7], first and last ayah idPart types are "surah", "juz", "rubAlHizb", "thumunAlHizb", "page", "manzil" and "ruku".
import { ayahStringSplitter, formatSurahAyah, formatAyahId, surahStringParser } from "quran-meta/hafs"
ayahStringSplitter("2:255") // [2, 255]
ayahStringSplitter("18:1-10") // [18, [1, 10]]
ayahStringSplitter("2:280-290") // throws RangeError, Al-Baqarah has 286 ayahs
formatSurahAyah([18, [1, 10]]) // "18:1-10"
formatAyahId(262) // "2:255"
surahStringParser("36") // 36The main entry has English and Arabic. The other languages are in quran-meta/i18n, one table per language, and your bundle gets only the tables you import:
import { getSurahName, surahNamesAr } from "quran-meta"
import { surahNamesFr } from "quran-meta/i18n"
getSurahName(2) // ["Al-Baqara", "The Cow"]
getSurahName(2, surahNamesAr) // ["البَقَرَة", "البقرة"], with and without tashkeel
getSurahName(1, surahNamesFr) // ["Al Fâtiha", "L'ouverture"]Each entry is [transliteration, translation]. Arabic holds the name with and without tashkeel instead, and Malayalam the Latin transliteration and the name in Malayalam script.
To pick the language at runtime, load it on demand. Each language is its own chunk, and only the one you ask for is downloaded:
import { getSurahName } from "quran-meta"
import { getSurahNamesAsync, languages } from "quran-meta/i18n/async"
languages // ["en", "ar", "az", "ru", "tr", "uz", "kk", "fr", "lt", "tg", "ky", "bs", "bn", "es", "id", "it", "ml", "ms", "nl", "sv", "ur", "zh"]
getSurahName(2, await getSurahNamesAsync("ru")) // ["Аль-Бакара", "Корова"]getSurahNames(lang) and surahNames from quran-meta/i18n also take the language at runtime, but they bundle all 22.
Every number type is a union of its valid values, so Surah is 1 | 2 | ... | 114. Type guards narrow unknown input and assertion functions throw on bad input:
import { checkValidSurahAyah, getAyahCountInSurah, isValidSurah } from "quran-meta/hafs"
function handle(input: unknown) {
if (isValidSurah(input)) {
getAyahCountInSurah(input) // input is Surah here
}
}
const ayah: unknown = 255
checkValidSurahAyah(2, ayah) // throws RangeError unless 2:ayah exists; afterwards ayah is AyahNocustomizeRiwaya starts from a built-in riwaya, replaces some lists and recalculates the counts in meta. Use it for a different mushaf page layout or your own juz or ruku divisions:
import { QuranRiwaya, customizeRiwaya, getListsOfRiwaya } from "quran-meta"
// Boundary lists hold the first ayah id of each part: [0, part1, part2, ..., numAyahs + 1]
const indoPak = customizeRiwaya(getListsOfRiwaya("Hafs"), { PageList: myIndoPakPageList })
const quran = QuranRiwaya.create(indoPak)
quran.meta.numPages // the length of your page list
quran.findPage(2, 255)The result is checked with validateRiwayaData, which you can also call yourself; it reports lists that are out of order, do not start at ayah 1 or do not end after the last ayah.
The full reference is at quran-center.github.io/quran-meta/docs, grouped by topic: class API, surah and ayah, navigation, juz, page, hizb, manzil and ruku, ranges and iteration, parsing and formatting, validation, riwaya conversion, riwaya data and surah names.
- The package is ESM-only.
lib_cjs/andlib_es/are gone. On Node 22.12+require("quran-meta")still works; for CommonJS on older Node versions, stay onquran-meta@6(see Install). The IIFE bundle is nowdist/quran-meta.iife.jsand is minified; the separate.min.jsbuilds are gone (jsDelivr serves minified files on request). - Node 22 or newer is required.
- In the root functional API,
ayahis now a required argument offindJuz,findJuzMetaBySurah,findManzil,findPage,findRubAlHizbandfindThumunAlHizb(it used to default to 1 before thelistsargument). Riwaya entry points and the class keep the default. - The Qalun entry exports
QalunListsinstead ofriwayaLists, like every other entry. QuranRiwayathumun al-hizb methods exist only on Qalun instances at the type level and no longer claim to returnnull.ayahStringSplitternow checks both ends of a range against the surah ("2:280-290"used to pass).languages(and theLangtype) gained Arabic and ten more languages, so code that builds aSurahNamesI18nobject needs entries for them.- Deprecated, still working until the next major:
findPagebyAyahIdis nowfindPageByAyahId, andSurahJuzMeta.leftjuzis nowleftJuz. - Surah names other than English and Arabic,
surahNames,getSurahNames,languagesand theLangandSurahNamesI18ntypes moved fromquran-metatoquran-meta/i18n(languagesis also inquran-meta/i18n/async), so the main entry no longer carries every language. For a classic<script>, they are indist/quran-meta-i18n.iife.js, which exposeswindow.quranMetaI18n.
- Surah: a chapter of the Quran. There are 114.
- Ayah: a verse, numbered within its surah.
- AyahId: the position of an ayah counted from the start of the Quran (1 to 6236 in Hafs), independent of the surah.
- Juz: one of 30 parts of roughly equal length. More
- Hizb: half a juz, 60 in total.
- Rub' al-hizb (maqra): a quarter of a hizb, 240 in total, marked in most mushafs with ۞. More
- Thumun al-hizb: an eighth of a hizb, 480 in total, used in North African mushafs. Qalun is the riwaya with this data here.
- Manzil: one of 7 portions for reading the Quran in a week. More
- Page: a page of the mushaf. The built-in data follows the 604-page Madinah mushaf for each riwaya.
- Sajdah: an ayah of prostration. Hafs has 15.
- Ruku: a thematic section of a surah, marked with ع. Hafs has 556.
- Qira'at and riwayat: the ten recognised readings are each named after a qari, and each is transmitted through riwayat (narrations). Hafs from 'Asim is the most widely used; Warsh and Qalun from Nafi' are common in North and West Africa. The differences are mostly pronunciation and a few word forms, but they also change how ayahs are counted. More
pnpm verify checks the lists against these sources, and pnpm verify:lists cross-checks them against the quranpedia dumps they were generated from:
qcloud-meta.json: AlQuran Cloud API metadatatanzil-data.js: Tanzil.net metadataquran-api.json: Quran API metadatahafsData_v2-0.json,hafs_smart_v8.json: KFGQPC Hafs font dataSousiData_v2-0.json: KFGQPC Sousi font dataDouriData_v2-0.json: KFGQPC Douri font dataQalounData_v2-1.json: KFGQPC Qaloun font datashubaData_v2-0.json: KFGQPC Shuba font datawarshData_v2-1.json: KFGQPC Warsh font datadata/quranpedia/*.json: quranpedia.net dumps for all eight riwayas, the source of the generated lists insrc/lists/
KFQC publishes no data for Bazzi and Qunbul, so their lists are checked against each other (both are from Ibn Kathir and must match). Known differences with the KFQC data (a few juz boundaries in Douri and Sousi, some page breaks) are listed in the check scripts under examples/data-check/.
Surah names in Bengali, Chinese, Dutch, Indonesian, Italian, Malay, Malayalam, Spanish, Swedish and Urdu come from the Quran.com API and are generated by examples/data-check/generate-surah-names.ts, which also compares them with the API when run with --check. Quran.com has no transliterations in these languages, so their first element is the Latin transliteration.
pnpm install
pnpm check # format, lint, types, generated files and tests, the same as CI
pnpm dev # tests in watch mode
pnpm build # dist/ via tsdown
pnpm generate # regenerate the riwaya entry points and the conversion table
pnpm docs:site # build the landing page and API reference into _site/
pnpm verify # check the data against external sourcesRiwaya entry points (src/hafs.ts, ...) are generated by scripts/generate-entries.ts and the conversion table by examples/data-check/generate-ayah-map.ts; CI fails if either is out of date. A pre-commit hook (installed by pnpm install) runs formatting, lint, type and test checks.
- Koran Center: a web application for reading and studying the Quran.
- Visual Quran: interactive visualisations of the structure of the Quran.
- Live demo: look up any ayah in any riwaya and see it converted to the others.
- Surah table: all 114 surahs with their metadata in any riwaya, with surah names in any of the supported languages.
- Playground: a Vue app built on quran-meta that you can edit in the browser and see the result as you type, nothing to install.
- Quran Meta Visualiser ESM Alpine 3: interactive charts of the Quran's structure with Alpine.js 3 and Chart.js 4.
- Quran Meta Visualiser ES module version: the Alpine.js 2 and Chart.js 2 version.
- Tanzil.net
- Quran.com JS API, Quran.com API: source of the surah names in Bengali, Chinese, Dutch, Indonesian, Italian, Malay, Malayalam, Spanish, Swedish and Urdu
- AlQuran Cloud
- KFQC data
- Quranpedia
MIT
