Jadwal Sholat — Haikel Ilham HakimSkip to content

Project archive

Jadwal Sholat

Haikel, 3 min read - Preview Source

بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ

I started Jadwal Sholat around 2022 because I was frustrated with the options available. Every prayer-time site I tried had the same issues: slow to load on mobile data, packed with ads, and most of them assumed the user was in Jakarta. I live in Bangka, and the time difference matters. I built the first version as a simple page that pulled prayer times based on the user’s location. That was it.

Then people I shared it with started asking for more. First the Quran. Then Asma’ul Husna. Then daily duas. Each request was reasonable, so I added them. What began as a single-purpose tool grew into something broader. I did not plan the expansion — users shaped it.

The hardest part of this project had nothing to do with features. It was the Next.js migration. I built the initial version on Next.js 12. When Next.js 13 released with the App Router in late 2022, I decided to migrate. What I underestimated was how deeply the existing codebase depended on Pages Router patterns — getServerSideProps, getStaticProps, the _app and _document files. The App Router replaced all of that with React Server Components, a new layout system, and a fundamentally different data-fetching model. It was not a version bump. It was a rewrite of how every route worked.

I spent weeks migrating routes one by one. The location-based prayer time logic had to be rethought because the data-fetching flow changed. The Quran reader relied on client-side state that did not map cleanly to the server-first paradigm. Some pages broke in ways the migration guide did not cover — the PWA manifest stopped resolving, service worker registration needed updating, and several libraries did not yet support React Server Components. At one point I considered rolling back to Pages Router. I did not, because the App Router was clearly where Next.js was heading. But the experience taught me that framework migrations are not about following a guide. They are about understanding every assumption your codebase has made, sometimes without you realizing it.

Performance matters for this app in a specific way. People open it five times every day, often from the same phone they have had for years. I chose PWA over native because most users in Indonesia discover apps through WhatsApp links, not app stores. Installing from the browser takes two taps. There is no APK, no permissions dialog.

Location is geolocation-first. The app requests permission and calculates prayer times for the user’s coordinates. If they decline, it falls back to Bandung — the city where most early users were based. An explicit fallback that is slightly off is better than silently defaulting to Jakarta and being wrong.

The Quran reader took the most effort. Per-ayah audio with pause and resume sounds straightforward, but the external API does not provide ayah-level timestamps, and different qaris recite at different speeds. I had to buffer audio segments and track position manually. The Asma’ul Husna page uses GraphQL — the only route that does — because the data has nested relationships. In hindsight I would not add GraphQL again for what is essentially a read-only reference page, but it is stable.

The adhan feature preloads and caches the audio before the scheduled time. If the file has not downloaded when the time arrives, the adhan simply does not play. That is the failure mode, and preloading is the mitigation. Small thing, but if the adhan does not fire on time, the feature has no purpose.

Screenshots

Tech Stack

  • Next JS
  • Typescript
  • Tailwind CSS + Shadcn/UI
  • React Query
  • Zustand
  • Turborepo
  • Cypress
  • Leaflet
  • Framer Motion