A practical adoption playbook
Theme first, freeze sprawl, win the shell, then migrate high-traffic seams - a step-by-step path without a big-bang rewrite.
Adopting GDS is not a weekend rewrite. Most Granicus frontends already ship with another UI kit - Chakra UI, Material UI, Ant Design, or a mix. The goal is to converge over time: put the theme in place, win the highest-traffic seams, then retire legacy UI islands when they stop earning their keep.
This playbook is the path we recommend for apps like Admin UI, Blue Haven, Bazaar, and other EHQ (or Granicus) frontends.
Principles
- Ship value continuously - migration work should ride alongside product delivery, not freeze it.
- Theme before components - get tokens and
GDSThemeProviderin early so new work looks right even before every control is swapped. - Migrate by seam, not by folder - replace UI at natural boundaries
(shell, forms, dialogs), not “all of
src/componentsthis sprint.” - One system going forward - new screens default to GDS; legacy kits shrink on purpose.
- Design and code move together - use Figma SPECS +
Gds*names so prototypes and PRs speak the same language.
Phase 0 - Align the team
Before installing a package, agree on the basics:
| Decision | Why it matters |
|---|---|
| Owner | Who answers “use GDS or legacy?” for this repo |
| Success metrics | e.g. % of new PRs on GDS, critical flows migrated |
| Non-goals | What you will not rewrite in the first quarter |
| Design contact | Who maintains Figma SPECS usage for the squad |
Publish a short RFC or Confluence note. Ambiguity is what keeps both Chakra and MUI growing in the same app forever.
Phase 1 - Install and theme
Follow Getting started: Artifactory auth, @granicus/gds-core,
peers, and GDSThemeProvider.
Checklist:
- App root (or feature shell) wrapped in
GDSThemeProvider -
CssBaseline(or equivalent) so typography and surfaces reset cleanly - Light/dark strategy decided (system, user toggle, or product default)
- Engineers know to import
Gds*from@granicus/gds-core/..., not raw@mui/materialfor new UI
Done when: a throwaway screen (or Storybook-in-app sandbox) renders a
GdsButton and GdsTextField with correct Granicus tokens.
Phase 2 - Freeze the sprawl
Stop the bleeding before you migrate history:
- New features use GDS by default
- No new dependency on an alternate UI kit for net-new screens
- Exceptions require a short written reason (timeline, missing component)
This phase is mostly process. It is also where you get the biggest ROI: every week of new Chakra/Ant UI is another week of dual maintenance.
Phase 3 - Win the shell
Migrate the chrome users see on every visit:
- App bar / side nav / page headers
- Primary buttons and icon buttons in the shell
- Global feedback (toasts, banners, dialogs used for confirmations)
Why shell first? One successful swap teaches the team the patterns and makes the product feel like GDS immediately - even if deep feature pages still use legacy controls.
Phase 4 - High-traffic components
Pick the controls that appear in almost every flow:
- Buttons - primary, secondary, destructive
- Text fields & selects - search, filters, forms
- Dialogs & alerts - confirms, errors, empty states
- Tabs, chips, tables - list and admin-heavy UIs
For each family:
- Map legacy usage → matching
Gds*component (Components) - Swap in one vertical slice (one form, one settings page)
- Fix spacing/tokens, then expand horizontally
Prefer strangler PRs: old and new can coexist behind a clear import boundary. Avoid mega-PRs that touch 80 files.
Phase 5 - Forms and complex flows
Forms are where libraries diverge most. Approach them deliberately:
- Rebuild new forms on GDS form primitives
- Migrate existing forms when you already touch that flow for product work
- Keep validation and data logic; replace only the presentation layer when possible
If a flow is scheduled for redesign, migrate UI in that redesign - do not migrate twice.
Phase 6 - Retire legacy UI kits
Only after coverage is real:
- Inventory remaining Chakra / MUI / Ant / custom islands
- Tag each: migrate, wrap, or delete (dead UI)
- Remove unused theme providers and global CSS from the old kit
- Drop the dependency when imports hit zero
Celebrate dependency removal in release notes - it is the clearest signal that adoption stuck.
Suggested sequencing for an EHQ-style repo
Week 1-2 Theme + freeze sprawl + shell pilot
Week 3-6 Buttons, fields, dialogs on 1-2 critical journeys
Month 2-3 Expand to lists/tables/filters; migrate on product touch
Ongoing Retire legacy kit; track % GDS in new PRsAdjust for team size. A smaller squad should stay ruthless about shell + one journey before widening.
Anti-patterns to avoid
| Anti-pattern | Better approach |
|---|---|
| Big-bang rewrite branch | Seam-by-seam strangler |
| Wrapping every legacy component forever | Prefer real Gds* swaps for shared primitives |
| Mixing three kits on one new screen | One system per screen going forward |
| Migrating without design | Use Figma SPECS; open a design thread when a pattern is missing |
| “We’ll migrate when GDS is complete” | Adopt what exists; request gaps via the GDS / design team |
When a component is missing
Do not invent a second design system in your repo.
- Check Components and Storybook
- Check Figma SPECS for an existing pattern
- Open a request with the GDS / design team
- If you must ship now, isolate a temporary local control and schedule the GDS replacement
How you know it is working
Useful signals:
- New PRs default to
Gds*imports - Shell and top journeys match Figma SPECS
- Legacy UI kit bundle size / import count trends down
- Design review comments shift from “wrong button” to product questions