Next.js Discord

Discord Forum

Hydration error using a simple zustand store

Answered
American black bear posted this in #help-forum
Open in Discord
American black bearOP
Hi everyone,

I am currently having an issue with zustand and Next13. I am getting an hydration error with the code below.
How could i set a value from the local storage in a zustand store without causing an hydration error ? Since it could be different from the default "Kg" value ?

Thanks in advance for any help / clarification 😀

code sandbox:https://codesandbox.io/p/sandbox/nervous-night-mvyq8d?file=%2Fapp%2FweightUnit.ts%3A28%2C1

the error:
Warning: Text content did not match. Server: "kg" Client: "lbs"
Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.


code:
import { create } from "zustand";
import { z } from "zod";

const weightUnitSchema = z.enum(["kg", "lbs"]);
type WeightUnit = z.infer<typeof weightUnitSchema>;

type WeightUnitStore = {
  value: WeightUnit;
  setValue: (newValue: WeightUnit) => void;
};

const WEIGHT_KEY = "weightUnit";

export const useWeightUnit = create<WeightUnitStore>((set) => ({
  value: (() => {
    try {
      return weightUnitSchema.parse(localStorage.getItem(WEIGHT_KEY));
    } catch (error) {
      return "kg";
    }
  })(),

  setValue: (newValue) => {
    localStorage.setItem(WEIGHT_KEY, newValue);
    return set((store) => ({ ...store, value: newValue }));
  },
}));
Answered by Rafael Almeida
to fix your issue you need to initialize your store with a default value for both the server and the client and set the correct value to the store inside a useEffect hook which only runs in the client
View full answer

8 Replies

localStorage isn't available in the server, so when creating the store in the server localStorage.getItem will throw an error and set the initial value as "kg". then when the code runs in the client, localStorage will work and the initial value will be "lbs". this is where the mismatch happens, the initial render of both the server and the client should be the same
to fix your issue you need to initialize your store with a default value for both the server and the client and set the correct value to the store inside a useEffect hook which only runs in the client
Answer
American black bearOP
but I am only using this store in client components. Why is it being run in the server ? Do client components run both on the sever and client ?
yeah
server components run exclusively in the server, client components are still pre-rendered so they run in both environments
if you don't want this component rendering in the server at all, you can skip SSR with next/dynamic: https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading#skipping-ssr
American black bearOP
thanks for the help I got it fixed with your explainations 🙏
np! :blobthumbsup: