Why are my Material UI styles missing when using Prerender.io with a React app?
Material UI (Emotion) skips full CSS injection in its "speedy" mode, so prerendered pages render unstyled unless you disable speedy mode when Prerender.io is the requester.
TL;DR
Material UI v5+ uses Emotion for styling, and Emotion's default speedy mode skips full CSS injection into the DOM because it relies on a faster API that prerendering snapshots can't capture. As a result, AI crawlers and search engines see your prerendered pages without their styles. Fix it by detecting Prerender.io's User-Agent on the client side, setting speedy: false in your Emotion cache configuration, and wrapping your app in <CacheProvider>. After deploying, clear and recache the affected pages in your Prerender.io dashboard.
📝 Scope: this guide covers Material UI v5+ (which uses Emotion). Material UI v4 uses JSS, which has a different SSR solution. If you're on v4, follow Material UI's v4 server-side rendering guide instead.
What goes wrong
When you use Emotion-based styling (which is the default in Material UI v5+) in a React app, AI crawlers and search engines may see prerendered pages that have:
- Layout and visual styling missing or partially missing in the prerendered cache.
- Browser console warnings indicating unstyled or partially styled content.
This affects how AI crawlers (GPTBot, ClaudeBot, Perplexity, and others) and search engines (Googlebot, Bingbot) understand and present your content, because unstyled pages often look like broken pages. The fix is configuration-level: tell Emotion to render full styles when Prerender.io is the one requesting the page.
Why this happens
Emotion's speedy mode skips DOM injection
Emotion has a performance optimisation called speedy mode, which uses the CSSStyleSheet.insertRule() browser API to inject styles. This is significantly faster than injecting style elements into the DOM, but the rules don't appear in the rendered HTML. Headless prerendering (which Prerender.io uses) captures the DOM, not the live CSSStyleSheet rules, so the prerendered snapshot is missing all the speedy-injected styles.
Server rendering can miss the SSR path
If your Express.js server isn't configured to route client-side paths (such as /about) through index.html, Emotion doesn't get the chance to inject the full styles on the initial load.
How to fix it
Step 1: detect the Prerender.io User-Agent on the client side.
Inspect navigator.userAgent in your React code to detect Prerender.io. This tells Emotion whether to operate in full DOM-injection mode or in fast in-memory mode:
const speedy = navigator.userAgent.toLowerCase().indexOf("prerender") === -1;
This sets speedy to false when Prerender.io is detected, so Emotion will inject style elements into the DOM that the prerender snapshot can capture.
Step 2: configure the Emotion cache to use that flag.
Update App.js (or wherever you mount the root React component) to create an Emotion cache with the speedy flag and wrap your app in <CacheProvider>:
import { CacheProvider } from '@emotion/react';
import createCache from "@emotion/cache";
const speedy = navigator.userAgent.toLowerCase().indexOf("prerender") === -1;
const emotionCache = createCache({
key: "emotion-cache",
speedy: speedy,
});
function App() {
return (
<CacheProvider value={emotionCache}>
{/* App content here */}
</CacheProvider>
);
}
export default App;
What this does:
CacheProviderwraps your app so Emotion's style injection uses the configured cache.createCachewithspeedy: speedytells Emotion to inject full style elements when Prerender.io is detected, while keeping the fast path for real users.
Step 3: route client-side paths through index.html in your Express server.
Make sure your Express server routes paths without file extensions (such as /about) through index.html so React Router can take over rendering. Otherwise the React app never mounts, the cache provider never runs, and the fix above has nothing to attach to.
app.use(
"/",
(req, res, next) => {
// URLs that don't include extensions are assumed to be routes handled by React Router
const extRegex = /\/[^.]+$/; // Matches URLs that do not contain a period (i.e., no file extension)
if (extRegex.test(req.url)) {
// Append index.html so the React app's client-side router handles the path
req.url += "/index.html";
}
next();
},
express.static(BUILD) // serve static files from the build directory
);
Step 4: clear and recache affected pages.
After deploying the fix, your existing Prerender.io cache still holds the broken (unstyled) snapshots. Open Cache Manager in your Prerender.io dashboard, clear the affected URLs, and let them recache (or trigger a manual recache via the Recache API).
✅ The fix is working when the Prerender.io snapshot of an affected page shows the styled Material UI components, the x-prerender-requestid response header is present on bot-emulated requests, and the prerendered HTML contains <style> tags injected by Emotion.
Verifying the fix
- Use Chrome DevTools to emulate Googlebot's User-Agent on one of the affected pages.
- Reload the page and view the rendered HTML.
- Search the HTML for
<style>tags injected by Emotion. They should be present in the served snapshot. - If you can also view the page via the Prerender.io cache (using the eye icon in Cache Manager), confirm the styles are visually applied.
For the full bot-emulation flow, see how to verify your Prerender.io integration.
Tips for keeping styles intact long-term
- Re-test after every deployment that touches your style setup. Changes to your build, Emotion version, or Material UI version can re-introduce speedy-mode rendering.
- Add User-Agent checks consistently. If you have any other CSS-in-JS layer, similar speedy-style optimisations may exist in those libraries. Apply the same Prerender.io detection pattern.
- Clear and recache after the fix. Cached pages don't update automatically; running a manual cache clear in the dashboard is the fastest way to see the corrected output.
Get support
If you have configured the Emotion cache as above and your Express server routes correctly, but styles are still missing from the prerendered output:
- Email support@prerender.io
- Or click the green Support button in your Prerender.io dashboard and choose Contact Support
To help us diagnose quickly, please include:
- A URL where the issue is visible.
- A screenshot showing the unstyled output.
- Your Material UI and Emotion versions (
npm ls @mui/material @emotion/react). - Your Emotion cache configuration and how you detect the Prerender.io User-Agent.
Related articles
- How to verify your Prerender.io integration
- Node (Express.js) integration guide
- What can I do with the Prerender.io REST API?
- How to purge your cache
- Emotion server-side rendering documentation
- Material UI server-rendering guide
💬 Still need help?
If the Emotion configuration is in place and pages have been recached but styles are still missing, our support team can help.
→ Contact us at support@prerender.io