Journey
MilestoneDesign SystemsChangesetsrelease

The catalog that kept us honest

For a year, all of our UI lived inside a single app. This week it moved into a home of its own at ui.pangaea.id: one design system, split into thirty-seven versioned packages, with a live catalog you can browse. The milestone wasn't the code — the code had existed for years. The milestone was deciding to treat our own UI like a product.

The boring middle

Extraction is the work nobody claps for. Pulling rv-* out of one app meant dozens of small packages, each with React as a peer dependency, its own CSS, and its own package.json, all sitting in one monorepo. There's no dazzling demo at the end of it — just hygiene.

The obvious temptation: skip the catalog, "just publish the packages", move on. We didn't. And the catalog is exactly what saved us.

The catalog kept us honest

You can't put a half-extracted component on a page that has to actually render, be drivable with controls, and have its usage copyable in full. If a package quietly still leaned on something in the old app, its catalog page broke — right in front of us.

That's what the catalog is for: it's the place every component has to prove it truly stands alone. A package that passes in the catalog is a package that's already clean. The catalog gave us failure that was loud and fast — far better than finding a broken package months later inside someone else's app.

The chore we didn't see coming

The surprise wasn't in the packages — it was in the release. The first release took two manual merges: your code PR, then a "Version PR" that bumps the versions and writes the changelog. That second merge looks trivial. But a small tax like that is exactly what quietly makes you stop releasing — you defer one fix, then the next, until the design system goes stale.

So we treated release ergonomics as a feature, not an afterthought. Now one changeset landing on main triggers one CI/CD run that opens the Version PR, auto-merges it, then publishes — all in one pass. The only manual step left is writing the changeset: one sentence about what changed.

The real work wasn't packaging the components — it was making the next release nearly frictionless. If releasing feels like a chore, you'll stop releasing.

what we learned this week

What we'd tell ourselves on day one

Do

  • Build the live catalog first — it's what forces the extraction to be honest, not a reward at the end
  • Extract one component end to end before you extract thirty-seven
  • Treat release ergonomics as a feature — the last manual step is a tax you pay forever

Don't

  • Defer the catalog as "documentation we'll write later" — without it, broken packages slip through
  • Ship a two-merge release and hope you'll get used to it — you'll just stop releasing
  • Extract every component before one catalog page proves the shape

How it's built, end to end — the monorepo, the deploy to Cloudflare Pages, and Changesets — is in the article: Shipping a design system at ui.pangaea.id. And why we chose the boring versioned packages over the exciting runtime module federation is in the earlier note: The federation we didn't build.

Sources

  1. ui.pangaea.id — the Pangaea design system catalog
  2. labspangaea/pangaea.ui — the repository