One codebase, three brands: How Awaze shipped 3 enterprise apps fast with Expo

One codebase, three brands: How Awaze shipped 3 enterprise apps fast with Expo

This is a guest post from Paul Brickles - a developer, Engineering Manager at Awaze , hot beverage enthusiast, and trumpet player residing in North West England. ...

In mid-2024, I was presented with an opportunity to join Awaze, Europe’s leading managed vacation rentals group with around 6 million holidaymakers a year. The challenge was to recruit an engineering team from scratch and lead the build of a brand-new mobile app for guests. Naturally, I jumped at this exciting proposition, but there were a few catches…

First, we didn’t need to build one guest-facing app, we needed three, each tied to a different major brand. In the UK, we needed to launch apps for cottages.com and Hoseasons, plus an app for our largest European brand, Novasol, which needed to support more than 10 language locales.

Second, because of the seasonal nature of the vacation rentals business in Europe, we had about 10 months to get them shipped.

And third, while I’d worked closely with mobile teams before, this was my first time leading one from start to finish, including hiring, delivery, and launch.

This is the story of how we built a team, shipped fast inside a large enterprise, and leaned on the power of React Native and Expo to make it possible.

Article content

Building the right team, fast

The first task was to figure out what kind of developers we needed to meet this opportunity head-on and, by extension, what kind of tech they’d need to be comfortable with. We wanted to build cross-platform apps and potentially make use of the fact that our web products were built in React, so React Native quickly became a clear front runner.

However, it was also necessary to think beyond the UI. Our architecture would require an aggregation layer to interface with the many services and legacy systems scattered across the organization.

That meant we weren’t just looking for mobile specialists. Instead, we needed engineers with a breadth of experience — developers and testers familiar with React Native, but also the kind of T-shaped skill sets that could handle an AWS-based backend and the realities of integration and testing in a large enterprise environment.

Fortunately, the North West of England has a thriving tech scene and we had an exciting proposition to get people onboard. By January, we’d grown to a team of five engineers and two QA engineers. That left us with just five months to build and ship our product in time for the peak booking season in May and June.

Choosing Expo and keeping it simple

Now that we were coming together as a team and establishing our ways of working, there was a lot to consider. As an Engineering Manager, my focus is often on how to get the most out of the team without burdening them with unnecessary cognitive load.

When it came to choosing platforms and tooling, I was conscious that we needed something that would maximize delivery speed without draining mental energy on setup or infrastructure. We were building multiple apps, so we needed to avoid getting bogged down in the complexity of juggling several native projects or reinventing the wheel on deployment.

For us, Expo was the obvious choice to scaffold our apps — and even before the team was in place, it allowed me to experiment and prototype using Expo Go.

Even at that early stage, it was clear we could manage all three brands from a single codebase, using, in most cases, a single environment variable to switch between app configurations. Once we got our first development builds running, it became obvious that Expo’s tooling would make it much easier to build and test across multiple brands without duplicating effort or adding unnecessary complexity.

Reducing cognitive load with Expo and EAS

As the team settled in and we found our rhythm, the next step was to choose tooling that would let us move quickly without introducing unnecessary complexity. After looking around in the marketplace, we quickly saw that using Expo Application Services (EAS) could help us do that.

EAS Build allowed us to generate separate binaries for each of our branded apps, using custom build profiles and environment variables to manage differences in configuration — such as building against different production environments. This meant engineers could trigger development builds or updates for cottages.com, Hoseasons, or Novasol simply by opening a single PR.

To support this, we configured each app using a custom config.ts file that exports brand-specific configuration based on a single EXPO_PUBLIC_BRAND environment variable. This is consumed by our app.config.ts file, which uses it to generate a tailored ExpoConfig object for each brand. With this pattern, we can easily control things like app names, splash screens, and project IDs — all from a shared structure.

Here’s an example of what the config for Hoseasons looks like:

// config/config.ts

const HOSEASONS_CONFIG: Partial<ExpoConfig> = {
  name: getAppName("hoseasons", APP_ENV),
  slug: "hoseasons",
  scheme: "hoseasons",
  orientation: "portrait",
  updates: {
    url: `https://u.expo.dev/${projectIdMap["hoseasons"]}`,
  },
  ios: {
    icon: "./assets/images/icon-hoseasons.png",
    splash: {
      image: "./assets/images/splash-ios-hoseasons.png",
      imageWidth: 344,
      backgroundColor: "#1B8572",
    },
    supportsTablet: false,
    bundleIdentifier: getBundleIdentifier("hoseasons", APP_ENV),
    googleServicesFile: getGoogleServicePath("hoseasons", APP_ENV),
  },
  android: {
    package: getBundleIdentifier("hoseasons", APP_ENV),
    icon: "./assets/images/icon-hoseasons.png",
    splash: {
      image: "./assets/images/splash-android-hoseasons.png",
      resizeMode: "native",
      backgroundColor: "#1B8572",
    },
  },
  extra: {
    router: {
      origin: false,
    },
    eas: {
      projectId: projectIdMap["hoseasons"],
    },
  },
};        

We also made extensive use of EAS Update, particularly for internal testing and QA. Rather than creating new builds of all three apps with every pull request — which quickly became too heavy — we used EAS’s fingerprinting mechanism to push updates over-the-air. This gave us fast feedback loops and kept internal testers working with the latest features without needing to install a new build.

For a team trying to move fast without cutting corners, this tooling helped keep our cognitive load low and throughput high. Developers could stay focused on product features rather than worrying about too many build scripts or release processes, making a real difference to our delivery pace.

Article content

Future-proofing with Expo Router

Initially, we had a strong business case centered around allowing guests to manage their holiday bookings within the app: we wanted to enhance the post-booking experience, and this is where we began with our native builds.

However, it quickly became clear that if we wanted to deliver an experience guests would recognize as a complete travel app, we also needed to incorporate search and booking functionality. We wanted guests not only to manage their stay but potentially rebook the same property for their next visit, all within the same app flow.

As brands with well-established mobile web experiences, we realized that WebViews were the fastest way to bring this functionality into the app. But we also knew we didn’t want to be tied to them forever. Our long-term goal was to rebuild key parts of the web experience natively, once we were in market.

To support this, we leaned heavily on Expo Router, using it to wrap each WebView inside a native route. This allowed us to treat every embedded web experience as a screen in its own right. By treating each of our embedded web journeys as a native screen inside app/, we preserved deep linking, consistent navigation patterns, locale handling where appropriate for Novasol and the ability to swap in native implementations over time.

// app/(tabs)/explore/search.tsx

import { WebView } from 'react-native-webview';
import { useLocalSearchParams } from "expo-router";

export default function SearchWebView() {  
 const { searchUri } = useLocalSearchParams<{ searchUri: string }>();
 
 const createSearchUri = useCallback(
    () => {
	    //some function that returns the correct uri for the brand/locale
    },
    [locale]
  );
  
  return (
    <WebView 
	    source={{ uri: createSearchUri() }}
	    { ...otherWebviewProps }
    />
  );
}        

Localization at scale

For our European brand, Novasol, we needed to support 14 language variants across a range of markets. On the web, this had traditionally been handled through URL parameters or market-specific domains.

Article content

In the app, we didn’t have the luxury of IP detection, so we turned to the Expo Localization package to read the device’s language settings. From there, we mapped the detected locale to our list of supported languages.

We used this mapping in combination with the i18next library and a set of existing translations sourced from our CMS. This allowed us to serve the correct content inside the app with minimal overhead.

For WebViews, we applied a similar approach: on app open, we used the locale information to build the appropriate URL schema for each sales market, ensuring that guests saw the correct version of the site from the very first screen.

From launch to iteration

Despite the time constraints and some of the complexities of team building and enterprise environments, I’m pleased to say that we delivered all three apps in time for peak holiday season. Getting to market quickly has made our team a revenue stream rather than a cost center, and as any of you working in business will know, that brings new and often exciting challenges ahead.

We have a pretty robust codebase for our apps and services and we’re constantly looking to improve things. For example, we have concurrently built a suite of Maestro tests alongside other apps, and we’re considering how best to run them regularly so we can get fast feedback on any issues. We managed to start our builds just in time to get on the new React Native architecture and we’re planning to upgrade to Expo SDK 53 soon - we’re particularly looking forward to how we can use the new native UI tooling and background tasks that this will provide to upgrade experiences for our guests.

Most of all, we can start to think about what our iterative process looks like for improving our products and the experiences our guests have. For Awaze and our brands to now have access to native capabilities on device, we are already sifting through a raft of ideas for improvements to our offering.

One codebase, three Brands, and a strong start

At Awaze, we took a bold approach: build three branded mobile apps from a single codebase, with a new team, in just over six months. That decision paid off — not just in terms of getting to market quickly, but in creating a platform we can now iterate on, scale, and improve.

Choosing React Native, Expo, and a shared codebase allowed us to keep complexity down while delivering real value fast. But just as important was the mindset: stay pragmatic, reduce friction, and give engineers the space to focus on guest experience over boilerplate and build scripts.

We're proud of what we’ve launched — and now we’re focused on making it even better.

Jyoti Kumari Gupta.

React Native & React.js Developer at Digital Ads Fusion | 💼 Freelance Developer | 🚀 Proficient in Node.js, MongoDB, Firebase, AWS & Tailwind CSS | Next.js.

3mo

Thanks for sharing

Like
Reply

To view or add a comment, sign in

More articles by Expo

Others also viewed

Explore content categories