quran-meta
    Preparing search index...

    quran-meta

    Quran Meta

    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 Static Badge Documentation Live demo Playground GitHub License GitHub top language
    Package NPM Version npmx GitHub package.json version NPM Type Definitions Node version Module type
    Build & tests CI Docs site Quality Gate Status Snyk Security Score
    Usage NPM Downloads jsDelivr hits GitHub Repo stars npms.io popularity
    Scores npms.io final npms.io quality npms.io maintenance GitHub last commit GitHub issues
    Size npm bundle size npm bundle size (gzip) GitHub repo size GitHub code size in bytes

    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: Surah is 1 | 2 | ... | 114, Juz is 1 ... 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-meta
    

    The 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 id

    Part 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") // 36

    The 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 AyahNo

    customizeRiwaya 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/ and lib_es/ are gone. On Node 22.12+ require("quran-meta") still works; for CommonJS on older Node versions, stay on quran-meta@6 (see Install). The IIFE bundle is now dist/quran-meta.iife.js and is minified; the separate .min.js builds are gone (jsDelivr serves minified files on request).
    • Node 22 or newer is required.
    • In the root functional API, ayah is now a required argument of findJuz, findJuzMetaBySurah, findManzil, findPage, findRubAlHizb and findThumunAlHizb (it used to default to 1 before the lists argument). Riwaya entry points and the class keep the default.
    • The Qalun entry exports QalunLists instead of riwayaLists, like every other entry.
    • QuranRiwaya thumun al-hizb methods exist only on Qalun instances at the type level and no longer claim to return null.
    • ayahStringSplitter now checks both ends of a range against the surah ("2:280-290" used to pass).
    • languages (and the Lang type) gained Arabic and ten more languages, so code that builds a SurahNamesI18n object needs entries for them.
    • Deprecated, still working until the next major: findPagebyAyahId is now findPageByAyahId, and SurahJuzMeta.leftjuz is now leftJuz.
    • Surah names other than English and Arabic, surahNames, getSurahNames, languages and the Lang and SurahNamesI18n types moved from quran-meta to quran-meta/i18n (languages is also in quran-meta/i18n/async), so the main entry no longer carries every language. For a classic <script>, they are in dist/quran-meta-i18n.iife.js, which exposes window.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:

    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 sources

    Riwaya 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.

    Demo image

    MIT