I set up Prerender.io to cache my website, but when I look at the cached page, it seems to only cache the page layout and not the stylesheet. The result is quite ugly and certainly will affect SEO. The pages look fine in the web browser.
I'm using React with Material UI to create my webpages, as well as the npm package react-router-dom
to go between pages.
I also have a bit of a janky set up for Express to serve my pages. The problem was that if you went to the URL /book
you'd be given the express static 404 page, instead of the React page for /book
. So I redirected all non-extension URLs to index.html using a regex pattern.
app.use("/", (req, res, next) => {
// URLs that don't include extensions are assumed to be pages
// Render them with the /index.html.
const extRegex = /\.\w+\/?$/
if (extRegex.test(req.url) === false) {
// Request url does not have an extension.
req.url = "/index.html"
}
next()
}, express.static(BUILD))
My Prerender.io middleware is placed before this, so that it is passed the unaltered req.url when it is called.
Finally, sometimes I see the warning "Layout was forced before the page was fully loaded. If stylesheets are not yet loaded this may cause a flash of unstyled content." in the console on Firefox. I have not been able to figure out why this happens.
Any help is appreciated! Thank you.
I found a solution. The problem was that the library emotion-js, which is used by Material UI, was injecting CSS using Javascript. The CSS would not show up in the DOM however, so when the page was cached it would not include the CSS.
The solution to this is to set the "speedy" setting of emotion-js to false when it detects that the page is being server-side rendered. I do this by checking the user agent, and whether it contains the string "prerender". Then, to set speedy to false when working in React you need to use a CacheProvider and create a cache.
Here's an outline of the solution:
App.js
import { CacheProvider } from '@emotion/react';
import createCache from "@emotion/cache";
// Check if the user agent is Prerender. Speedy should be false if so.
const speedy = navigator.userAgent.toLowerCase().indexOf("prerender") === -1;
// Create an emotion cache
const emotionCache = createCache({
key: "emotion-cache",
speedy: speedy,
})
function App() {
return <CacheProvider value={emotionCache}>
...
</CacheProvider>
}
export default App