The fastest way to find a blood donor β a real-time, geo-fenced blood donation network built with React Native, Expo & Supabase.
BludStack is a real-time blood donation app for iOS and Android, built as a TypeScript monorepo with React Native, Expo SDK 54, and Supabase. When a recipient posts a blood request, the backend expands outward in geo-fenced rings β notifying compatible, eligible donors 1 km away first, then 5 km, 15 km, 30 km, 50 km, and finally country-wide β until a donor accepts. Think Uber, but for the most important ride of someone's life.
In emergencies the bottleneck isn't blood supply β it's the time it takes to reach a compatible donor nearby. BludStack pages only matching donors, in priority order, in real time.
| Feature | Description | |
|---|---|---|
| π‘ | Real-time ring escalation | Compatible donors in widening geo-rings (1 β 5 β 15 β 30 β 50 km β country-wide) are paged in priority order until the request is filled. |
| π | Atomic accept & complete RPCs | SECURITY DEFINER PostgreSQL functions enforce capacity, cooldown, age, and the N-donors rule in a single transaction β no race conditions, no double-accepts. |
| π°οΈ | Live donor heartbeat | Once a donor accepts, foreground GPS streams their location so the recipient watches them approach on a live map, Uber-driver style. |
| 𧬠| Blood compatibility matrix | Donor matching follows the full ABO/Rh compatibility table server-side, with defensive client-side filtering. |
| π‘οΈ | Row-level security everywhere | Every table has RLS policies; recipient phone numbers stay hidden until a donor commits. |
| π | Killed-state push | expo-notifications + Expo Push wake the app from any state on Android and iOS, with per-OEM channel tuning. |
| π¨ | Tri-state theme & 120 FPS target | System / dark / light tokens, Reanimated v4 worklets, and FlashList v2 target buttery scrolling on mid-tier Android. |
Active development. The mobile client is distributed via EAS builds (no public store listing yet). Clone and run it locally with the steps below.
| Layer | Choice |
|---|---|
| Mobile | Expo SDK 54, React Native 0.81, React 19, TypeScript (strict), Expo Router v6 |
| UI / motion | Reanimated v4 worklets, @shopify/flash-list v2, expo-image, react-native-maps, Ionicons |
| State | React Context (Auth Β· Theme Β· Toast) + Supabase Realtime as cache invalidation |
| Backend | Node 20 + Express on Vercel Serverless, with Vercel Cron for escalation & expiry |
| Database | Supabase Postgres with RLS, atomic RPCs, and postgres_changes realtime |
| Auth | Supabase Auth β passwordless email OTP |
| Notifications | expo-notifications + Expo Push API (APNs / FCM) |
| Storage | @react-native-async-storage/async-storage + expo-secure-store |
This is a monorepo with two apps: backend/ (Express API) and mobile/ (Expo client).
- Node.js >= 20
- npm (lockfiles are committed)
- A Supabase project (Postgres + Auth) and an Expo account for push & EAS builds
git clone https://github.com/aashir-athar/BludStack-RN.git
cd BludStack-RNcd backend
npm install
cp .env.example .env
# Fill in SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, JWT_SECRET, EXPO_ACCESS_TOKEN
# Apply the schema (single source of truth)
psql "$SUPABASE_DB_URL" < ../supabase_schema.sql
npm run devcd ../mobile
npm install
cp .env.example .env
# Fill in EXPO_PUBLIC_API_URL, EXPO_PUBLIC_SUPABASE_URL, EXPO_PUBLIC_SUPABASE_ANON_KEY
npx expo start
β οΈ Push notifications are not delivered to Expo Go on Android since SDK 53. Build a dev client (npx eas build --profile development) to test pushes.
A request flows through the platform like this:
- Recipient posts a request β blood group, units needed, hospital location, and urgency.
- Geo-fence escalation begins β
nearby_compatible_donors(req_id, radius_km)filters by the compatibility matrix and eligibility (age, cooldown, availability), then Expo Push pages the cohort ring by ring. - A donor accepts β an atomic
accept_blood_requestRPC reserves a slot and starts the live GPS heartbeat. - Donation completes β
complete_blood_donationincrements the donor's history and checks fulfillment. A request for N units needs N distinct donors before it flips tofulfilled.
𧬠Blood compatibility matrix
| Recipient | Can receive from |
|---|---|
| Oβ | Oβ |
| O+ | Oβ, O+ |
| Aβ | Oβ, Aβ |
| A+ | Oβ, O+, Aβ, A+ |
| Bβ | Oβ, Bβ |
| B+ | Oβ, O+, Bβ, B+ |
| ABβ | Oβ, Aβ, Bβ, ABβ |
| AB+ | All groups (universal recipient) |
DONOR_FOR_RECIPIENT in constants/BloodData.ts is the canonical mapping; the backend filters by it server-side.
π Geo-fence escalation rings
Request posted
β Ring 1 km (~30 s)
β Ring 5 km (~60 s)
β Ring 15 km (~90 s)
β Ring 30 km (~2 min)
β Ring 50 km (~3 min)
β Country-wide
Each ring claim is DB-persisted with compare-and-set (CAS) semantics, so two cron ticks can never double-page the same ring.
- Real-time geo-fence ring escalation
- Atomic accept / complete RPCs with N-donors rule
- Live donor heartbeat & map tracking
- Passwordless email OTP auth
- Killed-state push notifications (Android + iOS)
- Public app store releases (Google Play / App Store)
- Donor reputation & verified-badge program
- In-app analytics for hospitals & blood banks
Contributions are welcome and appreciated. For major changes, please open an issue first to discuss what you'd like to change.
- Fork the repo
- Create a branch (
git checkout -b feat/your-feature) - Commit your changes
- Push the branch and open a Pull Request
Distributed under the MIT License. See LICENSE for details.
Aashir Athar
Built with React Native, Expo & Supabase by aashir-athar
Keywords: blood donation app Β· React Native Β· Expo SDK 54 Β· Supabase Β· blood bank Β· donor matching Β· geofencing Β· real-time healthcare app Β· TypeScript Β· iOS Β· Android Β· cross-platform mobile app