An open-source React Native social media app for fast, calm, intentional sharing — built end-to-end with Expo SDK 54 and TypeScript.
Getting Started · Deploy Guide · Report Bug · Request Feature
xMind is a production-grade, open-source social network you can clone, study, and ship. It's a complete reference for building a modern, cross-platform mobile social media app on React Native + Expo SDK 54 — strict TypeScript, a server-paginated infinite feed, optimistic mutations, fuzzy on-device search, an on-device personalised feed ranker, real-time-feeling chat, and a token-driven design system that adapts across iOS and Android.
Most social-media tutorials stop at "a list of posts." xMind starts where they end — with reshares, a reshare graph, a TF-IDF + MMR ranking pipeline, and a backend engineered to scale on serverless. The app is tuned to feel smooth even on a 2 GB-RAM Android device.
🚧 Active development. Core feed, chat, reshares, profiles, and search are working; some items on the roadmap (stories backend, push notifications, web build) are still in progress.
| Feature | Description | |
|---|---|---|
| 🧠 | On-device feed ranker | Layered scorer in pure TypeScript — TF-IDF cosine relevance, exposure decay, MMR diversity rerank, per-author cap, and cold-start fallback. Deterministic and sub-millisecond per page. |
| ♾️ | Cursor-paginated infinite feed | TanStack useInfiniteQuery with onEndReached, native pull-to-refresh, and optimistic like / reshare / delete that never invalidates the whole list. |
| 🔁 | Reshare graph | First-class repost model with originalPost linkage, atomic idempotent toggles, author notifications, and automatic purge of dangling reshares. |
| 💬 | Real-time-feeling chat | Inbox + thread screens with idempotent clientId-keyed sends, FlashList v2 maintainVisibleContentPosition, AppState-aware polling, and tappable shared-post preview cards in DMs. |
| 🔍 | Fuzzy search | Fuse.js indices over users and posts, leading-@ tolerance, merged with debounced server-side search. Feels instant on every keystroke. |
| 🎨 | Token-driven design system | Primitive → semantic → CSS-variable token chain that flips the whole UI on prefers-color-scheme. One designer's hand across every card. |
| ⚡ | 120 fps animations | Reanimated v4 worklets on the UI thread for every press, like burst, and screen transition. No JS-thread animations anywhere. |
| 🛡️ | Backend that scales | Cached MongoDB connection across serverless cold starts, atomic toggles, Mongo indexes on every hot query, and an Arcjet bot / rate-limit shield. |
| 🔐 | Clerk auth + Cloudinary | Social sign-in with a server-side profile-sync bridge, plus on-the-fly Cloudinary image transforms for posts, avatars, and banners. |
Mobile
- Expo Router v6 file-based routing with the
(tabs)group - TanStack React Query v5 —
useInfiniteQuery, optimistic mutations - FlashList v2 for every virtualised list, expo-image for caching
- Zustand + AsyncStorage for session and feedback stores
- Clerk Expo auth, Fuse.js fuzzy search, expo-haptics / expo-blur / expo-glass-effect
Backend
- Express 5 (ES modules) + Mongoose 8 on MongoDB Atlas
- Clerk Express middleware, Cloudinary image hosting
- Arcjet shield, bot detection, and token-bucket rate limiting
- Cached Mongoose connection +
compressionfor serverless deploys
- Node.js >= 18 and Git
- An iOS Simulator (macOS) or Android Emulator (any OS)
- Free-tier accounts: MongoDB Atlas, Clerk, Cloudinary, Arcjet
# 1. Clone the repo
git clone https://github.com/aashir-athar/xmind-app.git
cd xmind-app
# 2. Install backend deps and start the API on http://localhost:5001
cd Backend
npm install
cp .env.example .env # then fill it in (see Configuration below)
npm run dev
# 3. In a second terminal, start the mobile app
cd ../Mobile
npm install
cp .env.example .env # set EXPO_PUBLIC_API_URL to your LAN IP
npx expo start --clearPress i for iOS or a for Android. On a physical device, set EXPO_PUBLIC_API_URL to your computer's LAN IP — not localhost.
For full deploy instructions (EAS Build, Atlas, Clerk, Cloudinary, Arcjet), read zero-to-deploy.md.
Configuration & environment variables
Backend .env
PORT=5001
NODE_ENV=development
MONGO_URI=mongodb+srv://<user>:<pwd>@<cluster>/xmind?retryWrites=true&w=majority
CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
CLOUDINARY_CLOUD_NAME=...
CLOUDINARY_API_KEY=...
CLOUDINARY_API_SECRET=...
ARCJET_KEY=ajkey_...
ALLOWED_ORIGINS=https://your-web-dashboard.example.comMobile .env
EXPO_PUBLIC_API_URL=http://192.168.1.42:5001/api
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...Never commit either file — .gitignore already excludes them.
xMind is a monorepo with two apps. Common commands:
| Where | Command | What it does |
|---|---|---|
Mobile/ |
npm start |
Metro bundler (iOS + Android + Web) |
Mobile/ |
npm run ios |
Open in the iOS Simulator |
Mobile/ |
npm run android |
Open in the Android Emulator |
Mobile/ |
npm run lint |
ESLint with Expo's ruleset |
Mobile/ |
npx tsc --noEmit |
Strict TypeScript check |
Backend/ |
npm run dev |
Express with node --watch |
Backend/ |
npm start |
Production start |
How the feed ranker works
The feed is a layered scorer — every signal is a pure, individually exported function that can be unit-tested in isolation:
| Layer | Signal | Notes |
|---|---|---|
| Hard filters | Muted / blocked / max-age / low-quality | Removed up-front |
| Topical relevance | TF-IDF cosine vs. the user's interaction profile | Hashtags 3× weighted |
| Engagement velocity | Likes + comments per hour | Log-normalised by author followers |
| Time decay | Half-life decay | Active-hour multiplier for the user's timezone |
| Diversity rerank | MMR (λ = 0.7) | Prevents 10 posts about the same hashtag in a row |
| Per-author cap | ≤ 2 posts | One person never dominates the feed |
| Cold start | Discover-mode fallback | Until interaction count ≥ 5 |
The ranker is deterministic — no Math.random(). Same inputs, same order, so scroll position stays stable across re-renders. Because ranking runs on-device, the backend stays a stateless, cacheable cursor API.
- Reshares with
originalPostgraph + reshare notifications - Tappable @mentions and #hashtags in posts and comments
- Reply-to-comment threading
- Followers / Following management (Remove + Unfollow)
- Shared-post preview cards in chat (no raw links)
- Quote-reshare (carry resharer commentary above the source post)
- @mentions autocomplete in the composer
- WebSocket transport for chat (Pusher / Ably swap)
- Stories backend (currently UI-only on the home rail)
- Push notifications (EAS Build + APNs / FCM)
- Web build and an end-to-end test pass with Maestro
Want to own one of these? Open an issue and it'll be tagged good first issue or help wanted.
Contributions are welcome and encouraged — code or docs, big or small.
- Fork the repo
- Create a feature branch (
git checkout -b feat/your-thing) - Make your change, then verify with
cd Mobile && npx tsc --noEmit && npm run lint - Commit using Conventional Commits (
feat(home): ...) - Push and open a Pull Request
House rules: TypeScript strict (no any), use the design tokens (no inline hex / magic spacing), and install new deps via npx expo install <pkg> so SDK 54 versions pin correctly.
Distributed under the MIT License. See LICENSE for details.
Aashir Athar
Built by aashir-athar · If xMind helped you, consider leaving a ⭐ — it genuinely lifts the project's reach.
Keywords: react native social media app · expo sdk 54 · typescript social network · on-device feed ranker · tf-idf mmr feed ranking · nativewind · expo router · tanstack query · mongodb express backend · clerk auth · cross-platform ios android · open-source social app