How we fully migrated github.com away from CSS-in-JS.
The Primer Design System powers many of the experiences you see on GitHub today. From buttons to banners to breadcrumbs, these foundational components are required to be accessible, flexible, and performant across a wide variety of scenarios.
Back in 2023, the number of components on certain pages began to explode. This led to several performance-related challenges with our existing CSS-in-JS solution:
It became clear that the Primer team needed to address the issue at the source. We needed to find an alternative that would completely avoid the client and server costs that we were seeing with our current solution. Most importantly, any alternative we pick would need to work in a way that would avoid any breakage to GitHub during the migration.
Introducing CSS (Modules)
The Primer team found a solution that met all of our criteria: CSS Modules. This format would allow us to do one of our favorite things: write and use native CSS features, while still allowing some amount of the colocation and encapsulation that we had come to expect from CSS-in-JS.
With CSS Modules, styles would be authored in a CSS file alongside the JavaScript source for the component. It would also allow us to treat all class names as local by default, preventing some of the collisions and challenges that can come from global selectors. This format also removes the need for any client or server runtime behavior. Instead, styles would roll up into CSS stylesheets that were sent as part of the HTML for a page.
A gradual march towards CSS Modules
For each component, our plan was to:
- Add a new file that translated existing styles to CSS Modules
- Add the component to a feature flag that would toggle between the new and old styles
- Use existing visual regression tests to verify snapshots were identical
- Gradually roll out the feature flag to our team, then to GitHub staff, and finally to all GitHub users
By December 2024, all components in Primer were migrated over to CSS Modules. We saw performance wins across the board, in particular:
- 55% less time to server-side render a page
- 25% less time for components on a page to initialize
Moving away from CSS-in-JS at GitHub
One of the trickiest parts about removing our CSS-in-JS solution from Primer was due to the usage of the sx prop. This prop was the way to style and customize components from Primer. It represented the best and worst parts of CSS-in-JS:
- Excellent TypeScript support with integration with our Design Tokens
- Co-located with the component
- High runtime cost due to the dynamic nature of inline objects used for
sx - Difficulties scaling as the number of components using
sx on a page grew
The Duality of Primer
While the design system itself was officially off styled-components, a large part of the GitHub codebase itself wasn’t. We created “wrapper” components in a transitive library we called @primer/styled-react so instances using sx could continue while we realized performance gains elsewhere.
Styled Box Zero
The work kicked off April 2025 with a peak of ~7,760 sx props to be migrated; we wouldn’t see it realized until May 2026. A rotation of 8 engineers migrated 6,419 props over the course of 6 months, observing SSR time performance gains ranging from 1% up to 22% in some pages.
By April of 2026, we were able to get down from 895 to 0 sx props in the span of three weeks with a team of two engineers and Copilot coding agents.
Battle of the themes
GitHub supports seven different themes, all of which offer a high contrast mode variation — previously enabled through styled-components. After decoupling theming, we were all-systems go for dependency removal.
All’s well that ends well
GitHub has been running on 100% CSS modules as of June 2026. What looked at first like a CSS migration turned out to be a gradual re-platforming of how GitHub styles, themes, and ships UI at scale.