bubble-icon
Skip to main content

Multiple themes/brands with multiple websites

When deciding how to develop multiple themes/brands, there are several gradations of customizability.

Deciding on how to approach this is a trade-off between the amount of customizability and the amount of code duplication.

Gradations of modularization/thematization

Level 1: One codebase, one build. Differences are configured in GraphCommerceStorefrontConfig

  • Advantage: Simple, everything is easy to configure in the config.
  • Advantage: Upgrade once
  • Disadvantage: Not tree shakable (very large differences between brands can become problematic)

This requires discipline from the developer that we maintain the 'Single responsibility' principle for components. Components that actually serve two completely separate functionalities depending on the brand are not desirable.

Create a Config.graphqls in your graphql directory with the following contents:

enum ThemeName {
  BRAND_1
  BRAND_2
}

extend input GraphCommerceStorefrontConfig {
  themeName: ThemeName!
}

Applying a different theme based on the store configuration:

// in _app.tsx
const { themeName } = useStorefrontConfig()
const theme = useMemo(() => {
  if (themeName === 'BRAND_1') {
    const brand1Palette = ...
    return createThemeWithPalette(brand1Palette)
  }
}, [domain])

Applying a different component based on the store configuration:

export function MyComponent() {
  const { themeName } = useStorefrontConfig()

  if (themeName === 'BRAND_1') return <Brand1Component>
  return <Brand2Component {...}/>
}

Level 2: One codebase, multiple builds. Differences are configured in GraphCommerceConfig

  • Advantage: Tree shakable
  • Advantage: Upgrade once
  • Disadvantage: Multiple builds
  • Disadvantage: Multiple deployments

Create a Config.graphqls in your graphql directory with the following contents:

enum ThemeName {
  BRAND_1
  BRAND_2
}

extend input GraphCommerceConfig {
  themeName: ThemeName!
}

During deploy you'd have to set the theme that is being build: GC_THEME_NAME=BRAND_1.

Create a separate theme for each brand:

let theme: Theme
if (import.meta.graphCommerce.theme === 'BRAND_1') {
  const brand1Palette = ...
  theme = createThemeWithPalette(brand1Palette)
} else if (import.meta.graphCommerce.theme === 'BRAND_2') {
  const brand2Palette = ...
  theme = createThemeWithPalette(brand2Palette)
}
theme.components = createOverrides(theme) as Components

export { theme }

Replace/Plugin a component based on the theme:

// plugins/BRAND_1/magento-product/Replace.tsx
export const config: PluginConfig<'themeName'> = {
  type: 'replace',
  module: '@graphcommerce/magento-product',
  ifConfig: ['themeName', 'BRAND_1'],
}

export function ProductListItem(props: ProductListItemProps) {
  //...
}

Now when a new brand is added you can copy-paste the BRAND_1 directory and you should be able to modify the components to your liking.