Craig Glennie

Fixing layout flicker in NextJ due to uncompiled styled JSX

For a while now this website had an irritating layout-shift when it first loaded.


I tried a couple of things to fix this, like putting styles directly into the index.html file rather than a separate CSS file (in retrospect I don’t know why I thought this would help: it’s normal to have external CSS files and also not have the content flickering)

Eventually I noticed that I was getting a warning from styled-jsx when Next compiled the site:

Warning: Received `true` for a non-boolean attribute `jsx`.

If you want to write it to the DOM, pass a string instead: jsx="true" or jsx={value.toString()}.
    in style (at MediaStyle.tsx:7)
    ...

MediaStyle.jsx is a file with a couple of components that I created to wrap styles for mobile and desktop. This is the whole thing:

import React from "react";

// Devices with this width or less are considered mobile
const MOBILE_MAX_WIDTH_PX = 1049;

export const DesktopStyle = ({ children }) => {
  return (
    <style jsx>{`
      @media (min-width: ${MOBILE_MAX_WIDTH_PX + 1}px) {
        ${children}
      }
    `}</style>
  );
};

export const MobileStyle = ({ children }) => {
  return (
    <style jsx>{`
      @media (max-width: ${MOBILE_MAX_WIDTH_PX}px) {
        ${children}
      }
    `}</style>
  );
};

And in various parts of my code I would use DesktopStyle or MobileStyle as-needed:

<DesktopStyle>{`
  .Columns {
    /* Some styling */
  }`}</DesktopStyle>

What I eventually figured out is that whilst I was diligently DRYing (yay, right?) I was preventing styled-jsx from compiling the style, which makes it a dynamic style - which is to say, it’s rendered in the browser, on the fly. This is intended to enable styling “from the outside world”, but for me it meant that when the page initially rendered it had an incomplete set of styles. Only when the dynamic styles had run was the styling complete and the layout correct - hence the flicker.

So I fixed this by doing away with the helper components and writing the styles like normal:

<style jsx>{`
  @media (min-width: 1050px) {
    .Columns {
      /* Some styling */
    }
  }
`}</style>