هداية
Prayer times for your location, the Hijri calendar with its occasions, and the full Quran in Arabic, English and Urdu with tafsir and recitation.
quran · prayer-times · salah · namaz · hijri-calendar · tafsir · adhan · qibla · nextjs · pwa · web-push · islamic-app
- Features
- Getting started
- Environment variables
- The prayer alarm
- Accuracy
- Design
- Data sources
- Project structure
- Scripts
- Testing
- Deployment
- Contributing
- Licence
| Feature | Detail | |
|---|---|---|
| 🕌 | Prayer times | Computed on your device from your coordinates. Twelve calculation authorities, both Asr conventions, and correct behaviour at high latitude and inside the polar circle. |
| ⏳ | Live countdown | An engraved dial that fills as the window to the next prayer closes, with the full day timeline beside it. |
| 🔔 | Prayer alarm | Notifies you when a prayer begins, including when the site is closed. See the prayer alarm. |
| 🧭 | Qibla compass | The bearing to the Kaaba, following your device compass where the browser exposes one. |
| 🌙 | Hijri calendar | A month grid in Hijri and Gregorian together, marking Ramadan, both Eids, Ashura, Laylat al Qadr, Arafah, the white days and more. |
| 📖 | The full Quran | All 114 surahs in the Uthmani script, with ten English and eight Urdu translations. |
| 📃 | Two reading modes | Study lays out each ayah with its translation. Mushaf runs the Arabic together as continuous flowing text the way a printed copy reads, with everything else behind a tap. |
| 🔖 | Paras, resume and bookmarks | Browse and read any of the thirty juz end to end across surah boundaries, pick up where you stopped, and bookmark any ayah. |
| 🔍 | Search | Search across the translations and jump straight to the ayah. |
| 📿 | Tasbih | A dhikr counter with the 33/33/34 cycle, istighfar and salawat, counted and kept between visits. |
| ✅ | Prayer tracker | Mark each prayer as prayed, with a streak and a seven day view. |
| 📜 | Tafsir | Per ayah commentary in English and Urdu, including Ibn Kathir and Bayan ul Quran. |
| 🎧 | Recitation | Seven reciters, per ayah playback, continuous recitation, repeat, and the ayah lighting up as it is recited. |
| 🎨 | Three themes | Emerald, Lapis and Ink, each with a real dark and light variant rather than one washed out copy of the other. |
| 📴 | Works offline | Prayer times need no network at all. Quran text is cached once and then served locally. |
Requires Node 20 or newer.
git clone https://github.com/Abudora-0/Hidayah.gitcd Hidayah && npm install && npm run devOpen http://localhost:3000.
Nothing needs configuring. Every environment variable is optional, and without them the app runs in full except that the prayer alarm only fires while the site is open in a tab.
Copy .env.example to .env.local and fill in only what you need. All of
these are for background notifications, which is the one feature that cannot
work from the browser alone.
| Variable | Required for | Notes |
|---|---|---|
NEXT_PUBLIC_VAPID_PUBLIC_KEY |
Web Push | Generate with npm run generate-vapid. Safe to expose. |
VAPID_PRIVATE_KEY |
Web Push | Keep secret. Never commit it. |
VAPID_SUBJECT |
Web Push | A mailto: address that push services can contact. |
UPSTASH_REDIS_REST_URL |
Storing subscriptions | From Upstash. |
UPSTASH_REDIS_REST_TOKEN |
Storing subscriptions | From the same Upstash database. |
QSTASH_URL |
Scheduling notifications | The endpoint for your QStash region. Needed if you chose the US region, since the client defaults to the EU endpoint. |
QSTASH_TOKEN |
Scheduling notifications | From the QStash tab in the Upstash console. |
QSTASH_CURRENT_SIGNING_KEY |
Verifying callbacks | Required, or the delivery route rejects everything. |
QSTASH_NEXT_SIGNING_KEY |
Verifying callbacks | Used during Upstash key rotation. |
Pick one QStash region and take all four values from it. The token and the signing keys differ per region, and mixing them makes every callback fail signature verification, which is silent from the sending side. |
NEXT_PUBLIC_SITE_URL| Scheduling notifications | The public origin, used to build the callback URL. | |CRON_SECRET| Guarding the cron route | You must create this. Vercel does not generate it, it only forwards it once it exists. Without it,/api/cron/scheduleis publicly callable. |
Generate a VAPID key pair with:
npm run generate-vapidA browser can only notify you reliably in the background if the site is installed and push is granted. Hidayah does that properly, and the design is shaped by two hard limits worth knowing about.
Vercel's Hobby plan allows one cron run per day, with up to an hour of drift. The usual "check every minute and notify whoever is due" pattern is not available, and a cron expression that runs more often fails at deploy time.
QStash accepts a delayed message up to seven days out and delivers it at an exact timestamp.
So the cron never delivers a notification. It only enqueues.
Daily cron -> /api/cron/schedule
for each subscriber:
work out their coming prayer times with adhan
hand each one to QStash, pinned to its exact instant
|
QStash calls back at the exact minute
v
/api/push/fire -> Web Push to that subscriber
v
Service worker shows the notification
This is free, stays within the Hobby plan, and is more precise than a minute cron would have been, because the hour of cron drift only affects when the work is queued rather than when it lands.
A few details that matter:
- The cron covers a rolling window slightly longer than a day, so nothing can fall between two runs. Each prayer is claimed in Redis before being queued, so the overlap can never notify anyone twice.
- The delivery route verifies the QStash signature. It is a public URL, and without verification anyone could notify any subscriber at any time.
- Prayer times are computed against each subscriber's own calendar day read through their time zone, not the server's.
- Your coordinates only leave your device if you turn this on. The server needs them to work out prayer times when no browser is running. Turning the setting off deletes them.
Without the Upstash and VAPID variables set, all of this degrades cleanly to an in tab alarm and the app tells you so plainly.
No adhan recording is bundled. Those recordings are someone's work with varying licence terms, and the public endpoints that serve them proved rate limited and unreliable. The default is a short chime synthesised in the browser, which needs no network.
To use a real adhan, drop an audio file at public/adhan.mp3. It is picked up
automatically, with the chime as the fallback.
Prayer times are calculated locally with adhan rather than fetched, so they work offline and are never rate limited. Nothing external is verifying them, so the settings screen has a Check against Aladhan button that compares today's times against the Aladhan API for the same authority and madhab, and reports the difference in minutes.
For Lahore with the Karachi authority and the Hanafi Asr, the two agree to within one minute across all six times, which is rounding.
Hijri dates come from Intl with the Umm al Qura calendar, which ships with
the runtime. They were cross checked against Aladhan on the start of Ramadan,
both Eids and other dates, all matching.
A note on dates. The Umm al Qura calendar is calculated rather than sighted. Your local announcement of Ramadan or Eid may fall a day either side, and the calendar says so on screen rather than implying a precision it does not have.
The mark is a girih tile, the outlined interlace star of Islamic
strapwork. It was chosen because it is line based, so the same geometry runs
through the whole interface rather than sitting in the corner as a logo: the
section rules, the ayah number rosettes, the quarter markers on the countdown
dial, the faint lattice behind the hero panels, and the illuminated unwan
panel that opens each surah.
Colours come from Quranic manuscript illumination rather than the usual palette for this kind of app.
| Theme | Dark | Accent |
|---|---|---|
| Emerald (default) | #06231C |
#D4AF37 |
| Lapis | #0B1A2F |
#C9A227 |
| Ink | #121212 |
#B08D57 |
Typography pairs Amiri Quran for the Uthmani script, Noto Nastaliq Urdu for Urdu with the line height Nastaliq needs, and Reem Kufi for headings and the wordmark.
Every control is themed rather than left as a browser default: the scrollbar,
inputs, the range slider, checkboxes, radios, the focus ring, text selection,
and a custom listbox replacing the native select. All motion is behind
prefers-reduced-motion.
Dropdowns render through one Popover primitive that portals to the body and
positions itself from the trigger's rectangle. That is not incidental. Any
ancestor with a transform creates a stacking context that traps a panel
whatever its z-index, and any ancestor with overflow: hidden clips it, so a
panel rendered in place is at the mercy of its parents. It also flips above the
trigger when there is no room below, instead of running off the bottom of the
screen.
Hidayah is a client for work done by others, and it is worth naming them.
| Source | Used for |
|---|---|
| Aladhan | Independent verification of prayer times and Hijri dates |
| Al Quran Cloud | Quran text and the English and Urdu translations |
| spa5k/tafsir_api | Tafsir editions, served over jsDelivr |
| Islamic Network | Per ayah recitation audio |
| adhan | The prayer time and Qibla calculations |
src/
app/ routes, API handlers, manifest and icons
quran/[surah] the reader, in study or mushaf mode
quran/juz/[juz] a whole para, across surah boundaries
quran/search search the translations
tasbih the dhikr counter
api/cron/schedule the daily enqueue
api/push/fire QStash callback that sends one notification
components/
ornament/ the girih SVG library
prayer/ countdown, timeline, Qibla, alarm
quran/ surah index, reader, audio, tafsir
calendar/ the Hijri month grid
settings/ preferences and the Aladhan check
tasbih/ the dhikr counter
ui/ popover, themed select, toggle, counter, switcher
data/ editions, reciters, occasions, cities
lib/ prayer, hijri, quran, push, theme, settings, reading
scripts/
check-no-emdash.mjs lint rule described below
generate-vapid.mjs Web Push key generation
| Script | Does |
|---|---|
npm run dev |
Development server |
npm run build |
Production build |
npm run start |
Serve the production build |
npm run lint |
ESLint plus the em dash check |
npm run typecheck |
Route type generation and tsc --noEmit |
npm run test |
Vitest |
npm run check:contrast |
Audit colour contrast across all six palettes |
npm run check |
Lint, typecheck, test and contrast together |
npm run generate-vapid |
Print a fresh VAPID key pair |
npm run lint includes scripts/check-no-emdash.mjs, which fails the build if
an em dash appears anywhere in the source tree. The house style is a comma, a
colon or a full stop instead, and enforcing it mechanically means it cannot
quietly regress.
npm run test41 tests covering the parts most likely to break quietly:
- Bismillah handling. The Arabic edition prepends the bismillah to the first ayah of every surah except At Tawbah, which would print it twice given the surah opening already shows it. Stripping is done with diacritics removed, since the vowel marking differs between editions. Al Fatihah keeps it, because there it genuinely is ayah one.
- Hijri conversion, against dates confirmed with Aladhan, plus a round trip of every Hijri month across eleven years.
- The prayer schedule, including the rollover to tomorrow's Fajr after Isha, the window that reaches back to yesterday's Isha before dawn, and the later Hanafi Asr.
One of these tests found a real bug: inside the polar circle adhan returns
NaN for every prayer time unless polarCircleResolution is set.
npm run check:contrastThree themes with a dark and light variant each is six palettes, which is more than anyone can reliably eyeball. This checks every colour pairing the interface actually uses against the WCAG AA thresholds, 4.5 for text and 3.0 for the boundaries of interactive controls, and fails if any pair falls short.
It was worth writing. The failures it found on its first run were not the obvious ones: hint text sat between 3.1 and 4.5 against its background in every single theme, and the one theme whose gold is dark needed light text on gold fills while the other five needed dark. All 66 pairings pass now.
The app is built for Vercel.
- Push the repository to GitHub and import it at vercel.com/new. No build configuration is needed.
- Add the environment variables above in Settings, Environment Variables,
if you want background notifications. Set
NEXT_PUBLIC_SITE_URLto the deployed origin. - The daily cron in
vercel.jsonis picked up automatically. On the Hobby plan it runs once a day, which is all this design needs.
Surah pages are rendered on first request and then cached, rather than prerendered at build time. Prerendering all 114 fires that many parallel requests at a free API, which rate limits and fails the build.
Issues and pull requests are welcome. Before opening a pull request:
npm run checkPlease keep to the two house rules: no em dashes anywhere, and every control stays themed rather than falling back to a browser default.
MIT. Do what you like with it.
The Quran text, translations, tafsir and recitations belong to their respective publishers and reciters and are served from the sources listed above. The MIT licence covers this application, not that material.
رَبِّ زِدْنِي عِلْمًا