On this page

Foam

Foam is a React Native Twitch client built to create a cleaner mobile viewing experience with better chat support and a stronger mobile delivery workflow.

react-nativeexpoexpo-routertypescriptbunlegend-statelegend-listreact-queryi18nextmaestrotwitch7tvbttv

Foam is a custom mobile Twitch client that I started as a way to improve my React Native skills while building something I would actually want to use day to day. The native Twitch mobile app still feels limited compared with desktop, especially around chat customisation and support for third-party extensions, so this project explores what a cleaner and more capable mobile experience could look like.

Foam stream chat screen showing Twitch messages with third-party emotes and badges Foam search screen listing live Twitch channels

The app is built with React Native, Expo, Expo Router and TypeScript, with Bun used for package management and runtime tooling. It integrates with Twitch APIs for core app functionality, and pulls in third-party chat providers like 7TV, BTTV, FFZ and StreamElements to make chat feel closer to desktop. I also set the project up with Storybook for isolated UI development, Maestro for end-to-end testing, and EAS for building and distributing multiple app variants across iOS and Android.

The high-churn parts of Twitch (live playback, fast-moving chat, third-party emotes, badges and cosmetics) drove most of the architecture. Chat state lives outside React in Legend State so frequent message, emote and badge updates avoid broad re-renders, and chat rows are virtualised with Legend List using bounded message windows and memoised rows. Networking goes through a single tagged API client built on TanStack Query with per-request timeouts, image loading is handled by expo-image and react-native-nitro-image behind a custom priority-queued cache, persistence uses MMKV, and the app is internationalised with i18next. Sentry covers error and performance monitoring, while Reassure guards against chat performance regressions in CI.

One of the more interesting parts of Foam is the amount of platform and delivery work around the app itself. The repository supports separate development and production variants, includes an auth proxy for Twitch authentication, and is structured so local development builds, preview builds and production builds can all coexist without clashing on the same device (different bundle identifiers).

This project has been a good way to go deeper on modern mobile workflows as well as app architecture. It has pushed me to think more seriously about native build tooling, CI/CD for mobile, automated UI testing, release management and how to keep a React Native codebase maintainable as it grows.

It’s still a work in progress, and I’m iterating on features as I use it.

What I focused on

  • Building a React Native app around a real product problem rather than a generic starter
  • Exploring Expo and EAS workflows for multiple mobile build variants
  • Improving developer experience with Bun, Storybook, conventional commits and stronger project documentation
  • Adding more confidence around releases with Maestro-based end-to-end testing
  • Working through Twitch integrations and the infrastructure needed to support authentication properly
  • Performance optimizations and better memory management for high throughput components such as the chat UI

What I learned

  • How to structure a React Native app so development, preview and production variants can live side by side
  • More about the realities of mobile delivery, including build pipelines and store-facing workflows
  • The value of treating documentation, release process and testing as first-class parts of the project
  • How much stronger a side project becomes when it is tied to a genuine user experience problem
  • Performance optimizations and footguns in React Native