Building Synapse: A Full-Featured Social Media App with React Native & Supabase (Launching Soon!)
Source: Dev.to
Key Features
- Multi‑block posts (text, images, videos, audio, files, live photos)
- Markdown with syntax highlighting and MathJax support
- Feed recommendations
- Real‑time direct messaging
- Full‑text search
Why We Built This
The idea for Synapse came from a frustration that many researchers and developers share: existing social platforms aren’t built for technical discourse.
The Problems We Kept Running Into
1. Fragmented Discussions
Try following a machine‑learning research discussion on X (Twitter). A single paper breakdown gets split into 15+ tweets in a thread. Equations are screenshots. Code is images. Context gets lost in quote tweets and replies. It’s chaos.
2. No Built‑in Support for Rich Content
Want to share a PDF of your paper? Upload an audio explanation of your code? Attach a dataset file? You can’t. Current platforms treat everything as text + images/video, forcing researchers and developers to link out to external services for anything beyond basic media.
3. Discovery is Broken
Finding the right people to follow in your niche is surprisingly hard. Algorithms optimize for engagement, not relevance. There’s no good way to discover researchers in your specific subfield or developers working on similar problems.
The Vision
We wanted a platform where you could write a single, cohesive post that includes:
- Your explanation in markdown with proper formatting
- Mathematical equations rendered beautifully (LaTeX/MathJax)
- Code snippets with syntax highlighting
- The actual PDF or dataset file
- An audio walkthrough for those who prefer listening
All in one place. No threads. No external links. No screenshots of equations.
Who Is This For?
Researchers & Academics
Scientists who want to share their work, discuss papers, and connect with peers in their field. Whether you’re in ML, physics, biology, or any technical discipline, you deserve a platform that speaks your language—literally, with native LaTeX support.
Developers & Technical Creators
Programmers who want to share ideas, tutorials, and projects in a beautiful, readable format. Write technical content with proper code highlighting, attach relevant files, and build an audience that cares about substance over virality.
We believe the best ideas deserve better than a character limit and a thread of fragmented thoughts.
The Tech Stack
Frontend
- React Native 0.81 with the new architecture enabled
- Expo SDK 54 for rapid development and OTA updates
- React 19 with the latest concurrent features
- TypeScript in strict mode (no
anyallowed!)
State Management
We went with a hybrid approach:
- React Query 5 for server state and caching
- Zustand for client‑side stores
- Context API for global app state (Auth, Audio, Analytics)
Backend
Supabase as our Backend‑as‑Service
- PostgreSQL for the database
- Supabase Auth with email OTP
- Supabase Storage (S3‑compatible) for media
- Realtime subscriptions for live updates
- Edge Functions (Deno) for serverless compute
Technical Deep Dives
1. The Content Block System
Instead of storing posts as flat text, we designed a flexible block‑based architecture:
type ContentBlock = TextBlock | MediaBlock | AudioBlock | FileBlock;
interface MediaBlock {
type: 'media';
items: MediaItem[];
}
interface MediaItem {
type: 'image' | 'video' | 'live_photo';
storagePath: string;
width: number;
height: number;
thumbnailUrl?: string;
// Live photos have separate video/photo URLs
videoStoragePath?: string;
}
Why this matters
- Posts can contain mixed content types
- Each block handles its own rendering logic
- Easy to add new block types in the future
- Type‑safe with runtime type guards
2. Optimistic Messaging
Nobody wants to wait for a server response to see their message. We implemented optimistic updates with local IDs:
// Generate local ID for immediate display
const localId = `local_${Date.now()}_${Math.random().toString(36)}`;
// Show message immediately with "pending" status
addMessageToCache({
id: localId,
content,
status: 'pending',
created_at: new Date().toISOString(),
});
// Server confirms and provides real ID
// (replace the local entry with the server‑provided one)
const { data } = await supabase
.from('messages')
.insert({ content, conversation_id })
.select()
.single();
// Reconcile local and server states
replaceLocalMessage(localId, data);
The tricky part? Handling edge cases
- What if the server response arrives before the optimistic update?
- What if the user sends multiple messages quickly?
- How do we handle failures gracefully?
We built an OptimisticMessageManager that handles all these scenarios with proper reconciliation logic.
3. Signed URLs with Smart Caching
Media files in Supabase Storage require signed URLs for private access. The challenge: these URLs expire!
Our solution
- Cache signed URLs with TTL tracking
- Refresh URLs 5 minutes before expiration
- Batch URL requests to reduce API calls
class SignedUrlService {
private cache = new Map<string, { url: string; expiresAt: number }>();
async getSignedUrl(storagePath: string, postId?: string): Promise<string> {
const cacheKey = this.buildCacheKey(storagePath, postId);
const cached = this.cache.get(cacheKey);
if (cached && !this.isExpiringSoon(cached.expiresAt)) {
return cached.url;
}
return this.fetchAndCache(storagePath, postId);
}
// Helper methods (buildCacheKey, isExpiringSoon, fetchAndCache) omitted for brevity
}
Propagate postId context for cache invalidation.
4. Feed Recommendation Engine
We built a server‑side recommendation RPC that scores posts using multiple factors:
- Recency
- Engagement metrics
- User’s social graph
- Content‑type preferences
// Edge Function: feed-recommendation
const candidates = await fetchCandidates(userId, excludeIds);
const scored = candidates.map(post => ({
...post,
score: calculateRelevanceScore(post, userPreferences)
}));
return scored
.sort((a, b) => b.score - a.score)
.slice(0, limit);
Users can toggle between chronological and recommended feeds.
5. Audio Recording with Gesture Detection
We built custom HoldToRecordButton and HoldToRecordOverlay components:
- Long press starts recording
- Drag to cancel zone aborts
- Visual waveform feedback
- Haptic feedback on state changes
This required deep integration with React Native Gesture Handler and Expo Audio.
Lessons Learned
1. Start with TypeScript Strict Mode
We enabled strict mode from day one. It caught countless bugs during development that would have been runtime errors otherwise. The upfront investment pays dividends.
2. Server‑Side Logic > Client‑Side Complexity
Instead of managing complex state on the client (e.g., cascade deletes when blocking a user), we use database triggers:
CREATE TRIGGER on_user_blocked
AFTER INSERT ON blocked_users
FOR EACH ROW
EXECUTE FUNCTION cleanup_follows_on_block();
This ensures data consistency even if the client crashes mid‑operation.
3. Design for Offline First
We use MMKV (a fast key‑value store) for local caching with TTLs:
- Profiles: 5 days
- Feed data: 2 days
- Messages: Synced with pending states
The app feels snappy because most data is served from cache.
4. Don’t Underestimate Media Handling
Image/video upload seems simple until you consider:
- Compression for faster uploads
- Thumbnail generation
- Progress tracking
- Upload cancellation
- Quota enforcement
- Signed URL management
Our postService.ts is 56 KB for a reason.
5. React Query is a Game Changer
Before React Query, we had manual cache invalidation everywhere. Now:
const { data, isLoading, refetch } = useQuery({
queryKey: ['posts', userId],
queryFn: () => fetchUserPosts(userId),
staleTime: 5 * 60 * 1000,
});
// Mutations automatically invalidate related queries
useMutation({
mutationFn: createPost,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
}
});
What’s Next?
We’re polishing the final details before App Store submission:
- Performance profiling on older devices
- Accessibility audit
- Final round of beta testing
- App Store assets and screenshots
Want to Follow Along?
We’re building in public! Follow our journey:
- X:
- LinkedIn:
- Landing page & waitlist:
If you’re building something similar or have questions about our approach, drop a comment below. Happy to share more details about any aspect of the stack!
What technical challenges have you faced building mobile apps? Let’s discuss in the comments!


