I have been building Chattr, a social platform designed specifically for Twitch streamers and their communities. Think of it as a Twitter-like feed built on top of the Twitch identity layer -- where posts can include clips, polls, stream announcements, and custom reactions using Twitch emotes. After months of development, the Android version is now in closed beta, and I am looking for testers to help put it through its paces.
This post covers the stack I chose, some of the interesting problems I ran into along the way, and lessons learned shipping a cross-platform native app with real money moving through it.
The Stack
Chattr is built with React Native 0.85 + React 19 and written entirely in TypeScript. The main libraries driving the experience:
- NativeWind v4 for Tailwind-style styling in React Native
- React Navigation (native-stack + bottom-tabs) for navigation
- Stripe React Native for tips and creator payouts
- Firebase Cloud Messaging for push notifications
- react-native-app-auth with PKCE for Twitch OAuth
- react-native-keychain for secure token storage
The backend is a Laravel API at chattr.online/api/v1 that uses Sanctum for token-based authentication, with Twitch's API and Stripe Connect layered on top.
Connecting to Twitch Identity
The first real challenge was Twitch OAuth. The flow itself is standard OAuth 2.0 with PKCE, but Twitch returns scopes as a JSON array rather than a space-separated string. react-native-app-auth expects the latter, so deserialising the token response would blow up.
The fix was to normalise the response on the server before it ever reached the client. The app exchanges the code server-side, receives the corrected token shape, and stores credentials in the device keychain. Credentials never touch AsyncStorage -- they are encrypted at the OS level via keychain, which matters when you are handling payment-related sessions.
From there, the app pulls the user's Twitch broadcaster type, follower counts, subscriber status, and stream metadata to populate their Chattr profile. This lets the social graph feel native to Twitch -- you already know who is an Affiliate, who is a Partner, and who is live right now.
Post Types and the Feed
The feed supports several post types:
- Text posts (up to 2000 characters)
- Twitch clips (embedded via WebView)
- Polls (2 to 5 options)
- Scheduled stream announcements
- Live notifications (when a streamer goes live)
- Quote reposts
Reactions use three Twitch emotes: KreyGasm, BleedPurple, and BigSad. It keeps the social layer feeling native to the platform rather than grafting on a generic like/heart system.
Controlling Video in the Feed
Embedded clips in a scrolling feed are a battery and CPU trap if you are not careful. Every WebView kept playing even when scrolled off screen.
The fix was using viewAreaCoveragePercentThreshold on the FlatList to track viewport visibility. When a clip drops below 50% visible, its WebView pauses. When the user pulls to refresh, a refreshKey state integer is incremented, which remounts the affected WebViews and stops any mid-playback clips from continuing in the background. It is a small thing but it makes a real difference in perceived battery usage.
Push Notifications: Three States, One Flow
Push notifications in React Native have a well-known gotcha: you need to handle three distinct app states -- foreground, background, and quit (cold launch) -- and they all behave differently.
For Chattr, there are over ten notification types to route: reactions, mentions, reposts, comments, live alerts, scheduled stream reminders, tips received, tip refunds, and collaborator tags.
The solution was:
- Foreground: update the badge count in the notification bell without navigating
-
Background (
onNotificationOpenedApp): navigate to the relevant post or screen when the user taps -
Cold launch (
getInitialNotification): check on app mount and perform the same deep-link navigation
Firebase token rotation also needs careful handling. The app subscribes to onTokenRefresh and also refreshes tokens on AppState restore after a device factory reset clears the keychain. Missing either case means silent notification failures that are hard to debug in production.
Tipping with Stripe
Chattr lets viewers tip creators between $1 and $500 per post. The flow uses Stripe Payment Intents created server-side. The client gets back a client secret, presents the Stripe sheet, and on confirmation the tip is recorded.
Creators who want payouts go through Stripe Connect onboarding. This is a WebView-based flow that hands off to Stripe's hosted onboarding and then redirects back to the app. The settings screen monitors AppState changes so that when the user returns from the background after completing onboarding, the payout status updates immediately without needing a manual refresh.
Email verification is gated before a user can enable tips -- partly a Stripe compliance requirement, partly a way to reduce throwaway accounts.
A Few Other Notes
Comments are flat, intentionally. Replies go one level deep. Supporting arbitrary nesting in a mobile feed creates layout and performance complexity that is not worth it for the use case. One level of replies handles 95% of real conversations.
Cancellation tokens everywhere. Any async fetch in a component returns a cleanup function. When React unmounts the component mid-fetch (navigation, etc.), the update is cancelled. This avoids the classic "can't perform a state update on unmounted component" warning and the subtle bugs that come from stale state being applied after navigation.
AppState for payout sync. It took a few iterations to get right, but monitoring AppState changes in the settings screen is the cleanest way to reflect external state changes (like completing Stripe onboarding) without polling an endpoint on a timer.
What's Next
The app is currently in closed beta on Android. If you are a developer, streamer, or Twitch community member who wants to kick the tyres, I would love to have you test it.
You can reach me at:
- Email: me@michaelbrooks.co.uk
iOS is also in development. The codebase is already cross-platform, so most of the feature work carries over -- it is mostly the platform-specific signing, entitlements, and App Store review pipeline that remains.
If you are building anything in the React Native + Twitch space, or have thoughts on the architecture decisions above, I would genuinely like to hear from you. Drop a comment below or reach out directly.
View the website at https://chattr.online
Chattr is built by a solo developer. Feedback from the community makes a real difference.
Top comments (0)