A real-time messaging application built with React Native (Expo), featuring direct messaging, group chats, typing indicators, read receipts, and image sharing.
- Clone Repo —
git clone https://github.com/0xAmaan/message-ai.git cd message-ai/- Install Packages —
bun i - Add keys I provided to .env
- Open two separate terminals and run:
bun run convex-devbun start
- When signing up, you must use test phone numbers:
- +15555550104
- +15555550105
- +15555550106
- +15555550108
- +15555550109
- Verification Code — 424242
- This is for all numbers
- One-on-one direct messaging
- Group chat support (3+ participants)
- Real-time message delivery
- Message persistence (survives app restarts)
- Optimistic UI updates (instant message display)
- Image sending and receiving
- Message timestamps
- Read receipts (checkmark for sent, double checkmark for read)
- Typing indicators ("[Name] is typing...")
- Online/offline status indicators
- Push notifications (foreground)
- Phone number authentication
- Frontend: React Native with Expo SDK 54
- Routing: Expo Router (file-based routing)
- Styling: NativeWind (Tailwind CSS for React Native)
- Authentication: Clerk
- Backend: Convex (real-time backend-as-a-service)
- State Management: Convex real-time subscriptions
- Images: Expo Image, Expo Image Picker
- Notifications: expo-notifications
- Node.js 18+ or Bun 1.0+
- iOS Simulator (macOS) or Android Emulator
- Expo Go app (for physical device testing)
- Clerk account (for authentication)
- Convex account (for backend)
-
Clone the repository
git clone <your-repo-url> cd message-ai
-
Install dependencies
bun install # or npm install -
Set up environment variables
Create a
.env.localfile in the root directory:EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... EXPO_PUBLIC_CONVEX_URL=https://...convex.cloud
-
Set up Convex
npx convex dev
This will:
- Create a new Convex project (first time only)
- Deploy your backend functions
- Generate TypeScript types
- Start watching for changes
-
Set up Clerk
- Go to clerk.com and create a project
- Enable phone number authentication
- Copy your publishable key to
.env.local
Start both Expo and Convex servers:
bun run devThis runs both expo start and npx convex dev concurrently.
Or run them separately:
# Terminal 1: Expo
bun run start
# Terminal 2: Convex
bun run convex-dev# iOS Simulator
bun run ios
# Android Emulator
bun run android
# Web Browser
bun run webmessage-ai/
├── app/ # Expo Router pages
│ ├── (auth)/ # Authentication flow
│ │ ├── phone-input.tsx
│ │ ├── verify-otp.tsx
│ │ ├── username-setup.tsx
│ │ └── profile-setup.tsx
│ ├── (tabs)/ # Main app (authenticated)
│ │ └── index.tsx # Chat list
│ ├── chat/[id].tsx # Individual chat screen
│ ├── new-chat.tsx # Create new chat/group
│ └── _layout.tsx # Root layout
│
├── components/ # Reusable components
│ ├── ChatListItem.tsx # Conversation preview
│ ├── MessageBubble.tsx # Individual message
│ ├── MessageInput.tsx # Text input + image picker
│ └── SignOutButton.tsx
│
├── convex/ # Backend (Convex)
│ ├── schema.ts # Database schema
│ ├── users.ts # User operations
│ ├── conversations.ts # Conversation management
│ ├── messages.ts # Message CRUD
│ ├── typing.ts # Typing indicators
│ └── auth.config.ts # Clerk integration
│
├── lib/ # Utilities
│ ├── constants.ts # App constants
│ ├── notifications.ts # Push notification setup
│ └── network.ts # Network status hook
│
└── docs/ # Documentation
├── MVP_TESTING_CHECKLIST.md
├── IMPLEMENTATION_SUMMARY.md
└── BUG_FIXES.md
- Uses Convex subscriptions for instant updates - no manual polling needed.
When you send a message, it appears instantly in your chat with a pending indicator (clock). The app:
- Shows the message immediately (optimistic update)
- Sends to server in the background
- Updates indicator to checkmark when confirmed
- Changes to double checkmark when recipient reads it
- Single checkmark = Message sent and delivered
- Double checkmark = Message read by recipient(s)
- Clock icon = Message pending/sending
To create a group chat:
- Tap the "+" button
- Select 2 or more users (checkboxes appear)
- Tap "Create Group (X people)"
- Start chatting!
- Green dot on avatar = User is online
- No dot = User is offline
- Updates automatically when users open/close the app
# Install dependencies
bun install
# Start development servers
bun run dev
# Run linter
bun run lint
# Deploy Convex functions
npx convex deploy
# Clear Expo cache
expo start -cRequired in .env.local:
# Clerk Authentication
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
# Convex Backend
EXPO_PUBLIC_CONVEX_URL=https://xxxxx.convex.cloud- User profiles, online status, phone numbers
- Chat metadata (direct/group, participants)
- Message content, images, read receipts
- Real-time typing status
See convex/schema.ts for full schema definition.
Phone Input -> OTP Verification -> Username Setup -> Profile Setup -> Main App
User types -> Optimistic Update -> Convex Mutation -> Database Update -> Real-time Sync to All Clients
Pick Image -> Request Upload URL -> Upload to Convex Storage -> Send Message with Storage ID -> Recipients Fetch Image URL
# Deploy to production
npx convex deploy
# Set production environment variables
npx convex env set CLERK_PUBLISHABLE_KEY pk_live_xxxxxiOS (TestFlight):
# Install EAS CLI
npm install -g eas-cli
# Build for iOS
eas build --platform ios
# Submit to TestFlight
eas submit --platform iosAndroid (APK/Play Store):
# Build for Android
eas build --platform android
# Submit to Play Store
eas submit --platform android- Built with Expo
- Backend by Convex
- Authentication by Clerk
- Styled with NativeWind