Hydration Error when passing bound data to custom component

Hello, I’m experiencing some hydration issues when passing bound data to a custom component. Despite trying numerous solutions, I find myself quite lost on how to resolve this problem. Am I making a mistake, or has anyone else encountered a similar issue and found a solution? Any guidance would be greatly appreciated. You can check my repo, which is a basic NextJS project to reproduce the exact issue.

Builder content link

Builder public api key
9eacceea83e34c62b1ceadf2a0411f09

Detailed steps to reproduce the bug
See loom below

Screenshots or video link

Builder.io - NextJS - Hydration Error - Watch Video

Code stack you are integrating Builder with
NextJS 14.2.3

Reproducible code example
https://github.com/Skry/builder-io-nextjs-hydration-error

1 Like

Hi Jean,

Thanks for including all that info. I have cloned your repository on my side and I was able to reproduce the issue. It seems like a bug, so we are discussing this with our internal team to find out a possible workaround. I will keep you updated on the progress of this issue.

Hello @jean,

We were able to reproduce the issue on our end, and it seems the hydration error is caused because you’re passing the builder content object. We are not sure why you are passing the content object; however, if you need to access the builder content data, you don’t need to pass it along with the builder component. Instead, you can use it from the builder context object in the editor. Here is an example:

context.builderContent.data.example

Additionally, we tested the scenario when passing an object that is used for binding in the editor. We did not encounter the same error, but you might need to use the useEffect hook for the parent component. Here is a modified code example:

import React, { useEffect, useState } from 'react';
import HydrationTestChild from './HydrationTestChild';

type Props = {
  text: string;
};

const HydrationTestParent = (props: Props) => {
  const [text, setText] = useState(props.text);

  useEffect(() => {
    if (typeof window !== 'undefined') {
      setText(props.text); // Ensure client-side rehydration consistency
    }
  }, [props.text]);

  return (
    <div className="p-20 text-4xl w-full bg-green-400">
      Parent : {text}
      <HydrationTestChild text={text} />
    </div>
  );
};

export default HydrationTestParent;

Here is an example of fetching and rendering the builder content:

import { builder } from "@builder.io/sdk";
import { RenderBuilderContent } from "../../components/builder";

// Builder Public API Key set in .env file
builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!);

interface PageProps {
  params: {
    page: string[];
  };
}
const myData = {
  content: {
    data: {
      example: "your text here a"
    }
  }
};
export default async function Page(props: PageProps) {
  const builderModelName = "page";

  const content = await builder
    // Get the page content from Builder with the specified options
    .get(builderModelName, {
      enrich: true,
      userAttributes: {
        // Use the page path specified in the URL to fetch the content
        urlPath: "/" + (props?.params?.page?.join("/") || ""),
      },
    })
    // Convert the result to a promise
    .toPromise();

  return (
    <>
      {/* Render the Builder page */}
      <RenderBuilderContent data={{ myData}} content={content} model={builderModelName} />
    </>
  );
}

For visual guidance, you can refer to the following loom Hydration error - Builder data | Visual Editor | Builder.io - 8 July 2024 | Loom

Hope this help!

Thanks,

I’ve duplicated this example block with hardcoded “myData” as in the example and pass that into my component and it still throws a hydration error. The prop value will be an empty string, then the value of “your text here a.”

This only works with Builder components, but if I try using a custom component with say a, “title” prop and bind the data, I get the hydration issue.

This is forcing me to have to hardcode all my product values or else I get a flash of my default string or a blank space before the data loads in.