One line of JavaScript disabled server rendering on 190 pages

oujan1 pts0 comments

One line of JavaScript disabled server rendering on 190 pages | WatchNext<br>Select Location

Select an available country to view real-time live shows from that region.

Menu

Set dark theme

Change the theme to Dark mode

Browse our website, visit different pages and explore your favorite shows.

HomeLiveUpcomingFavorite<br>ListJoin

Log In

New Episodes<br>Turn off new-episode notifications

You're all caught up.

Forgot your password?<br>Log In<br>or

Create AccountAn account is optional — it syncs your favorites across devices. By creating one you agree to our Terms and Privacy Policy.

← All postsOne line of JavaScript disabled server rendering on 190 pages<br>Google reported soft 404s across almost every show page on this site — months after we had deliberately added proper metadata to all of them. The cause was a single property read during render, and the reason it went unnoticed for so long is that this particular failure is invisible from a browser.<br>18 August 2026 · 6 min read · WatchNext<br>A while ago we did the SEO work properly. Every show page got a real title and description instead of the site-wide default, an Open Graph image, a sitemap that pulls in the shows people actually search for. It took a day. It worked — we checked the pages, the tags were there, the previews rendered.<br>Months later Google Search Console reported that roughly 190 of those pages were soft 404s : URLs that return a success status but look, to a crawler, like an error page.<br>The obvious readings were all wrong. The pages returned 200. They were not empty, not duplicated, not thin. Open any of them and you get a show, a poster, a trailer, a cast list, an episode list. They were, by any measure available in a browser, fine.<br>Looking at what was actually sent<br>The thing we had never done was look at the HTML the server sent, as opposed to the page the browser ended up showing. Those are different documents, and only the first one is what a crawler sees first.<br>Stripping the tags out of the served HTML for a show page left one character of body text.<br>Not a truncated page. Not a slow page. The server was sending an empty shell and the entire visible site was being assembled in the browser afterwards. Everything we had verified — the content, the headings, the metadata that Google could in fact read — existed only after JavaScript ran. To a crawler taking a first look, there was nothing on the page at all, which is exactly what a soft 404 means.<br>The cause<br>Somewhere in a context provider that wraps the whole application, one line read the window width during render, to decide whether to use a short label or a long one:<br>const isMobile = window.innerWidth<br>On the server there is no window. That line throws ReferenceError: window is not defined every single time the page is rendered on the server.<br>Here is the part that turns a small mistake into a large one. Next.js does not fail the build, and it does not show an error. It catches the exception, gives up on server rendering that page, and falls back to rendering it in the browser instead. Which works. The user gets the page, slightly later, and nothing anywhere says that anything went wrong.<br>This is a reasonable thing for a framework to do. Degrading to a working page is better than showing a stack trace to a visitor. The cost is that the signal disappears along with the failure.<br>Why every kind of testing we did missed it<br>Look at the list of things that do not catch this, and the shape of the problem becomes clearer.<br>The build passes , because nothing is statically wrong. TypeScript passes , because window.innerWidth is a perfectly well-typed expression. The dev server is quiet. Clicking around the site works , because by then you are in the browser, where window exists. Lighthouse scores fine , because it runs JavaScript. Sharing a link produces a correct preview , because the metadata comes from a separate server function that never touched the broken code.<br>Every tool we had was either running JavaScript or checking something orthogonal. The one observer that behaves differently — a crawler forming a first impression from raw HTML — was the one we had no feedback loop from, until Search Console told us months after the fact.<br>The fix, in two parts<br>The line itself is easy. Keep the value in state, start it at something that is true on the server, and set the real value after mount:<br>const [isMobile, setIsMobile] = useState(false);<br>…with a useEffect that reads window.innerWidth, sets the state, and subscribes to resize. The server renders the desktop label, the browser corrects it immediately if needed, and the first render matches on both sides so there is no hydration mismatch either.<br>The second part was less obvious. With server rendering restored, the show pages served their layout — but the show data was still fetched in the browser, so the server was sending a page that said “Loading show details…”. Technically server-rendered, still nothing to read.<br>So the page component became a server component...

server page pages show browser window

Related Articles