Behind the Scenes: Rebuilding Our Mobile App With React Native, Expo, and TypeScript
In this post, we will dive into the technical details and general approach for the redesign.
The challenge: Improving performance for larger teams
Our old mobile app was built with React Native and Expo managed workflow, as well as Apollo and GraphQL. Although we enjoyed that stack, some customers with a large volume of people, projects, and tasks were experiencing long load times, slow schedule rendering, and reduced performance.
Since the web and mobile apps didn’t share a great deal of code (i.e., only a couple of private NPM packages), and the last rewrite of our web app schedule was done in September 2019, the mobile app was missing out on key performance optimization. Even though the UI and interaction had fundamental differences between web and mobile, there was still a lot of common behavior—like the state management, business logic, and schedule data processing (which were essentially the same).
Over time, it became evident that both apps would benefit from a common codebase. We decided it would be worth investing in a monorepo (common + web + mobile) and taking the opportunity to redesign the mobile app from the ground up—revisiting all aspects of the UI and UX.
The approach: A UI redesign from the ground up, with a focus on the most used mobile features
The primary goal with the new mobile app was to significantly improve perceived performance and provide the best-in-class mobile UX, while also borrowing some of the wins from the web app.
We took a few steps before starting the development, including:
- Collecting insights from our analytics based on the usage of the existing mobile app.
- Gathering customer feedback and several suggestions and ideas.
- Defining the scope of the new version from a user perspective.
- Redesigning from scratch with a consistent UI between Android and iOS, and removing unnecessary clutter.
- Making prototypes to demonstrate different explorations of the schedule scroll.
- Abstracting part of the web app logic and moving it to a common package within our monorepo.
For the scope, we wanted to go with a more streamlined approach and focus on the core features most relevant to mobile users rather than replicating all functionalities from the web app. With that in mind, we decided to focus only on the Schedule and Log Time areas.
The tech stack: React Native was the right tool for the job
We decided to develop the new mobile app as a greenfield rewrite due to many differences in the scope and architecture. It was also a great opportunity to introduce some new concepts into our engineering process:
- TypeScript: It's such a joy to code React components in TypeScript, and there was no reason to start a fresh project without it. It's quickly becoming the industry standard, and with great DX, it's fantastic for code quality and refactoring with confidence. As the first project at Float in this language, we’re also using it as a reference for new initiatives and potential adoption in other areas.
- React hooks: The React ecosystem has evolved significantly, and with the addition of hooks, we decided to build the entire app exclusively with functional components. By under engineering, we were able to keep things simple and allow common logic to be shared more easily.
- Expo bare workflow: We were well aware of the limitations of managed workflow in our previous mobile app, so we wanted the flexibility of using any native module (e.g., react-native-config and react-native-intercom), while still being able to take advantage of the Expo SDK modules like Updates and Notifications. The bare workflow offered the best of both worlds and was the right solution for us.
The process: Reviewing the techniques we used
SVG + Reanimated
React Native Gesture Handler + Reanimated
React Native Gesture Handler and Reanimated are a match made in heaven. In combination with Redash, we had the perfect toolkit to build a bespoke solution for the schedule infinite horizontal scroll (with snap points based on user preferences) as well as the people sidebar swipe interaction. The Interpolate API was used to ensure task labels always stay visible within the viewport.
React Navigation + BottomSheet
The card screens have two different variations: with full height or as bottom sheet. When rendering those screens with the stack navigator from React Navigation, we define a custom cardStyleInterpolator for each case, while the bottom sheet variant uses a BottomSheet wrapper internally.
Recycling is the best way to render long lists without compromising performance or memory efficiency. We used this technique in the schedule vertical scroll with RecyclerListView and when virtualizing cell items (e.g., tasks, time offs, statuses, etc.) in the horizontal swipe.
The outcome: A 3x faster mobile app that’s loved by our users
We are extremely pleased with the new foundation and are confident that it provides a much better experience across the board for both our users and our software and QA engineers.
We're also excited to continue working on making the experience as intuitive and seemless as possible, regardless of the device being used to access Float. Recent features like dark mode and a magic link sign in option are great examples of our ongoing effort to build the best app possible for our users.
Did you know that Float is hiring? If you are interested in joining our team, please apply for one of our open engineering roles.