I won’t bore you with all the red-herring rabbit-holes I went down, but eventually I found myself comparing 2 versions of the site deployed as 2 separate Heroku apps. Because I was lazy, only one of them was using with the CloudFlare CDN, as the real production site would be… and Bingo! The non-CDN’d site was working just fine, but the one served behind CloudFlare was having trouble.
So what was going on? For those of you with great eyesight, I invite you to compare the source code of both versions of the site:
Can’t Spot the difference? I’ll zoom and enhance:
It turns out that the way React handles components whose render method return a
null are drawn as commented-out placeholders like the above. This way, if the state of that component changes and has something to render, we know where in the document it belongs. If that comment is suddenly missing… 💥!
If you have a vanilla render-only-in-the-browser app, you probably would never find yourself looking at the source of your initial page, as it will be very empty. However, Next.JS provides both server-side and client side rendering for each page. This means that your initial request to the server will return a
hydrated version of the page. This is better for SEO, page speed, etc.
The trouble happens when you pipe your site though CLoudFlare, and have their auto-minified options on:
These options are normally great… unless the comments in your HTML have semantic value! Which they do here.
The solution is simple: just turn off auto-minify. You can probably trust your webpack/babel pipeline to produce efficient HTML, so this extra check is likely not necessary.
But… this raises a question: Is it OK that React uses comments in this way? Should, perhaps React be using DIVs which are hidden and empty instead? To me, commented code should always be safe to remove… and it looks like CloudFlare thinks so too.