← All work#ProductPlatform

Component Platform

A reusable, white-label UI platform several products build on — shared components, theming, and configurable structure instead of rebuilding the front end for each new product.

BeforeApp Aown ButtonApp Bown Button · driftedApp Cown Button · +variantThree copies, slowly disagreeing.AfterShared Libraryone definitionApp AApp BApp COne change reaches all three.

Problem

We shipped several products that were meant to feel like a family, and each one rebuilt the same front-end primitives from scratch — buttons, form fields, modals, galleries, the way a map behaves, empty states. Standing up a new product's interface took the better part of a year, most of it spent rebuilding things that already existed a few repositories away.

The copies also drifted. The "same" button had different padding in one app and last year's hover colour in another. A single accessibility fix turned into three separate pieces of work, coordinated by hand, with the products out of sync in between. The cost of making one consistent change grew with every new product we added.

Approach

Rather than copy components between repositories, I built and maintained the shared UI as a real, published package — bundled with Rollup, shipped as CommonJS, ES modules, and TypeScript types, versioned with semantic versioning, and consumed by three separate applications: a customer-facing product, a partner-branded one, and a configurable white-label surface.

Two decisions did most of the work. React and the UI framework were declared as peer dependencies, so each host kept a single copy of the runtime; and appearance was driven by theme tokens, so one component could render distinct brands — the white-label part — without a fork per product. A release pipeline held it together: a changelog, versioned releases, and one reviewed place where shared changes went out.

Key decisions
  • Shared, published component platform
  • Theme-token architecture (white-label)
  • Peer dependencies for the runtime
  • Semantic versioning as a contract
  • Additive-change & deprecation policy

What made it hard

The components themselves were never the hard part. The hard part was that a change to shared code stops being a local edit and becomes a public event for three teams at once — sometimes mid-sprint, sometimes breaking one of them in a way nobody noticed until later. Most of the real work was treating the package as a contract: keeping changes additive, deprecating before removing, and being deliberate about what shipped and when.

Two judgment calls came up constantly. What belongs in the library and what doesn't — say yes to every "general" request and it fills with one-off exceptions; say no too often and teams rebuild locally and the drift comes straight back. And adoption — a platform only pays off if products keep upgrading, so keeping upgrades cheap enough that staying current was the easy path was an ongoing job, not a one-time release.

Trade-offs

Products gave up some local freedom: a component came from the library, so a team worked within its API or requested a change rather than tweaking it in place. Shared changes carried coordination overhead — versioning, communicating impact, keeping things additive — which is the right price for a primitive used everywhere and the wrong price for something still in flux. And the platform became a dependency that needed an owner; without one it would have belonged to no one and quietly rotted. It was also an up-front investment that didn't pay back on the first product — it paid back on the third, and every one after.

Result

New product, before
~1 year
New product, after
< 6 weeks

Standing up the front end for a new product dropped from roughly a year to under six weeks: the new app inherited a working, themed, accessible component system instead of rebuilding one, and the team's effort went into what made the product different. Consistency became the default rather than something chased after the fact — a shared change landed once and reached every product — and cross-cutting work like accessibility sweeps became a single versioned change that teams adopted on their own schedule.

What I learned

The transferable lesson — why a shared library is better understood as a product with consumers than as a folder of shared code — is in the companion article. What this specific platform taught me is narrower and more practical: the engineering was the easy half. The half that decided whether it worked was organizational — versioning honestly, defending the boundary against one-off exceptions, and keeping upgrades cheap enough that teams chose to stay current.

What I worked on
Reusable component architectureWhite-label & themingDesign-system foundationsCross-product UI platformReact · TypeScript