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'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