How `global.css` Gets Injected Across All Routes in SXO
The SXO build system automatically discovers src/pages/global.css and appends it to every route's entry points, then injects the resulting stylesheet link into the HTML <head> at runtime.
In the gc-victor/sxo repository, global styles are shared across every page without requiring manual imports in each component. This automatic injection ensures consistent styling while keeping the developer experience ergonomic.
Entry Point Discovery and Configuration
The injection process begins during the entry-point discovery phase in src/js/esbuild/entry-points-config.js. The build system specifically looks for a file named global.css inside the pages directory.
When found, the relative path is stored and appended to each route's entryPoints array:
// src/js/esbuild/entry-points-config.js
const rel = `${PAGES_RELATIVE_DIR}/global.css`; // ← global stylesheet
if (await fileExists(rel)) {
// Append to each route's entry points
route.entryPoints.push(rel);
}
This ensures that every route's client bundle includes the global CSS file as a shared dependency.
Bundling and Asset Generation
During the esbuild bundling phase, the build receives the list of entry points per route. Because every route now references the same global.css file, esbuild deduplicates it and produces a single hashed CSS asset (for example, global.XYZ789.css).
The manifest entry for each route is updated in src/js/esbuild/esbuild-metafile.plugin.js to include this CSS asset in route.assets.css. This metadata persists across both development and production builds, ensuring the stylesheet is tracked as a dependency for every page.
Runtime HTML Injection
When a page is rendered—whether in development, production, or static generation—the server calls injectAssets from src/js/server/utils/inject-assets.js. This utility inserts the CSS link tags before the closing </head> tag for every entry listed in route.assets.css.
The injectCss function handles the actual string manipulation:
// src/js/server/utils/inject-assets.js
export function injectAssets(html, assets, publicPath) {
let result = html;
if (assets.css?.length) result = injectCss(result, assets.css, publicPath);
if (assets.js?.length) result = injectJs(result, assets.js, publicPath);
return result;
}
// injectCss inserts <link> before </head>
export function injectCss(html, css, publicPath) {
const tags = css.map(
href => `<link rel="stylesheet" href="${publicPath}${href}">`
).join("");
return html.replace(/<\/head>/i, tags + "</head>");
}
Because global.css is included in route.assets.css for every route, the production handler in src/js/server/prod/core-handler.js automatically injects it:
// src/js/server/prod/core-handler.js (excerpt)
const normalizedPublicPath = normalizePublicPath(PUBLIC_PATH);
page = injectAssets(page, route.assets, normalizedPublicPath);
Test Verification
Unit tests confirm this behavior at two critical points. The entry-point discovery tests in src/js/esbuild/entry-points-config.test.js verify that each discovered route contains the global.css path in its entryPoints array. Additionally, runtime tests in src/js/server/utils/tests/inject-assets.runtime.test.js validate that the generated HTML includes the CSS <link> tag before the closing </head> tag.
Summary
- Discovery: The build system checks for
src/pages/global.cssduring entry-point configuration and appends it to every route's entry points. - Bundling: esbuild deduplicates the global CSS into a single hashed asset referenced in the route manifest.
- Injection: The
injectAssetsutility inserts<link>tags into the HTML<head>at runtime for every route. - Consistency: This pipeline ensures
global.cssloads on every page without manual imports or configuration.
Frequently Asked Questions
Where should I place the global.css file?
Place your global.css file directly in the src/pages/ directory. The build system specifically looks for src/pages/global.css during the entry-point discovery phase in src/js/esbuild/entry-points-config.js.
Is the global CSS file deduplicated across routes?
Yes. Since every route includes the same global.css entry point, esbuild recognizes the shared dependency and produces a single CSS asset. Each route's manifest references this same hashed file, ensuring browsers cache it efficiently across page navigations.
Can I disable automatic global CSS injection?
The current implementation in gc-victor/sxo does not provide a configuration flag to disable this behavior. To prevent automatic injection, you would need to either remove the src/pages/global.css file or modify the entry-point discovery logic in src/js/esbuild/entry-points-config.js.
How does injection differ between development and production?
The injection mechanism remains consistent across environments. Both development and production builds use the same injectAssets function from src/js/server/utils/inject-assets.js to place CSS links before the </head> tag. The primary difference lies in asset hashing and caching headers, not in the injection logic itself.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →