Skip to content

Multi-Store Architecture

A single Maho Storefront Worker deployment can serve multiple stores, each with its own theme, component configuration, and isolated catalog data.

Fashion StoreTech Store
Fashion themeTech theme

Store Resolution

Configuration

stores.json

Maps store codes to theme and page config files:

json
{
  "stores": {
    "en": {
      "theme": "maho",
      "pageConfig": "page.json"
    },
    "sv_2": {
      "theme": "tech",
      "pageConfig": "page-tech.json"
    },
    "store_view_3": {
      "theme": "brew-beyond",
      "pageConfig": "page-brew-beyond.json"
    }
  },
  "defaultTheme": "maho",
  "defaultPageConfig": "page.json"
}

Store Registry

The store registry maps hostnames to store codes. It can be stored in KV (key: _stores) or configured as a Wrangler environment variable (DEMO_STORES):

json
{
  "demo.mageaustralia.com.au": "en",
  "demo2.mageaustralia.com.au": "sv_2",
  "cafe.mageaustralia.com.au": "store_view_3"
}

Data Isolation

Each store's data is isolated in KV via key prefixes:

StoreKey ExampleContent
enen:product:tori-tankEnglish store product
sv_2sv_2:product:tori-tankTech store product
enen:configEnglish store config
sv_2sv_2:configTech store config

This ensures:

  • Products can have different prices per store
  • Categories can be structured differently
  • CMS content is store-specific
  • Config (currency, locale) varies per store

Render Context

Before rendering any page, the store context is set:

typescript
// In route handler
const storeCode = getCurrentStoreCode(hostname, storeRegistry);
setRenderStore(storeCode);

// All components now use this store's config
const variant = getVariant('product', 'gallery'); // Uses store's page config

The render context is a module-level variable — set once per request, read by all components during synchronous SSR.

Theme Switching

Each store renders with its theme's CSS custom properties:

html
<!-- Store "sv_2" renders with tech theme -->
<html data-theme="tech">

The [data-theme="tech"] selector activates the tech theme's CSS custom properties, overriding the default :root values. All DaisyUI components and utility classes automatically use the store's colors, fonts, and spacing.

Adding a New Store

  1. Add the store code and configuration to stores.json
  2. Create a theme file (theme-{name}.json) if needed
  3. Create a page config (page-{name}.json) if needed
  4. Add hostname → store code mapping to the store registry
  5. Sync data for the new store: POST /sync (will fetch data for all registered stores)
  6. Rebuild CSS: bun run build:css (generates new theme CSS)
  7. Deploy: bun run deploy

Source: src/index.tsx, src/page-config.ts, stores.json