Component rendering incorrectly when Link is present
Unanswered
Yacare Caiman posted this in #help-forum
Yacare CaimanOP
The expected behavior for my website is that these cards render like in the first image, but it's rendering like in the second image (for the first two seconds, which causes a hydration error).
Here's the snippet of code which makes the error happen:
The thing that causes the cards to render wrong is the "Link" component, the author's in the card are supposed to be routable, but when adding the Link, it screws the whole component up. The third image shows how it renders in the html (the content being outside of the <a> tag).
Here's the snippet of code which makes the error happen:
<p className="text-neutral-400 font-semibold text-xs">
{authors.map((author, i) =>
<span key={i}>
<Link
href={`/artist/test`}
className="hover:underline"
>
{author}
</Link>
{i < authors.length - 1 && (
', '
)}
</span>
)}
</p>The thing that causes the cards to render wrong is the "Link" component, the author's in the card are supposed to be routable, but when adding the Link, it screws the whole component up. The third image shows how it renders in the html (the content being outside of the <a> tag).
24 Replies
Polar bear
show me the hydration error
Yacare CaimanOP
sure, give me a sec
I get these three everytime I refresh the page
try adding a useEffect that first checks if the component is mounted before mapping the data
@Yacare Caiman The expected behavior for my website is that these cards render like in the first image, but it's rendering like in the second image (for the first two seconds, which causes a hydration error).
Here's the snippet of code which makes the error happen:
ts
<p className="text-neutral-400 font-semibold text-xs">
{authors.map((author, i) =>
<span key={i}>
<Link
href={`/artist/test`}
className="hover:underline"
>
{author}
</Link>
{i < authors.length - 1 && (
', '
)}
</span>
)}
</p>
The thing that causes the cards to render wrong is the "Link" component, the author's in the card are supposed to be routable, but when adding the Link, it screws the whole component up. The third image shows how it renders in the html (the content being outside of the <a> tag).
hmm can you show the code wrapping this code? ideally a whole (simplified) page component with this code snippet that throws the hydration error
@Dayo try adding a useEffect that first checks if the component is mounted before mapping the data
Yacare CaimanOP
Oh, I'll look up how to do that thanks!
@joulev hmm can you show the code wrapping this code? ideally a whole (simplified) page component with this code snippet that throws the hydration error
Yacare CaimanOP
Sure, I'll make a quick simplifed version
@Dayo https://nextjs.org/docs/messages/react-hydration-error
Yacare CaimanOP
thanks!!
Here's the simplified page:
import Body from "@/components/layout/Body"
import AlbumCard from "@/components/card/AlbumCard"
export default async function Home() {
const albums = [
{ id: "blah", name: "blah", image: "blah", authors: ["blah", "blah2"] }
]
return (
<>
<div
className="absolute z-0 inset-x-0 h-96 rounded-t-[--radius] bg-gradient-to-b from-purple-950 to-transparent"
/>
<Body>
<h2 className="mt-4 text-white text-2xl font-bold">Latest releases</h2>
<div className="grid grid-cols-1 gap-4 @xs:grid-cols-2 @md:grid-cols-3 @2xl:grid-cols-4 @4xl:grid-cols-5 @5xl:grid-cols-6 @6xl:grid-cols-7">
{albums.map(album =>
<AlbumCard
key={album.id}
name={album.name}
href={`/album/${album.id}`}
image={album.image}
authors={album.authors}
/>
)}
</div>
</Body>
</>
)
}@Yacare Caiman Here's the simplified page:
ts
import Body from "@/components/layout/Body"
import AlbumCard from "@/components/card/AlbumCard"
export default async function Home() {
const albums = [
{ id: "blah", name: "blah", image: "blah", authors: ["blah", "blah2"] }
]
return (
<>
<div
className="absolute z-0 inset-x-0 h-96 rounded-t-[--radius] bg-gradient-to-b from-purple-950 to-transparent"
/>
<Body>
<h2 className="mt-4 text-white text-2xl font-bold">Latest releases</h2>
<div className="grid grid-cols-1 gap-4 @xs:grid-cols-2 @md:grid-cols-3 @2xl:grid-cols-4 @4xl:grid-cols-5 @5xl:grid-cols-6 @6xl:grid-cols-7">
{albums.map(album =>
<AlbumCard
key={album.id}
name={album.name}
href={`/album/${album.id}`}
image={album.image}
authors={album.authors}
/>
)}
</div>
</Body>
</>
)
}
does
<Body> declare a <body>? because you cannot have two <body>'s; one <body> is already declared in the root layout so you cannot use <body> again hereYacare CaimanOP
And here's the AlbumCard component:
export default function GenreCard({
name,
authors,
image,
href,
}: Props) {
return (
<Link
href={href}
draggable="false"
className="h-full"
>
<div className="group p-4 pb-6 flex flex-col bg-neutral-900 hover:bg-neutral-800 transition-colors gap-2 rounded-[--radius] overflow-hidden">
<div className="relative aspect-square transition-transform group-hover:-translate-y-1">
<Image
src={image}
alt={"blah"}
fill
/>
</div>
<h2 className="mt-2 truncate overflow-hidden text-white text-md font-bold drop-shadow-xl">
{name}
</h2>
<p className="text-neutral-400 font-semibold text-xs">
{authors.map((author, i) =>
<span key={i}>
<Link
href={`/artist/test`}
className="hover:underline"
>
{author}
</Link>
{i < authors.length - 1 && (
', '
)}
</span>
)}
</p>
</div>
</Link>
)
}@Yacare Caiman And here's the AlbumCard component:
ts
export default function GenreCard({
name,
authors,
image,
href,
}: Props) {
return (
<Link
href={href}
draggable="false"
className="h-full"
>
<div className="group p-4 pb-6 flex flex-col bg-neutral-900 hover:bg-neutral-800 transition-colors gap-2 rounded-[--radius] overflow-hidden">
<div className="relative aspect-square transition-transform group-hover:-translate-y-1">
<Image
src={image}
alt={"blah"}
fill
/>
</div>
<h2 className="mt-2 truncate overflow-hidden text-white text-md font-bold drop-shadow-xl">
{name}
</h2>
<p className="text-neutral-400 font-semibold text-xs">
{authors.map((author, i) =>
<span key={i}>
<Link
href={`/artist/test`}
className="hover:underline"
>
{author}
</Link>
{i < authors.length - 1 && (
', '
)}
</span>
)}
</p>
</div>
</Link>
)
}
ok there is a problem with this one, which is that links are nested.
<a> nested inside <a> is invalid html, hence the error@joulev does `<Body>` declare a `<body>`? because you cannot have two `<body>`'s; one `<body>` is already declared in the root layout so you cannot use `<body>` again here
Yacare CaimanOP
It renders a main tag so yeah there's no duplicate of body tag
@joulev ok there is a problem with this one, which is that links are nested. `<a>` nested inside `<a>` is invalid html, hence the error
Yacare CaimanOP
ohhh I didn't know that
you have to rewrite it so that the link elements are not nested
@Yacare Caiman It renders a main tag so yeah there's no duplicate of body tag
yes then that part is good, the error is due to the Link being descendant of Link
Yacare CaimanOP
It initially renders wrong and then shows it the way I intended, but yeah it causes the hydration error
@joulev you have to rewrite it so that the link elements are not nested
Yacare CaimanOP
Thank you so much I was really confused!
@Yacare Caiman It initially renders wrong and then shows it the way I intended, but yeah it causes the hydration error
it's because i think initially the renderer would put the nested Link outside the parent Link, making them siblings, hence messing up the display on initial render
@joulev it's because i think initially the renderer would put the nested Link outside the parent Link, making them siblings, hence messing up the display on initial render
Yacare CaimanOP
Yeah that's exactly what happens!
well I'll fix it right now, thanks again!
you're welcome! good luck and have fun