FleetbaseFleetbase

Extending

Replace any screen of the Storefront App, select built-in screen variants, and add your own screens, routes and tabs — all from the build's brand folder, without forking the app's code.

Extending

The app's navigators refer to screens by a stable screen id, and a registry resolves each id to the app's own screen, a build's replacement, or a selected variant. A build changes screens from brand/storefront.extensions.ts without touching the navigators, so it keeps taking new releases as a plain merge.

NeedWhere
Replace a screenbrand/storefront.extensions.ts → screens
Choose a built-in variantthe brand file → screens, or screenVariants in the extensions file
Add screens (routes)brand/storefront.extensions.ts → routes
Add tabsbrand/storefront.extensions.ts → tabs
Change or add copybrand/translations/<locale>.json

Replacements are ordinary modules compiled into the build. The app never downloads or runs code from an API; configuration can only select variants that are already compiled in.

Replacing a screen

Write the screen, for example brand/screens/HomeScreen.tsx:

import React from 'react';
import { YStack } from 'tamagui';
import type { StorefrontScreen } from '../../src/extensions';
import { UIText } from '../../src/ui';

const HomeScreen: StorefrontScreen<'store.home'> = ({ route, navigation, DefaultScreen }) => (
    <YStack flex={1}>
        <UIText variant='title'>Welcome</UIText>
        {/* Optional: reuse the app's own screen below yours */}
        <DefaultScreen route={route} navigation={navigation} />
    </YStack>
);

export default HomeScreen;

Register it:

// brand/storefront.extensions.ts
import { defineStorefrontExtensions } from '../src/extensions';

export default defineStorefrontExtensions({
    screens: {
        'store.home': { load: () => import('./screens/HomeScreen') },
    },
});

Routes, deep links, sign-in guards and headers stay as they were. HomeScreen.web.tsx and HomeScreen.native.tsx work as usual, and platforms: ['ios', 'android'] applies a replacement on some platforms only.

Variants

A replacement (or the app itself) can offer named variants, chosen in the brand file:

// brand/storefront.extensions.ts
screens: {
    'store.home': {
        load: () => import('./screens/HomeScreen'),
        variants: { editorial: () => import('./screens/EditorialHomeScreen') },
    },
},

// brand/storefront.brand.ts
screens: { 'store.home': { variant: 'editorial' } },

The app's built-in variants are chosen the same way, for example the Network shop home: screens: { 'network.home': { variant: 'shop' } }. An unknown variant falls back to the replacement, or to the app's screen.

Adding screens and tabs

import { defineStorefrontExtensions } from '../src/extensions';
import { faGift } from '@fortawesome/free-solid-svg-icons';

export default defineStorefrontExtensions({
    routes: {
        Rewards: { load: () => import('./screens/RewardsScreen'), path: 'rewards' },
        RewardDetail: { load: () => import('./screens/RewardDetailScreen'), path: 'rewards/:rewardId', presentation: 'modal' },
    },
    tabs: {
        RewardsTab: { initialRoute: 'Rewards', icon: faGift, labelKey: 'Brand.tabs.rewards', editions: ['store'] },
    },
});

Routes

  • Each route is added to every stack of its editions (editions, both by default), so navigation.navigate('RewardDetail', { rewardId }) works from any screen and opens in the current tab.
  • Names are PascalCase. A name that matches one of the app's routes is skipped with a warning; replace app screens with screens instead.
  • path makes the route deep-linkable. presentation: 'modal' presents it over the current screen.
  • A route that fails to load or throws shows an error view in its place instead of crashing the app.

Tabs

  • A tab opens one of your routes (initialRoute). Its stack also has every custom route and the edition's shared screens (products, offers, locations; in a Network also stores, categories, reviews and notifications).
  • icon is a FontAwesome icon or a function ({ color, size, focused }) => ReactNode. labelKey is a translation key; label is plain text or { en: 'Rewards', mn: '…' }.
  • Place it with navigation.store.tabs or navigation.network.tabs in the brand file; otherwise it goes at position, or just before Cart. initial: true opens the app on it.

Copy

brand/translations/<locale>.json is merged over the app's translations: change any string, or add keys for your own screens (for example under a Brand namespace). A file for a language the app doesn't ship adds it; offer it with AVAILABLE_LOCALES.

What your screens can use

Client screens may rely on:

  • src/extensions: types, defineStorefrontExtensions, screen ids and their params
  • src/ui: the app's components, tokens and display helpers
  • src/branding: defineBranding, useBranding
  • the hooks use-cart, use-cart-summary, use-storefront, use-current-location, use-food-trucks, use-store-locations, use-customer-coordinates, and useStorefrontRuntime, useLanguage, useAuth

Breaking changes to these, to screen ids and params, and to branding options are listed in the release notes. Anything else in src/ may change between releases. Replacements are typed (StorefrontScreen<'store.home'>), so a type check after a merge points at what needs updating.

When something fails

  • A replacement that fails to load, has no default export or throws while rendering is swapped for the app's own screen, and the error is logged.
  • If that fails too, a "Something went wrong" view is shown.
  • In development, unknown screen ids and malformed entries stop the app at start so you notice; in release builds they are skipped with a warning.

Screen ids

Screen idWhat it isKey params
store.homeThe store page (a single store's home; a member store in a Network)storeId?, categoryId?
store.catalogEvery category of the storenone
store.searchSearch in the storenone
store.mapThe store's locations on a mapnone
store.infoThe store's hours, locations and contactstore, storeLocation?
catalog.categoryJumps to a category section of the store pagecategoryId?
foodTrucks.homeThe trucks screen (map and list)none
foodTrucks.searchSearch across trucks and storesnone
foodTrucks.menuA truck's menufoodTruckId, truck?, categoryId?
product.detailA product, with options, booking and add to cartproductId, storeId?, product?
cart, cart.itemThe cart and one item in itnone, cartItem?
checkoutCheckout for the build's payment gatewaynone
order.detailAn order, with tracking, chat and receiptorderId
order.receiptAn order's receiptorder
order.historyActive and past ordersnone
order.chatChat with the store or the driver about an orderorderId
account.profileThe account homenone
account.detailsEdit profile (photo, name, email, phone)none
auth.login, auth.createAccountSign in and create an accountredirectTo?
network.homeA Network's home (variant shop: the shop home)none
network.directoryMember stores by category or tagcategoryId?, tags?
network.search, network.mapSearch across stores; every store on a mapnone
network.storeA member storestoreId
network.productA product of a member storeproductId, storeId?
network.shopCategoriesEvery category of the main store, as a gridstoreId?
network.shopCategoryOne category of the main store, with subcategories and offerscategoryId, storeId?
reviews.list, reviews.writeA store's reviews; writing onestoreId
offers.list, offers.detailEvery live offer; one offernone, offerId
notifications.inbox, notifications.settingsThe notifications inbox and its settingsnone

The boot, location permission, payment gateway, phone verification and account deletion screens can't be replaced.

Extending | Fleetbase