← Back to blog

How to White-Label a Trading Application: A Developer's Guide

Architecture decisions that make a trading UI themeable by multiple clients - CSS custom properties, runtime theme switching, and isolating client branding from core components.

By Oliver Benns


If you are building a trading platform for multiple clients - brokers, exchanges, or liquidity providers - you will eventually need to white-label it. Each client wants their logo, their colours, and ideally their custom domain. The technical challenge is supporting this without maintaining separate codebases.

What white-labelling actually requires

At minimum, clients expect to customise:

  • Logo and favicon - Their brand identity in the header and browser tab
  • Colour scheme - Primary brand colour applied throughout the interface
  • Typography - Some clients have brand fonts
  • Domain - Trading on trade.clientbrand.com, not your domain
  • Email templates - Notifications that come from their brand

More demanding clients also want:

  • Custom landing pages - Different marketing content per client
  • Feature toggles - Different modules enabled per client
  • Custom terminology - "Wallet" vs "Account", "Spot" vs "Cash"

The architecture

Tenant configuration

Define a tenant configuration type that captures everything that varies between clients:

interface TenantConfig { id: string; name: string; domain: string; theme: { primaryColor: string; primaryForeground: string; logoUrl: string; faviconUrl: string; fontFamily?: string; }; features: { spot: boolean; futures: boolean; margin: boolean; staking: boolean; }; terminology: Record<string, string>; }

Load this configuration at application startup based on the domain or a subdomain identifier.

CSS custom properties as the theming layer

CSS custom properties are the correct abstraction for white-labelling. They cascade through the DOM, can be set at runtime, and require zero JavaScript to apply:

function applyTenantTheme(config: TenantConfig) { const root = document.documentElement; root.style.setProperty("--color-primary", config.theme.primaryColor); root.style.setProperty("--color-primary-foreground", config.theme.primaryForeground); if (config.theme.fontFamily) { root.style.setProperty("--font-sans", config.theme.fontFamily); } }

Your components reference these variables through Tailwind or direct CSS. When the variables change, the entire UI updates without re-rendering a single React component.

Isolating brand assets

Store tenant assets (logos, favicons, custom images) in a structured directory or CDN path:

/tenants /client-a logo.svg favicon.ico og-image.png /client-b logo.svg favicon.ico og-image.png

Reference these through the tenant configuration rather than hardcoding paths.

Feature toggling

Not every client gets every feature. Use the tenant configuration to conditionally render modules:

function TradingLayout({ config }: { config: TenantConfig }) { return ( <Workspace> <ChartPanel /> <OrderBookPanel /> {config.features.futures && <FuturesPanel />} {config.features.margin && <MarginPanel />} {config.features.staking && <StakingPanel />} </Workspace> ); }

For more granular control, use a feature flag service that can be updated without deployments.

Custom terminology

Some clients call the same concept by different names. A translation layer keeps this out of your component logic:

function useTerm(key: string): string { const config = useTenantConfig(); return config.terminology[key] ?? key; } // In a component const walletLabel = useTerm("Account"); // Returns "Wallet" for Client A

Keep the default terminology in the components and override only where the client differs.

Multi-domain deployment

Each client wants their own domain. In Next.js, handle this with middleware:

// middleware.ts export function middleware(request: NextRequest) { const hostname = request.headers.get("host") ?? ""; const tenant = getTenantByDomain(hostname); if (tenant) { request.headers.set("x-tenant-id", tenant.id); } return NextResponse.next({ request }); }

The tenant ID propagates through the request, and server components can load the correct configuration.

Testing white-label configurations

Every tenant configuration is a potential source of bugs. Test each one:

  1. Visual regression tests - Capture screenshots of key pages with each tenant's theme applied. Compare against baselines.
  2. Feature matrix tests - Verify that enabled/disabled features render correctly per tenant.
  3. Theme contrast checks - Ensure that each client's primary colour meets contrast requirements against your surface colours.

Automate these in CI with a matrix build that iterates over tenant configurations.

Common mistakes

  • Forking the codebase per client - This scales to about 3 clients before becoming unmaintainable. Use configuration, not branches.
  • Hardcoding colours in components - Every colour reference must go through the token system. A single hardcoded #3b82f6 will look wrong in every non-default theme.
  • Client-specific logic in core components - If you find yourself writing if (tenant === "clientA"), extract the varying behaviour into the configuration.
  • Forgetting email templates - Users receive emails from the platform. These need to match the client's branding too.

Starting from the right foundation

White-labelling is dramatically easier when the application is built with theming from day one. Retrofitting CSS custom properties onto a codebase with hardcoded colours and inline styles is painful and error-prone.

If you know your platform will serve multiple clients, invest in the token-based theming system early. The marginal cost of making a component theme-aware during development is far lower than refactoring it later.

Kickstart Your Trading Application

Hedge UI is a React starter kit with production-ready trading components, real-time data handling, and customisable layouts — so you can ship faster.

Get Hedge UI