Build1 publisher3 min readPublished
GitHub cut server-side render time 55% by moving Primer off runtime CSS-in-JS
GitHub says moving every Primer component from runtime CSS-in-JS to CSS Modules cut server-side render time by 55% and component initialization by 25%. The figures come from GitHub's own pages without baselines, so they hold elsewhere only where styling work grows with component count as it did at GitHub.
The Engineer · Build desk

What happened
- In 2023 the number of components on some GitHub pages surged, and Primer's runtime CSS-in-JS started slowing page loads, server rendering and style updates.
- Primer's team replaced it with CSS Modules, a switch that meant updating every Primer component and every GitHub component styled with the old technique.
- GitHub says that once all Primer components were migrated in December 2024, pages took 55% less time to server-render and components 25% less time to initialize.
- Primer's sx prop, which let teams restyle a component with an inline object, was one of the trickiest parts of removing CSS-in-JS, according to GitHub.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- cost Static stylesheets move GitHub's styling cost out of render time and into CSS bytes shipped with every page's HTML, a trade GitHub made for its component-heavy pages.
- decision A team with lighter pages than GitHub's should profile its own style runtime before a rewrite, since the 55% server saving came from pages whose component counts had surged.
- capability A per-component flag gated on identical visual snapshots lets a design system swap its styling engine one component at a time while production keeps the old path available.
- constraint Until product teams convert their own components, GitHub runs two styling systems and still pays runtime style cost wherever the old one renders.
GitHub titled the post "Improving site performance by shipping more CSS" [18], the rare performance headline that asks for more bytes. The problem it solved sat in the render path. Primer's CSS-in-JS did styling work while a page rendered. In the browser, styles were initialized during the initial load [3]. On the server, rendering had to collect styles, a job that had shifted there from the client [4]. The work of updating styles grew with the number of components on the page [5].
CSS Modules take that work out of the render path. Styles live in a CSS file beside the component's JavaScript [6]. Class names are local by default, and the post says that prevents some of the collisions global selectors cause [6]. The files roll up into ordinary stylesheets sent with the page's HTML. No styling code runs on the client or the server [7].
The migration had to run while GitHub kept using both systems. Updates to Primer components could not break any GitHub usage, and the old technique had to keep working for components not yet converted [9]. Each component went through the same four steps [10]:
1. Write a new file translating the component's existing styles to CSS Modules. 2. Put the component behind a feature flag that switches between old and new styles. 3. Run the existing visual regression tests and require identical snapshots from both paths. 4. Roll the flag out to the Primer team, then GitHub staff, then all users.
Step 3 is the part I would copy. With identical snapshots required from both paths, the flag switches between two engines that render the same pixels, so what the rollout measured was performance. Josh Black wrote in the post, "The use of feature flags allowed us to do this migration safely while giving us clear signals on the performance benefits of CSS Modules." [11] According to the post, the loop flagged issues early as Primer kept shipping changes to GitHub [14].
The results describe GitHub's pages. The server figure means each render now takes 0.45 of its old time [19]. That reduction is 2.2 times the client-side one in percentage terms [20]. I think the server number matters more to anyone paying for render capacity. The post does not give baseline times, which pages were measured, or how many components they carried. For the gain to transfer, a page needs the condition GitHub had: enough components that per-component style work is a large share of render time [2]. On a page with few components I would expect a smaller gain. I would profile the style runtime before committing to a rewrite on GitHub's scale.
Outside Primer, the old runtime was still in use when the post was written [9][17]. The sx prop's appeal was typed styling: the post credits it with excellent TypeScript support integrated with GitHub's Design Tokens [16]. Once Primer's own components were done, the team began asking whether converting other parts of GitHub would bring similar wins, and how long it would take to drop CSS-in-JS support across the company [17].
What to watch
- A GitHub date, or a count of remaining components, for dropping CSS-in-JS support across the company.
- What Primer offers teams in place of the sx prop's typed, token-aware inline styling.
- Baseline render times and component counts behind the 55% and 25% figures, which would show how the gain scales with page size.