Two phones on a stone surface showing a student learning app with a maths lesson and a personal finance app with a balance chart

Mobile app developer & product builder

Mobile apps, designed and built by one person.

I'm Siddhesh, a freelance mobile developer and designer. I build iOS and Android apps for startups, from the first sketch to the App Store, and I stick around after launch.

iOS & AndroidFlutter · React NativeUS · UK · Europe

In my experience apps rarely fail because of bad code. They fail when the idea, the design and the build are handled by different people who never quite talk to each other. I do all three myself, so nothing gets lost in a handoff.

Approach

You talk to me

The person on the call is the person designing and writing your app. There's no account manager in between.

You own it

The code lives in your repositories with documentation, so you can bring in your own team later.

I stay after launch

OS updates, crash fixes and new features for as long as you want me around.

All case studies

Digital school · 2025

A digital school in your pocket: video lessons, notes, tests and an AI tutor for students, and a separate app that keeps parents in the loop.

Client
Bookvy Education
Platforms
Android · iOS
Timeline
First release in 12 weeks, ongoing since
Year
2025
The Bookvy app on a phone on a Class 10 student's desk, next to maths textbooks and handwritten notes

10,000+

Downloads on Google Play

2 apps

Student and parent, one backend

English + Hindi

Full app in both languages

Bookvy wanted to give school students in India a proper study companion on the phone they already carry: lessons, notes and practice tests in one place, in English or Hindi. Parents were the ones paying, so they also needed a simple way to see whether their child was actually studying and improving, without reading the student's app over their shoulder.

  1. 01

    I split it into two Flutter apps sharing one NestJS + MongoDB backend: a student app built around daily study, and a lighter parent app built around a quick daily check.

  2. 02

    The student app covers the whole loop: subjects and video lessons, a PDF notes library, quizzes and tests, then reports that point out weak topics, time spent and performance trends.

  3. 03

    I added an AI tutor chat for doubts, plus streaks, XP, achievements and a leaderboard so studying every day feels worth it.

  4. 04

    Parents link to their child by scanning a QR code or typing a short code, then get a daily summary, a 0–100 learning health score, strengths, focus areas and reports they can download.

  5. 05

    Payments run through Razorpay and in-app purchase, with paid exam registration that includes an Aadhaar scan and a live photo check.

  6. 06

    Every screen is translated into Hindi, and the app works on the low-cost Android phones most students use.

  1. Discovery

    Sat with the Bookvy team to map how students study after school and what parents actually want to know each day.

  2. Design

    Designed both apps in one system: indigo brand colour, Plus Jakarta Sans, large tap targets and clear progress visuals.

  3. Build

    Flutter with Riverpod and go_router for both apps, a shared NestJS API, Firebase for notifications and analytics.

  4. Launch

    Shipped to Google Play and the App Store with store listings, screenshots and the Hindi translation in place.

  5. After launch

    Still shipping regular releases: new features, analytics-led fixes and support for every new Android version.

Bookvy Parent: a parent checking their child's subject-wise progress from the sofa

Bookvy Parent: a parent checking their child's subject-wise progress from the sofa

Bookvy Parent on three phones: daily summary, learning reports and linking a child by QR code

Bookvy Parent on three phones: daily summary, learning reports and linking a child by QR code

Bookvy Digital School has passed 10,000 downloads on Google Play and is live on the App Store, with the parent app giving families a reason to keep the subscription going. I still look after both apps and the backend.

Scope

Product design, Android app, iOS app, Parent app, Payments, AI tutor, Hindi localisation, Backend API

Stack

Flutter, Riverpod, NestJS, MongoDB, Firebase, Razorpay, In-app purchase

Open the Bookvy case study on its own page

Daily astrology · 2026

The horoscope, but specific to you. One reading every morning: your energy score for the day, your best and riskiest hours, and how you vibe with your friends.

Client
Pulse (PULS-E)
Platforms
Android · iOS
Timeline
MVP in 10 weeks, ongoing since
Year
2026
The Pulse app's morning reading with the daily energy dial, checked in bed

1,000+

Downloads on Google Play

28 seconds

To read the whole day

4 tabs

Home, Squad, Vibe and Me

Horoscope apps are everywhere, and most of them read the same for everyone born in the same month. Pulse wanted readings worked out from each person's actual birth chart, delivered as something you can check in under a minute with your morning chai, and a social side so people come back to compare notes with friends.

  1. 01

    The Home screen answers one question: what's my energy today and what should I do with it? A 0–100 dial, a one-line headline, and the day split into morning, afternoon and evening windows.

  2. 02

    Peak and risk windows show the best hours to focus and the ones to be careful with, and each block loads on its own so the screen is never stuck on a spinner.

  3. 03

    Squad turns it social: add friends, form groups and chat in real time over WebSockets.

  4. 04

    Vibe runs one-on-one and group compatibility checks, and Me holds the personality profile and archetype card.

  5. 05

    The readings come from a Python backend: FastAPI with PostgreSQL, Redis and Celery for the daily jobs, Swiss Ephemeris for the astrology maths, and an AI layer that writes the readings in plain language.

  6. 06

    Subscriptions run through Razorpay and in-app purchase, with push notifications for the morning reading.

  1. Discovery

    Worked out with the founders what a reading should feel like: short, specific and personal, not a wall of text.

  2. Design

    A dark, calm interface with soft pastel accents and a serif display face, so it feels closer to a journal than a fortune-teller.

  3. Build

    React Native app with Expo, Expo Router, React Query and Zustand, a FastAPI backend with scheduled jobs, real-time chat and a React admin panel.

  4. Launch

    Released on Google Play with a landing site, onboarding that collects birth details in under a minute, and push reminders.

  5. After launch

    Iterating on retention: streaks, the daily personality card and new Squad features based on how people use it.

Vibe: an 84% compatibility check between two friends, over cutting chai

Vibe: an 84% compatibility check between two friends, over cutting chai

Pulse on three phones: home with the energy dial, the Squad tab and the personality card

Pulse on three phones: home with the energy dial, the Squad tab and the personality card

Pulse is live on Google Play with over 1,000 downloads, and people open it the way it was meant to be used: a quick look every morning, then back to compare with friends.

Scope

Product design, Android app, iOS app, Real-time chat, Subscriptions, Backend API, Admin panel

Stack

React Native, Expo, FastAPI, PostgreSQL, Redis, Celery, WebSockets, Razorpay

Open the Pulse case study on its own page

Video streaming · 2024

An Indian streaming app for movies, web series and short clips, with offline downloads and subscriptions. Half a million people have installed it.

Client
Kahani Play
Platforms
Android · iOS
Timeline
Launch, then 70+ releases
Year
2024
Watching a series on the Kahani Play app on a phone at home in the evening

500,000+

Downloads on Google Play

70+

Releases shipped

1

Flutter codebase for Android and iPhone

Kahani Play needed a streaming app that holds up for a large Indian audience: smooth playback on cheap phones and patchy mobile data, offline viewing for people who download on Wi-Fi and watch later, and payments that work for everyone, whether they pay by UPI or card. It also had to reach iPhone users without doubling the work.

  1. 01

    I built the app in Flutter, so one codebase runs on both Android and iPhone, with Riverpod for state and Dio for networking.

  2. 02

    Playback streams adaptive HLS and picks the right quality for the phone and the connection, so video starts fast even on patchy mobile data.

  3. 03

    Offline downloads are encrypted and kept in app-private storage, with a native Android layer for background downloads, so people can save episodes on Wi-Fi and watch later.

  4. 04

    The catalogue covers movies, web series with seasons and a feed of short clips, with voice search, watch history and a wishlist.

  5. 05

    Subscriptions run through UPI and Google Pay, SabPaisa and Cashfree on Android, and Apple in-app purchase on iPhone. Sign-in is by phone OTP.

  6. 06

    Firebase handles push notifications, analytics and crash reporting, AppsFlyer and Branch handle attribution and deep links, and in-app updates and reviews are built in.

  7. 07

    Behind it is a NestJS + MongoDB API with a Next.js admin panel for uploading and publishing content.

  1. Discovery

    Mapped the content types, the payment methods the audience actually uses, and the devices and networks to design for.

  2. Design

    A dark, poster-led interface where the content does the selling, with genre chips and rows that are quick to browse.

  3. Build

    Flutter with Riverpod and Dio, adaptive HLS playback, encrypted offline downloads, and payments for both Android and iPhone.

  4. Launch

    Released on Google Play, then tuned playback, start-up time and crash rates using real-world data.

  5. After launch

    More than 70 releases since launch: new formats like short clips, new payment options, an iPhone build, and a rebuilt backend and admin panel.

A Kahani Play series page with seasons and the episode list

A Kahani Play series page with seasons and the episode list

Kahani Play on three phones: home, a series with its episodes, and offline downloads

Kahani Play on three phones: home, a series with its episodes, and offline downloads

Kahani Play has crossed 500,000 downloads on Google Play. More than 70 releases on, it runs on one Flutter codebase for Android and iPhone, with a backend and admin panel that let the team publish content without needing a developer.

Scope

Android app, iOS app, Video streaming, Offline downloads, Subscriptions, Payments, Admin panel, Backend API

Stack

Flutter, Riverpod, Dio, HLS, Firebase, NestJS, MongoDB, Next.js

Open the Kahani Play case study on its own page

Fragrance store · 2026

HEIR sells perfume at ₹4,500 a bottle, and the store could not look like a Shopify theme. So Shopify keeps the products, orders and payments, and I built everything in front of it.

Client
HEIR
Platforms
Headless Shopify · Web
Timeline
Live, with releases since
Year
2026
heirbeauty.com
The HEIR homepage: hands cradling a fluted bottle in the dark, over the line Traces of longing, culture and memory

6

Products in the first collection

4.8★

Judge.me rating on the flagship scent

2

Scheduled jobs keeping Shopify in sync

HEIR is a new perfume house launching its first collection into a market where nobody knows the name yet. A bottle is ₹4,500 and the discovery set is ₹1,200, so the site has to carry the whole argument for the price: how the bottle is made, what the notes are, who composed it. A standard Shopify theme could not do that — the pages HEIR needed are long, dark, slow-moving editorial things, not a product grid with a sidebar. But the client still had to run the shop from the Shopify admin, because the person adding a product and fulfilling an order is not a developer and should never have to open a code editor. So the real problem was splitting the store in two: Shopify keeps products, inventory, orders and checkout, and everything a customer sees is mine. The second problem was accounts. Shopify's own customer accounts come with Shopify's own login screens, and HEIR wanted sign-in to happen on the site, in the site's own type.

  1. 01

    The storefront is Next.js 15 on the App Router, reading Shopify through the Storefront API and writing through the Admin API. Nothing about the catalogue is hardcoded: products, collections, blog articles and the shop's localisation all come from Shopify at request time.

  2. 02

    Every Shopify read is wrapped in Next's use cache with a tag and a lifetime, so product and collection queries are cached for days and localisation for hours. A Shopify webhook hits /api/revalidate, which calls revalidateTag for products or collections — the client edits a price in the admin and the site follows without a deploy.

  3. 03

    Shop filters are driven by a naming convention rather than code. Collections whose handle starts with collection- become the category filters and ones starting with family- become the olfactive families — Ambery Oud, Aromatic Woody, Floral Fruity Musk, Fruity Floral, Oriental Gourmand. Adding a sixth family is a new collection in Shopify, not a release.

  4. 04

    Product pages are assembled from Shopify metafields and metaobjects: notes_accords, perfumer, ingredients, product_details, shipping_information, return_policy. The accordion on a product page is built from whatever metafields exist, with defaults underneath, so a product with no perfumer note renders cleanly instead of leaving a gap.

  5. 05

    Pricing is multi-market. Middleware reads Vercel's x-vercel-ip-country header into a country cookie, the switcher writes a country_override cookie that always wins, and both feed Shopify's @inContext directive. The country list and currencies come from Shopify's Markets settings at runtime, so a new market is a dashboard change.

  6. 06

    Markets cost cache entries, so I collapse them. Prices only vary by currency, and sending all thirty-odd countries to @inContext would multiply the product cache for identical results — pricingCountryFor picks one representative country per currency so they share an entry. The cart still uses the visitor's real country, where shipping and tax genuinely differ.

  7. 07

    Login is email OTP through Supabase, on HEIR's own pages, and the Shopify customer is created behind it. /api/auth/complete-signup searches Shopify by email, falls back to searching by phone when Shopify rejects a duplicate number, then retries without the phone — Shopify treats customer phone numbers as unique and the signup form should not dead-end because of it.

  8. 08

    A user_mappings table in Supabase joins the Supabase auth user to the Shopify customer id, with partial indexes on email, phone and user id filtered to is_active = true. The Shopify webhook keeps it honest: customers/create fills in the Shopify id on a pending row, customers/update syncs name and email, and customers/delete soft-deletes rather than dropping the row, so a deletion in Shopify never silently orphans a login.

  9. 09

    The webhook verifies Shopify's HMAC-SHA256 signature against SHOPIFY_WEBHOOK_SECRET before it parses the body, and Indian phone numbers are normalised to +91 on the way in, because the same number arrives from Shopify in several shapes.

  10. 10

    Reviews are Judge.me, read server-side by product handle and folded into the product page's Product JSON-LD as aggregateRating, but only when there is at least one review — Google drops the whole rich result for an empty rating. ATLAS currently shows 4.8. Customers can upload up to five photos per review; those go to a Supabase storage bucket behind a login check and a rate limit.

  11. 11

    Public endpoints sit behind Upstash Redis sliding windows, tightest on the unauthenticated user-check route, because it is the one an attacker could use to enumerate phone numbers and every attempt costs a database query. If Upstash is not configured the limiters return null and the site still runs, which keeps local development simple.

  12. 12

    Two Vercel cron jobs: /api/reconcile at 02:00 daily counts failed events and pending or failed customer mappings so drift is visible instead of discovered by a customer, and /api/keep-alive on Mondays and Thursdays pings Supabase so the project is not paused for inactivity. The admin monitor page at /account/admin reads the same failed-events table, gated on a single ADMIN_EMAIL match.

  13. 13

    The whole site could be locked pre-launch. Setting SITE_PASSWORD makes middleware redirect everything except the unlock route to /site-locked, so HEIR could share the real domain with photographers and the perfumer before the collection was public. Unsetting it opens the site.

  1. Deciding what Shopify keeps

    I started by drawing the line: Shopify owns products, variants, inventory, orders, payments and blog content, and I own every pixel. That meant accepting Shopify's hosted checkout rather than rebuilding payments, which is the right trade for an Indian store — UPI, cards and the rest arrive maintained, and I do not hold card data. Everything upstream of the checkout button is mine.

  2. The Shopify data model

    Before any UI I worked out how a non-developer would edit the pages. Long-form product content became metafields and metaobjects, the shop and family filters became a collection naming convention, and the homepage video reels became everyday_moments_video metaobjects read through the Storefront API. The test was simple: could the client change the notes on NECTRA without calling me.

  3. Storefront and caching

    I built the Shopify client as one shopifyFetch with typed operations, then put use cache with tags and lifetimes over every read. Products and collections cache for days; the shop page is force-dynamic because it reads the country cookie and filter params per request, which makes any revalidate window a no-op there. Tag-based revalidation from the Shopify webhook is what keeps long cache lives safe.

  4. Accounts without Shopify's login screens

    The auth flow was the slowest part, and most of the time went on failure cases rather than the happy path. Email OTP via Supabase, a Shopify customer created or matched behind it, a mapping row joining the two, and layered fallbacks for the duplicate-phone case. Session state is httpOnly cookies for the Shopify customer id plus a non-httpOnly flag so client components can tell someone is signed in without reading a protected cookie.

  5. Markets and multi-currency

    Once the store sold outside India the single-currency assumptions had to go. Geo detection in middleware, a manual switcher that overrides it, the country list pulled from Shopify Markets rather than a constant in the code, and the per-currency cache collapse so thirty markets do not become thirty copies of the same product.

  6. Reviews, SEO and structured data

    Judge.me went in server-side so ratings are in the HTML rather than loaded by a widget. Product pages emit Product and BreadcrumbList JSON-LD with shipping and return details, prefer the SEO title and description merchandisers set in Shopify, trim descriptions to a clean 160 characters, and carry up to four product images for link previews. The sitemap is generated from live Shopify products, collections, pages and articles.

  7. Launch and handover

    The site ran behind SITE_PASSWORD until the collection was ready. I wrote the README as a handover document — which service does what, which ones stop the site if billing lapses, what is done in the Shopify admin and what needs a developer, and what to rotate before the keys change hands. Ownership of the domain, Shopify store, Vercel project and Supabase project transfers to HEIR, not to me.

heirbeauty.com
The first collection: five 50ml fragrances at ₹4,500 and a ₹1,200 discovery set

The first collection: five 50ml fragrances at ₹4,500 and a ₹1,200 discovery set

heirbeauty.com
The ATLAS product page, with its size options, accord chips and Judge.me rating

The ATLAS product page, with its size options, accord chips and Judge.me rating

heirbeauty.com
The About HEIR section on the home page

The About HEIR section on the home page

heirbeauty.com
The about page, built from the brand's own photography

The about page, built from the brand's own photography

heirbeauty.com
The journal, pulled straight from Shopify's blog

The journal, pulled straight from Shopify's blog

On a phone, where most of the traffic is

The HEIR home page on a phone
The KEDARA product page on a phone

HEIR launched its first collection on heirbeauty.com: six products, five fragrances at ₹4,500 for 50ml — ATLAS, NECTRA, LANÖ, MĪRĀS, KEDARA — and a ₹1,200 discovery set for people who want to try before they commit. ATLAS carries a 4.8 rating from Judge.me reviews, which appear in the page HTML and in its structured data. Day to day the client works entirely in Shopify: products, prices, photos, stock, orders and blog posts, with the storefront following through tag-based revalidation. The things that used to need a developer — a new olfactive family, a new market and currency, the notes on a product page — are now dashboard edits. The nightly reconcile and the admin failed-events page mean a broken customer mapping shows up on a screen rather than in a support email.

Scope

Headless Shopify storefront, Shopify data modelling (metafields, metaobjects, collections), Custom auth and customer accounts, Multi-market pricing and currency, Reviews and review image uploads, Technical SEO and structured data, Caching and revalidation, Cron jobs and admin monitoring, Handover documentation

Stack

Next.js 15 (App Router), React 19, TypeScript, Tailwind CSS v4, Shopify Storefront API, Shopify Admin API, Supabase (auth, Postgres, storage), Zustand, Framer Motion, Lenis, Embla Carousel, Radix UI, Judge.me, Upstash Redis, Vercel (hosting, cron, analytics)

Open the HEIR case study on its own page

Couture store · 2026

A made-to-order couture label where nothing is sold off a shelf. I built a headless Shopify storefront that treats a size as a brief rather than a stock number, and keeps the shop, the studio and the WhatsApp line pointed at the same order.

Client
Ishikaa Jain
Platforms
Headless Shopify · Web
Timeline
Live, with releases since
Year
2026
ishikaajain.com
The Ishikaa Jain homepage: a runway finale behind the line An expression of Indian craftsmanship

41

Pieces live, imported from the client's own workbook

₹9,000 – ₹1,52,000

Price range the catalogue has to hold

8

Measurements captured before a piece reaches the bag

Nothing in this catalogue is stock. A short kurta with flared pants is ₹9,000 and the Jhumar handwork mermaid skirt is ₹1,52,000, and every piece is cut after the order, not before it — the announcement bar says so outright: made to order, every piece cut to your measurements. That breaks the assumption a normal store is built on, where a size is a number in a warehouse and Add to Cart is the end of the conversation. At ₹1,52,000 the shopper does not want a checkout, she wants a fitting, a fabric in her hand and someone who will tell her what can be changed. Almost all of that was already happening on WhatsApp and in the studio, so a website that could only take card payments would have been a brochure sitting next to the real business. The site had to carry a measurement, a question and an appointment as seriously as it carries a transaction. And it had to do all of it while Ishikaa, not a developer, stays in charge of what the home page says.

  1. 01

    I built the storefront as a headless Next.js 15 app on Vercel reading the Shopify Storefront API, and left Shopify as the till, the catalogue and the admin. The tradeoff is that I own every page — a theme would have given me a product page for free — but a theme's product page is exactly the part that could not stay standard here.

  2. 02

    Add to Cart does not add on the first press. It opens a measurement sheet with eight fields — chest, waist, hip, inseam, shoulder, sleeve length, bottom, armhole — pre-filled from the chosen size, and only finishes the add once she saves. Measurements are the order, so they are collected while she is still looking at the garment rather than chased over email a week later.

  3. 03

    Those values travel as cart line attributes named with a measurement. prefix, which the server action filters on and Shopify shows as labelled rows against the order line. It also fixes a real bug: most pieces are a single Shopify variant with the size carried in the attributes, so without them the cart folds two sizes of one kurta into one line of quantity two while the order Shopify records has them as two.

  4. 04

    "Visit store (book an appointment)" goes to an in-app route under the product, not to an external calendar. It carries the handle and the chosen size through, so the booking page shows that garment, prices the variant she actually picked rather than the cheapest one, and then mounts a Cal.com inline embed for ishikaajain/personal-appointment. The whole route is noindex — a booking flow tied to one product has no business in search results.

  5. 05

    "Customise it" is a wa.me link with the message already written: "Hi, I'd like to customise: <garment name>". The number lives in one exported constant shared by the footer, the mobile menu and this button, because the way a customer ends up messaging a dead number is three copies of it drifting apart. It lands in the Interakt business inbox, not on someone's personal phone.

  6. 06

    Every piece of merchandising copy lives in a Shopify metaobject rather than in the code: the announcement bar, the signup offer, the collection launch modal, the Instagram reel row, and contact form submissions. Each one has a setup script that sets access.storefront to PUBLIC_READ and turns on the publishable capability — the first because a metaobject is invisible to the Storefront API without it and fails silently, the second because Active/Draft then doubles as the on/off switch with no custom boolean and no deploy.

  7. 07

    The launch modal's copy can contain {collection} and {discount} tokens, resolved at render from the referenced collection and the live Shopify discount. Neither is stored as text, so renaming a collection or dropping a discount from 10% to 5% cannot leave the popup promising something checkout then refuses.

  8. 08

    Navigation filters by tag, not by collection. A piece is women's and a kurta set and georgette and party wear all at once, and a collection per combination would be unmanageable, so the mega menu composes queries like tag:"women" AND tag:"kurta-set". Each tag is quoted because Shopify's search syntax reads a bare hyphen as an operator and hyphenated tags are the norm here.

  9. 09

    The client had already filled a Google Sheets workbook with one tab per product, labels down column A. Rewriting 24 tabs into my flat CSV template was the other option; meeting their format was the cheaper side to bend, so the importer reads both and dispatches on file extension. It tolerates "Rs 35000", "Rs. 60,000" and "Rs 1,52,000" by stripping every separator instead of interpreting it, because that last one is Indian grouping for 152000 and naive parsing reads it as 1.52. Each product goes in as one productSet call, which upserts on handle, so the corrected sheet the client always sends after the first pass updates the products instead of duplicating them.

  10. 10

    Where the workbook was thin, I derived rather than invented. Gender and garment type come out of fixed positions in the SKU (IJ-MTO-W-3PC-001-GL-XS → women, and 3PC broken by the title because five are kurta sets and one is a skirt set), fabric from words in the free text, occasion from the category. All of it is written into a flat review sheet marked as derived, so a wrong guess is a cell the client corrects rather than a bug I have to chase.

  11. 11

    The Admin API credentials are a Dev Dashboard app, which is issued a token that expires in 24 hours rather than a long-lived shpat_ one. So the token is exchanged with the client credentials grant, cached with a 60-second margin so a request cannot pick one up mid-expiry, and guarded by a single in-flight promise so a cold instance does not fire one exchange per concurrent caller.

  12. 12

    Indexing is gated on purpose. The canonical origin comes from one environment variable that every canonical URL, OG image, JSON-LD @id and the sitemap derive from, so moving the site to its own domain is a dashboard change rather than a release. While the flag is off, robots.txt still allows crawling but publishes no sitemap — a blocked page can never be read, so Google would never see the noindex and could index the bare URL anyway.

  1. Reading the business before the brief

    I started from where the selling already happened: Instagram, WhatsApp and the studio in Mumbai. That is what decided the shape of the product page — measurements, an appointment and a customise message sit beside Add to Cart rather than behind a contact form. The commerce plumbing was the easy half.

  2. Getting the catalogue in

    The 41 pieces arrived as a workbook with one tab each and no taxonomy. I wrote an importer around the client's format, then a second script that flattens every tab into one reviewable row and fills in the gender, garment, fabric and occasion columns the navigation needs. Everything imports as draft, because nothing should reach a live storefront on the strength of a parsed spreadsheet.

  3. The product page

    This is the page the project lives or dies on. The measurement sheet gates the add, the size follows the shopper into the appointment route, Judge.me supplies the rating badge, and the accordions read fabric and policy text out of Shopify metafields with honest fallbacks when a field is empty. Size runs XS to XXL with per-size defaults so nobody faces eight blank boxes.

  4. Booking, under the product

    I resisted linking straight out to Cal.com. A bare calendar loses the garment, so the appointment route rebuilds the piece — image, price for the selected size, boutique, duration, what the session includes — next to the inline embed. The namespace, event slug and mount id all derive from one booking config, which stopped two components pointing at two different Cal accounts.

  5. Handing the content over

    Then I moved everything Ishikaa would want to change out of the code: the announcement lines, the signup offer, the launch modal, the reel row, the home collections. Each has a documented setup script and is edited in Shopify admin → Content → Metaobjects. Collections are smart collections driven by tags, so a new piece files itself instead of relying on someone remembering.

  6. Syncing and SEO

    One webhook URL takes every topic, verifies the HMAC, and busts the matching Next cache tag for products, collections and discounts; customer events sync into Supabase with failures recorded rather than dropped. Each page declares its own canonical through a shared helper, since without one every page silently inherits the home page's and claims to be it.

  7. Holding it at the gate

    The store is live at ishikaajain.com. Search indexing is the one switch still to flip: the deployment is serving the real domain but the canonical origin and the indexing flag are still set to the development values, so the store is invisible to Google until those two environment variables are updated.

ishikaajain.com
41 pieces in arch-framed cards, from a ₹9,000 kurta set to a ₹1,52,000 mermaid skirt

41 pieces in arch-framed cards, from a ₹9,000 kurta set to a ₹1,52,000 mermaid skirt

ishikaajain.com
A product page: sizes XS to XXL, add measurements, book an appointment, customise on WhatsApp

A product page: sizes XS to XXL, add measurements, book an appointment, customise on WhatsApp

ishikaajain.com
The appointment route, with a Cal.com slot scoped to that one piece

The appointment route, with a Cal.com slot scoped to that one piece

ishikaajain.com
The lookbook, mixing runway video with campaign stills

The lookbook, mixing runway video with campaign stills

ishikaajain.com
The brand story page

The brand story page

On a phone, where most of the traffic is

The Ishikaa Jain home page on a phone
A product page on a phone

The store is live at ishikaajain.com, with all 41 pieces up and every price from ₹9,000 to ₹1,52,000 rendering with Indian grouping. A shopper can pick a size, give her measurements, and either add the piece to the bag with those numbers attached to the order line, or book a 30-minute fitting for that exact garment at the Mumbai store, or open WhatsApp with the customisation message already written. Ishikaa can change the announcement bar, the offer, the launch modal and the Instagram row from Shopify admin without me, and a corrected product sheet can be re-imported over the top of the old one without duplicating anything.

Scope

Headless storefront architecture, Product page and made-to-order flow, Measurement capture to Shopify order line, Per-product appointment booking, Spreadsheet-to-Shopify product import, Shopify metaobject content model, Tag-based catalogue navigation and filters, Customer accounts and email OTP auth, Cart, checkout handoff and discounts, Reviews integration, Webhooks, caching and cache invalidation, Technical SEO and structured data

Stack

Next.js 15 (App Router), TypeScript, Tailwind CSS, Shopify Storefront API, Shopify Admin GraphQL API, Shopify metaobjects and metafields, Supabase (auth and sync), Cal.com embed, Judge.me, Upstash Redis, Zustand, Framer Motion and GSAP, Vercel

Open the Ishikaa Jain case study on its own page

Cooking oil store · 2026

Seven wood-pressed cooking oils that look the same in a list and behave very differently in a pan. The storefront's whole job is making that difference easy to see before you add to cart.

Client
The Soul Foods
Platforms
Headless Shopify · Web
Timeline
Live, with releases since
Year
2026
thesoulfoods.in
The Soul Foods collection page: seven wood-pressed oils with filters, sorting and a grid-density switch

7

Wood-pressed oils in the range

1L / 5L

Sizes across most of the range

₹999

Free shipping threshold, set in Shopify

The Soul Foods sells seven cold-pressed oils — sunflower, sesame, safflower, coconut, groundnut, mustard and avocado — and in a product list they are nearly indistinguishable. Same bottle, overlapping prices, one word of difference on the label. The real difference is what each oil is for: mustard oil at ₹427.50 belongs in pickles and North Indian cooking, avocado oil at ₹1,199 belongs on a salad or under a high-heat roast, and nobody buying their first bottle knows that. On top of that, every oil is discounted off a compare-at price, sits in two sizes, and the brand sells on a specific idea — a squeeze bottle you can portion with, instead of a tin you pour from. A stock Shopify theme flattens all of it into a grid of identical cards and a size dropdown. The brief was to build a storefront that explains the range while leaving Shopify in charge of money, stock and tax.

  1. 01

    The storefront is its own Next.js App Router app on Vercel, reading products, collections and articles through Shopify's Storefront API. Nothing about the layout is decided by a theme, and the brand's product photography gets to run full width.

  2. 02

    Shopify keeps the parts worth not rebuilding: inventory, orders, payments, taxes, discount codes and the hosted checkout. The compare-at pricing behind every bottle — coconut oil at ₹665 down from ₹850, sesame at ₹475 from ₹600, safflower at ₹465 from ₹800 — is Shopify's own pricing, read through the API, so a sale is a price change in the admin rather than a deploy.

  3. 03

    The collection page carries a filter drawer, a sort control and a grid-density switch. The range fits on one screen, so the useful action is not paging through results but comparing them side by side: the density switch lets someone go wide to read labels or tighten the grid to see all seven at once.

  4. 04

    Product pages lead with the bottle, then give the size switch for 1L and 5L, the discount against the compare-at price, and the smoke point. The smoke point is the fact that decides whether an oil survives what you are about to cook, so it sits on the page rather than three paragraphs into a description.

  5. 05

    Each product carries short, plain-language tags — Nutrient-Dense, Cold-Pressed, Gut-Friendly — instead of a nutrition table. The tradeoff is accepted deliberately: a tag is less precise than a panel, but it is read, and the detail still lives in the accordions underneath.

  6. 06

    Reviews run on Judge.me, with the rating shown on the product page and on the cards in the grid, and a write-a-review form that accepts photos. Review images upload to Supabase storage rather than being posted straight to a third party, so a failed submission is recoverable.

  7. 07

    Customer accounts run on Supabase with email OTP: no password to set, lose or reset, which suits a store people buy from every few months. The account area covers profile, order history and saved addresses, with orders read back from Shopify so the admin stays the single source of truth.

  8. 08

    Merchandising the brand changes often is read from Shopify rather than written in code — the ₹999 free-shipping announcement bar, the home page's featured collections, the newsletter sign-up and the trust strip. The founders edit those in the Shopify admin they already use; the cost is one more place to look when something on the page is wrong.

  9. 09

    Writes go through Shopify's Admin API behind the app's own API routes, never from the browser, and Shopify webhooks all land on one HMAC-verified endpoint that busts the matching Next.js cache tag. One URL and one secret to configure means a price edit in the admin reaches the live page without waiting out a cache.

  10. 10

    A scheduled reconcile job re-checks what a dropped webhook would have missed, and an admin page lists failed events so a sync problem is visible instead of silent. Upstash Redis rate-limits the public API routes, because contact, newsletter and review endpoints are the ones that get hammered.

  11. 11

    The contact page is a real form rather than a mailto link: name, email, an enquiry category and up to five image attachments, which matters for a food product where the complaint is usually a photo of a bottle. It sits beside a WhatsApp link, the brand's phone number and its Telangana plant address, and the submission is stored through Shopify metaobjects so nothing depends on an inbox.

  12. 12

    The blog is Shopify-backed, with a load-more endpoint instead of numbered pages, and carries the cooking and oil-guide posts the brand uses to bring people in from search. Every page's metadata is generated from the Shopify data, so a new product or article is indexable the day it is published.

  1. Discovery

    Started from the question a first-time buyer actually asks: which of these seven do I need? That turned into the facts that had to be on the card and the top of the product page — what the oil is for, its smoke point, and the price in each size — rather than buried in a tab.

  2. Design

    Built the storefront around the brand's own bottle photography, a warm palette and a rounded display face, with the squeeze bottle treated as a selling point rather than packaging. Product cards show the compare-at price struck through next to the live one, since the discount is part of how the range is sold.

  3. Build

    Next.js App Router with Tailwind and Zustand for cart state, reading from Shopify's Storefront API and writing through the Admin API. The filter, sort and density controls, the size switch and the review form were the pieces that took the most iteration, because they are the ones people actually touch.

  4. Catalogue and content

    Loaded the seven oils, their sizes, compare-at pricing, tags and the blog into Shopify with import scripts rather than by hand, so the catalogue can be rebuilt or extended without re-typing it. The announcement bar, featured collections and policy pages went in as Shopify content the founders can edit.

  5. Accounts and sync

    Wired Supabase email OTP for login, mapped customers to their Shopify records, and put the webhook, reconcile job and failed-events page in place. The point of that last piece is boring and important: when a sync breaks, someone can see it.

  6. Where it is now

    Live at thesoulfoods.in with the full range, accounts, reviews and the blog working end to end. What is still outstanding is content, not engineering — the home page hero and several product accordions carry placeholder copy the brand needs to replace.

thesoulfoods.in
The home page, led by the bottle rather than a banner

The home page, led by the bottle rather than a banner

thesoulfoods.in
Coconut oil: the 1L and 5L switch, the discount, and the plain-language tags

Coconut oil: the 1L and 5L switch, the discount, and the plain-language tags

thesoulfoods.in
The about page, with the brand's own timeline

The about page, with the brand's own timeline

thesoulfoods.in
The blog, pulled from Shopify

The blog, pulled from Shopify

On a phone, where most of the traffic is

The Soul Foods home page on a phone
A product page on a phone

All seven oils are live at thesoulfoods.in in both sizes, selling through Shopify checkout, with filters and sorting on the collection page, Judge.me reviews on the products, OTP accounts with order history, and a blog. The founders set pricing, discounts, the ₹999 free-shipping rule and the announcement bar from the Shopify admin, and fulfil orders there too, so nothing routine needs a developer. The storefront itself is entirely the brand's own — the design is not bounded by a theme, and the oils are presented by what they are for rather than as seven rows of the same bottle.

Scope

Storefront design, Headless Shopify build, Filtering, sorting & grid density, Customer accounts, Reviews with photo uploads, Webhooks & reconcile job, Contact & enquiry handling, Catalogue migration, Blog & SEO

Stack

Next.js, React, Tailwind, Zustand, Shopify Storefront API, Shopify Admin API, Supabase, Judge.me, Upstash Redis, Vercel

Open the The Soul Foods case study on its own page

I design the app, build it, put it on the stores and keep it running afterwards. Pick one part or hand me the whole thing.

All services
  • SwiftUI interfaces
  • Apple features
  • Works offline

I build native iPhone and iPad apps in Swift and SwiftUI. If your product needs Apple Pay, widgets, HealthKit or just the smoothest possible feel on iOS, native is usually the right call.

iOS app development

Nice screens are the easy part. The things below are what make people trust an app, and they're where most of the work goes.
01

Login

Email, one-time codes, Apple and Google sign-in, Face ID.

02

Payments

Stripe, Apple Pay, in-app purchases, subscriptions.

03

Notifications

Push messages that open the right screen and don't annoy people at 2am.

04

Analytics

Tracking set up to answer the questions you actually have.

05

Offline mode

The app keeps working without signal and catches up later.

06

APIs and admin

Clean APIs and simple admin screens for your team.

07

Store release

Screenshots, listings and app review on both stores.

08

Monitoring

Crash reports and alerts, so I hear about problems first.

How an idea becomes something on the stores. Four steps, and you see working builds through all of them.

How projects run
Process: 4 stepsTypical build: ~10 weeks
  1. Understand

    We talk through the idea, who it's for and what a good result looks like.

    25%

  2. App wireframes drawn in a dot-grid notebook, next to a phone showing the prototype

    Shape

    Flows, wireframes and a clickable prototype, while changes are still cheap.

    50%

  3. Build

    Design and code in one-week cycles, with a new test build most weeks.

    75%

  4. Launch and after

    Store release, monitoring and regular improvements once people start using it.

    100%

No round marketing figures. Every one of these is on a store listing you can open right now.

0k+

Downloads

Across the apps I've put on the stores

0+

Releases shipped

On one app alone, since launch

0 wks

To a first MVP

Pulse, from empty repo to build

0 day

To hear back

One working day, every enquiry

siddhesh.studio is just me, and that's on purpose. The person who designs your screens also writes the code, deals with App Store review and gets the alert at night if something breaks.

I take on two projects at a time at most, so yours gets proper attention. And I care a lot about finishing on the date we agreed.

Working with
US, UK & EU teams
Hours
Overlap with ET & CET
Replies
Within one working day
Projects at a time
Two, at most
Siddhesh working at a desk by a window

Most people start with a project and move onto a monthly arrangement once the app is live. You can also stop after launch and come back later.
How a project runs
Most start here

A project

$500and up

One app, one price

A first release designed and built end to end. Quoted off your feature list, not an hourly guess.

  • Design and build, start to finish
  • One fixed price, agreed before I start
  • Weekly builds you can open on your phone
  • Store submission and launch handled
  • The code in your own GitHub, documented
Get a price

Ongoing

Monthlyquoted on the call

Keeping it alive

For an app that's already shipped and needs someone looking after it every month.

  • A set number of days each month
  • New features, picked by you each sprint
  • OS updates and crash fixes as they land
  • Store releases whenever you want one
  • Cancel at the end of any month
Talk it through

Have an app idea? Tell me about it and I'll send you a price.

  • Projects from $500
  • Reply within one business day
  • Free, no obligation
Get a free estimate

Kind words

“Siddhesh treated our app like it was his own. He pushed back on the right things, shipped when he said he would, and the small details are what our users mention first.”

Founder, consumer fintech startup

Projects start from $500, or ₹60,000 for clients in India. Small jobs like a redesign, a fix or a single feature sit at that end, and full apps cost more depending on what they need to do. After a short call I send a fixed-scope proposal with one price in USD, GBP, EUR or INR.

A focused first version usually takes 8 to 12 weeks including design. A small MVP can launch in 6 to 10.

Yes. My clients are startups and companies in the United States, the UK, Europe and across India. I'm based in Mumbai, my working day overlaps with all of them, and I can meet in person in Mumbai.

Yes. Either natively in Swift and Kotlin or from one codebase with Flutter or React Native. I'll recommend whichever fits your product and budget.

You do, from the first commit. The code lives in your repositories, the apps are published under your Apple and Google accounts, and the IP is assigned to your company.

Most clients stay on a monthly plan for updates, fixes and new features. There's no long contract, so you can stop whenever you want.