0

I have a website where I wan't to apply a different background color to some components throughout my website. The site is statically generated using Next.js, and uses Tailwind for styling.

Using the naive approach by selecting a color using Math.random() won't work, since we can get different results on on the client and the server, and we might see the color quickly changes on first render.

const backgroundColors = ["beige", "purple", "orange"];
const shuffledBackgroundColors = backgroundColors.sort(
  () => Math.random() - 0.5
);

Will give us error:

Warning: Prop className did not match. Server: "bg-purple" Client: "bg-orange"


My next idea was then to use a pseudo random number generator, so that I could control the seed, and try to make sure we had the same seed on server and client, like this:

import gen from "rand-seed";

const r = new gen(new Date().getUTCMinutes().toString());
const backgroundColors = ["beige", "purple", "orange"];
const shuffledBackgroundColors = backgroundColors.sort(() => r.next() - 0.5);

There might be an edge case were the clock was 30m 59s on the server, and then 31m 0s on the client, but I was willing to live with this. On localhost this seemed to work great, but when I deployed it to Vercel, it looks like there was enough difference between the server and my browser, so it produced bad results.

Ideally it would also select a new random color on each page load, and not just every minute.

Any way to achieve this in a reliable way?

eivindml
  • 2,197
  • 7
  • 36
  • 68
  • Does this answer your question? [How to use template literals in tailwindcss to change classes dynamically?](https://stackoverflow.com/questions/68020378/how-to-use-template-literals-in-tailwindcss-to-change-classes-dynamically) – saboshi69 Jun 13 '22 at 07:43
  • Are you applying the class like bg-${mappingColor} ? If yes, you should not construct class names dynamically. – saboshi69 Jun 13 '22 at 07:48
  • No, I'm applying them using `classnames` package, like: `cs({"bg-red-100": props.backgroundColor === "red"})`. That's not the issue. Have also used the conventional approach from Tailwind. Same results. It is properly applied, and not purged on build. The issue is that we get different random color on server and client. – eivindml Jun 13 '22 at 09:58

1 Answers1

0

You can create seed on the server side of Next.js and then pass it to the React side, for example in getStaticProps (or getServerSideProps if your page is dynamic)

export async function getStaticProps(context) {
  return {
    props: {
      seed: new Date().getUTCMinutes().toString()
    }, // will be passed to the page component as props
  }
}

And then props.seed will be available to your page component and it will be the same in the server and on the client.

getServerSideProps would serve you better if you want different color on page refresh. Or you can use revalidate param for getStaticProps to refresh props with an interval.

If you just need one color (or an array of colors) maybe you can even skip the seed thing and just do the work on on the server side? Like that:

export async function getStaticProps(context) {
  return {
    props: {
      colors: ["beige", "purple", "orange"].sort(() => r.next() - 0.5)
    },
  }
}
Danila
  • 15,606
  • 2
  • 35
  • 67