plutownium
plutownium

Reputation: 2008

hydration failed error using recharts with nextjs

I have just started a new Next project, and I want to use a library called ReCharts.

However, whenever I use it in my project, even with one of the provided examples, it throws the following errors:

Error: Hydration failed because the initial UI does not match what was rendered on the server.

Warning: An error occurred during hydration. The server HTML was replaced with client content in `<div>`.

Warning: Prop `y` did not match. Server: "5" Client: "10.800000190734863"
    at text
    at Text (webpack-internal:///./node_modules/recharts/es6/component/Text.js:232:5)
    at g
    at Layer (webpack-internal:///./node_modules/recharts/es6/container/Layer.js:23:24)
    at g
    at g
    at Layer (webpack-internal:///./node_modules/recharts/es6/container/Layer.js:23:24)
    at CartesianAxis (webpack-internal:///./node_modules/recharts/es6/cartesian/CartesianAxis.js:77:5)
    at svg
    at Surface (webpack-internal:///./node_modules/recharts/es6/container/Surface.js:23:24)
    at div
    at CategoricalChartWrapper (webpack-internal:///./node_modules/recharts/es6/chart/generateCategoricalChart.js:920:7)
    at SimpleBarChart
    at main
    at div
    at Home

Here is my code:

const data = [
    {
        name: "Page A",
        uv: 4000,
        pv: 2400,
        amt: 2400,
    },
    {
        name: "Page B",
        uv: 3000,
        pv: 1398,
        amt: 2210,
    },
    {
        name: "Page C",
        uv: 2000,
        pv: 9800,
        amt: 2290,
    },
    {
        name: "Page D",
        uv: 2780,
        pv: 3908,
        amt: 2000,
    },
    {
        name: "Page E",
        uv: 1890,
        pv: 4800,
        amt: 2181,
    },
    {
        name: "Page F",
        uv: 2390,
        pv: 3800,
        amt: 2500,
    },
    {
        name: "Page G",
        uv: 3490,
        pv: 4300,
        amt: 2100,
    }
];

function SimpleBarChart(){
    return (
        <BarChart
            id={1}
            width={500}
            height={300}
            data={data}
            margin={{
                top: 5,
                right: 30,
                left: 20,
                bottom: 5,
            }}>
                <CartesianGrid strokeDasharray="3 3" />
                <XAxis dataKey="name" />
                <YAxis />
                <Tooltip />
                <Legend />
                <Bar dataKey="pv" fill="#8884d8" />
                <Bar dataKey="uv" fill="#82ca9d" />
        </BarChart>
    );
}

export default function Home(){
    return (
        <SimpleBarChart />
    );
}

Note that this is copied and pasted in from here, so it should really "just work" out of the box.

Upvotes: 7

Views: 5231

Answers (5)

GROVER.
GROVER.

Reputation: 4388

For those struggling with rendering the Pie chart using @plutownium's answer, you need to use a hybrid of dynamic imports with ssr disabled and regular importing:

import { Pie, Cell } from "recharts";

const PieChart = dynamic(
    () => (
        import("recharts").then(recharts => recharts.PieChart)
    ), 
    { 
        ssr: false 
    }
);

const Example = () => (
    <PieChart width={100} height={100}>
        <Pie data={data} {/* etc... */}>
            { data.map(entry => (
                <Cell fill={entry.color} key={entry.key} />
            ))}
        </Pie>
    </PieChart>
);

This should prevent hydration warnings and allow the chart to render.

Upvotes: 4

Otto G
Otto G

Reputation: 780

I think the easiest setup is to have a custom client component that does all the Recharts rendering and then to just load that component (and only that one) dynamically with SSR off.

Please note that, in one of the answers, dynamic() is called without defining a function for the first argument. That does not seem to work. (See also the documentation at https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading for examples.) Also note that adding .then() after import() is only needed if there is no default export that can be used.

Here is a sample server component that uses a chart rendered by a component called SimpleBars:

import dynamic from 'next/dynamic'
const SimpleBars = dynamic(() => import('./bars-test'), { ssr: false })
export default function ChartSection() {
  return (
    <section>
      <SimpleBars />
    </section>
  )
}

And here is the SimpleBars component (in a file named bars-test.jsx):

'use client'
import { BarChart, Bar } from 'recharts'
const data = [
  {name: 'Category 1', series1: 400},
  {name: 'Category 2', series1: 200},
  {name: 'Category 3', series1: 500},
  {name: 'Category 4', series1: 100},
]
export default function BarsTest() {
  return (
    <BarChart width={500} height={400} data={data}>
      <Bar dataKey="series1" fill="#bcbcbc" />
    </BarChart>
  )
}

On the server side, this will be rendered as the following HTML, and the chart will then be drawn on the client side, without any hydration errors. This was tested with Next.js 14.0.3 and Recharts 2.9.3.

<section>
  <template data-dgst="NEXT_DYNAMIC_NO_SSR_CODE" data-msg="NEXT_DYNAMIC_NO_SSR_CODE"></template>
</section>

Upvotes: 1

nikki
nikki

Reputation: 373

use the following to ensure Client-Side Rendering:

const [isClient, setIsClient] = useState(false);

  useEffect(() => {
    setIsClient(true);
  }, []);

Upvotes: -1

hello
hello

Reputation: 1

You can try this. It solves my problem

const LineChart = dynamic(() => import('recharts').then(mod => mod.LineChart), {
  ssr: false,
  loading: () => <p>Loading...</p>
});

Upvotes: 0

plutownium
plutownium

Reputation: 2008

As @juliomalves says, the answer is to use dynamic imports with ssr disabled:

import dynamic from "next/dynamic";

const SimpleBarChartWithoutSSR = dynamic(
    import("../components/rechartsCharts/SimpleBar"),
    { ssr: false }
);
const SimpleScatterChartWithoutSSR = dynamic(
    import("../components/rechartsCharts/SimpleScatter"),
    { ssr: false }
);
const DashedLineChartWithoutSSR = dynamic(
    import("../components/rechartsCharts/DashedLine"),
    { ssr: false }
);
const PercentAreaChartWithoutSSR = dynamic(
    import("../components/rechartsCharts/PercentArea"),
    { ssr: false }
);

Upvotes: 7

Related Questions