Rev
Niyyah is a faith companion app for Muslims. I worked on the React Native app, mostly around the AI chat experience, Quran reading, streaks, and the kind of Android polish that usually only gets noticed when it breaks.
The project was not hard because each feature was impossible on its own. It was hard because everything had to feel calm and reliable in a place where users are asking sensitive questions, reading Quran, and building a daily habit. That changed how I thought about the details. A loading state that feels fine in another app can feel broken here. A swipe direction that is slightly off can make the Quran reader feel wrong. A keyboard bug on one Android version can make the whole chat feel cheap.

What I built

Ask Anything, the AI chat

This was the main AI surface in the app. A user asks a question about Islam, then gets an answer inside a chat UI.
The important decision was to stream the response instead of waiting for the full answer. It makes the feature feel faster because the user sees something happening right away. I also added a stop button, suggested replies, retryable error bubbles, and handling for when the app goes to the background while an answer is still streaming.
The annoying part was markdown rendering. The answer is changing token by token, and our custom markdown renderer sometimes blinked because the component kept rerendering while the stream was still coming in. I spent a lot of time trying to reduce that flicker. It got better than before, but I would not call it fully solved.
My takeaway from that work is that streaming rich text in React Native needs a more native-minded architecture. JavaScript can get pretty far, but for content that changes on every token, a custom JS renderer alone starts to show its limits.
I wasn't the only one running into this, and I kept an eye on how others approached it. Around the same time, Fernando Rojo wrote about how Vercel built the v0 iOS app, where fast markdown and smooth streaming were core requirements: streamed text is split into words that fade in a few at a time. He went deeper on the app with Callstack engineers on React Universe On Air. Later, Software Mansion released react-native-enriched-markdown and React Native Streamdown, which render markdown natively and parse the incomplete stream on a worklet thread so the JS thread stays free. That's close to the architecture I was reaching for, and it's what I'd build on if I did this again.
Ask Anything streaming a reply: thinking, a skeleton while the Quran card loads, then the finished card.
Ask Anything streaming a reply: thinking, a skeleton while the Quran card loads, then the finished card.
Screen recording of the same reply streaming in.

QuranChat v2

QuranChat was the harder piece. The reader had to follow the way the Quran is actually read, which meant right-to-left page navigation and page behavior that feels natural for Quran content, not just a generic carousel.
I worked on the page swipe behavior, preloading the next pages so navigation did not stutter, saving the user’s reading position, and wiring the surah list so users could jump around without losing their place. There was also a chat layer around it, so users could ask follow-up questions from the reading flow.
This is the kind of work where the details matter a lot. If the direction is wrong, or the next page takes a beat too long, the whole thing feels off.
When QuranChat v2 shipped, I got positive feedback from stakeholders and general users. That meant a lot. It was one of those moments where the code did not just feel like tickets moving across a board. It was something many people could actually benefit from.
The reader also became the entry point for the AI. Every verse has an Ask button that opens a conversation about that verse, so users can ask about what they're reading without leaving the reader. Answers draw on sources like Tafsir Ibn Kathir.
Before: one verse at a time on a card. After: the full Quran reader.
Before: one verse at a time on a card. After: the full Quran reader.
Before: a generic Ask a question button. After: Ask about verse 2:110, answered with Tafsir Ibn Kathir.
Before: a generic Ask a question button. After: Ask about verse 2:110, answered with Tafsir Ibn Kathir.
QuranChat v1 and v2 side by side.
Tapping Ask on a verse opens a conversation about it.

Streaks

The streak work was more product-facing, but still pretty fun to build. I added the home calendar, streak indicators across the app, and the small reward moments when a user keeps the habit going.
It was not a huge technical system compared to the AI work, but it needed to feel good. The confetti, haptics, and calendar state all had to line up so the moment felt intentional instead of random.
Small feature, lots of tiny timing details.

Android polish and performance

This was the least flashy part and probably the most realistic mobile engineering part.
I spent a lot of time fixing Android-specific behavior: keyboards resizing screens differently across versions, Android 15 navigation bar safe areas, layout animation bugs, and screens that felt fine on one device but janky on another. I also worked on preloading tabs, caching data, and virtualizing heavier lists so the app opened and moved faster.
None of this makes for a great screenshot. But if it is missing, people feel it immediately.

What I learned

The hardest part was learning a new stack while still shipping inside an older codebase used by 150k+ users.
I was getting better at React Native, Expo, Realm, streaming UI, Android behavior, and all the mobile-specific stuff at the same time I was working in a codebase that already had its own patterns, history, and messy edges. That was uncomfortable in a useful way. I could not just build the clean version in my head. I had to understand what was already there, avoid breaking it, and still move the product forward.
It was also my first time seeing what AI inference and product architecture look like at this scale. Not just a small AI demo, but a real app where streaming, state, storage, analytics, feature flags, and fallbacks all have to work together. That changed how I think about AI features. The model call is only one part of it. The harder part is usually everything around it.
The AI chat and streak animation are the parts people notice. But the part that makes the app feel trustworthy is usually something smaller: the keyboard not covering the input, the page not jumping when Android changes an inset, the next Quran page being ready before the user swipes.
I remember spending way too much time on Android keyboard behavior. It looked like a small layout issue at first. Then every Android version had a slightly different opinion about resizing, safe areas, and keyboard height. Annoying, but useful. It taught me that platform bugs are real product work, not cleanup after the “real” feature is done.