I’m developing an app with Next.js 14, TypeScript, Mongoose & MongoDB.
I fetched users from my database and rendered them in cards with some of their information, like tags displayed in badges.
I also used the Link
component to make the cards and the badges clickable.
Here is some of my relevant code:
- “componentscardsUserCard.tsx”:
import Link from "next/link";
import Image from "next/image";
import { getTopInteractedTags } from "@/lib/actions/tag.action";
import { Badge } from "../ui/badge";
import RenderTag from "../shared/RenderTag";
interface Props {
user: {
_id: string;
clerkId: string;
picture: string;
name: string;
username: string;
// orgId?: string;
};
}
const UserCard = async ({ user }: Props) => {
const interactedTags = await getTopInteractedTags({ userId: user._id });
return (
<Link
href={`/profile/${user.clerkId}`}
className="shadow-light100_darknone w-full max-xs:min-w-full xs:w-[260px]"
>
<article className="background-light900_dark200 light-border flex w-full flex-col items-center justify-center rounded-2xl border p-8">
<Image
src={user.picture}
alt="image profil utilisateur"
width={100}
height={100}
className="rounded-full"
/>
<div className="mt-4 text-center">
<h3 className="h3-bold text-dark200_light900 line-clamp-1">
{user.name}
</h3>
<p className="body-regular text-dark500_light500 mt-2">
@{user.username}
</p>
{/* {user.orgId && (
<p className="body-regular text-dark500_light500 mt-2">
Org ID: {user.orgId}
</p>
)} */}
</div>
<div className="mt-5">
{interactedTags.length > 0 ? (
<div className="flex items-center gap-2">
{interactedTags.map((tag) => (
<RenderTag key={tag._id} _id={tag._id} name={tag.name} />
))}
</div>
) : (
<Badge>Pas encore d’étiquettes</Badge>
)}
</div>
</article>
</Link>
);
};
export default UserCard;
- “componentssharedRenderTag.tsx”:
import React from "react";
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
interface Props {
_id: string;
name: string;
totalQuestions?: number;
showCount?: boolean;
}
const RenderTag = ({ _id, name, totalQuestions, showCount }: Props) => {
return (
<Link href={`/etiquettes/${_id}`} className="flex justify-between gap-2">
<Badge className="subtle-medium background-light800_dark300 text-light400_light500 rounded-md border-none px-4 py-2 uppercase">
{name}
</Badge>
{showCount && (
<p className="small-medium text-dark500_light700">{totalQuestions}</p>
)}
</Link>
);
};
export default RenderTag;
However, I get this error:
Error: Hydration failed because the initial UI does not match what was
rendered on the server. See more info here:
https://nextjs.org/docs/messages/react-hydration-errorExpected server HTML to contain a matching in .
I tried many solutions, like replacing the article
with a div
or passing the suppressHydrationWarning
property to the related elements to remove this warning.
However, these solutions didn’t help me fix this issue.
Therefore, I came out with another alternative which is using useRouter
instead of Link
for the navigation.
Here is my code:
- “componentssharedRenderTag.tsx”:
"use client";
import { useRouter } from "next/navigation";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
interface Props {
_id: string;
name: string;
totalQuestions?: number;
showCount?: boolean;
}
const RenderTag = ({ _id, name, totalQuestions, showCount }: Props) => {
const router = useRouter();
return (
<Button
className="flex justify-between gap-2"
onClick={() => router.push(`/tags/${_id}`)}
>
<Badge className="subtle-medium background-light800_dark300 text-light400_light500 rounded-md border-none px-4 py-2 uppercase">
{name}
</Badge>
{showCount && (
<p className="small-medium text-dark500_light700">{totalQuestions}</p>
)}
</Button>
);
};
export default RenderTag;
Now, the hydration error disappears, but I get this warning in my terminal:
Warning: Only plain objects can be passed to Client Components from
Server Components. Objects with toJSON methods are not supported.
Convert it manually to a simple value before passing it to props.
<… _id={{buffer: …}} name=”next.js”>
Where this error is coming from, especially this part <... _id={{buffer: ...}} name="next.js">
?
I guess it is related to my database “tags” collection whis has the following data:
{"_id":{"$oid":"66a476d397749b79a8140e72"},"__v":{"$numberInt":"0"},"createdOn":{"$date":{"$numberLong":"1722054355742"}},"followers":[],"name":"next.js","questions":[]}
{"_id":{"$oid":"66a5164197749b79a8a3258b"},"__v":{"$numberInt":"0"},"createdOn":{"$date":{"$numberLong":"1722095169919"}},"followers":[],"name":"React","questions":[]}
But, why I get this error only when I use useRouter
?