How to fix styled-components ssr issues in Gatsby production build.
In the development environment everything was working fine, but after building the Gatsby site, an annoying flickering effect appeared where styles from styled-components where being applied with delay, like shown in the gif above.
Although that's the problem I personally encountered, it could also happen that your app is ignoring some or all styles from styled-components. We'll see why this happens and how to troubleshoot it.
→ My initial approach 👎
My initial thoughts were: "what the hell is going on!¿?@!$¿¡!". Finding out that people had similar issues with Gatsby having different behaviour between development and production builds (e.g. [1], [2]), and that it was related to rehydration, was a relief.
If you want to know more about Gatsby and hydration, you may have a look to it at Gatsby Docs [3].
Then I discovered an awesome post about rehydration problems server-side-rendering (ssr) React apps like Gatsby [4] and I thought: "hmmm... this should work for me". And it did! It looked like a rehydration problem, so if I waited for the client (React) to be ready, then the styles would be applied when the content is painted on the screen. Nice, problem solved!
Though this approach is cool if we intend to solve small rehydration issues in some specific components, it is not for the entire app. If you do that, you're taking no advantage of ssr, which very briefly is 1) bad for performance and 2) bad for SEO [5].
So, after quite a bit of debugging, research and gaining more knowledge on Gatsby, I was able to solve the problem in a more elegant way (in fact, the proper way of solving it) while taking advantage of ssr.
Next you'll find different solutions I tried, which seem to be useful for most common issues users have with styled-components and Gatsby.
Check you have gatsby-plugin-styled-components
& babel-plugin-styled-components
installed. Styled-components will not work without them in ssr [6].
Check that each css property ends with a semicolon and there are no css typos like closing a line
with double semicolon ;;
[7].
Check your are properly using wrapRootElement
and wrapPageElement
APIs.
The first is "useful to set up any Provider components that will wrap
your application" and the second one "set persistent UI elements around pages". See Gatsby docs for more info about those APIs [8].
If you use wrapRootElement
and wrapPageElement
, don't forget to implement it both in gatsby-browser.js
and gatsby-ssr.js
.
In my case, checking solutions 2 and 3 helped me to fix the issue. As an example, my gatsby-browser.js
and gatsby-ssr.js
ended up sharing the following code:
export const wrapRootElement = ({element}) => (// Context API provider<AppProvider>{element}</AppProvider>)export const wrapPageElement = ({ element }) => (<><GlobalStyles /><Navbar /><MDXStyles>{element}</MDXStyles><ScrollToTop /><Footer /></>);
The problem described seems to be a very common pitfall in the first steps in Gatsby, and it can become difficult and tedious to debug and find the right solution. So in case you came here looking for help, I hope this post has been helpful to you.
Finally, facing this type of issues teaches how not to overestimate development environments, because even if everything works fine in development, many things can go wrong in production!
[1] https://github.com/gatsbyjs/gatsby/issues/10706
[2] https://github.com/gatsbyjs/gatsby/discussions/17914
[3] https://www.gatsbyjs.com/docs/glossary/hydration/
[4] https://www.joshwcomeau.com/react/the-perils-of-rehydration/
[5] https://medium.com/walmartglobaltech/the-benefits-of-server-side-rendering-over-client-side-rendering-5d07ff2cefe8
[6] https://www.gatsbyjs.com/plugins/gatsby-plugin-styled-components/
[7] https://github.com/gatsbyjs/gatsby/issues/16039
[8] https://www.gatsbyjs.com/docs/reference/config-files/gatsby-ssr/#wrapPageElement
📣If you've found something missing, something wrong or something that could be improved in this post, please drop a pull request in the site's repo or email me at contact@davidmitjana.me.