I have a problem with a dynamic page in my next application, basically it is about a web site, in which through SANITY, I upload my work done. The idea is that when I click on a button, a Shadcn component is activated and shows me the data from sanity. I'm using Nextjs14, typescript, tailwind, sanity and Shadcn
I attach the code:
import ImageGallery from "@/app/components/ImageGallery"
import { client } from "../../../utils/sanity/client"
// UI SHADCN
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
import { Button } from "@/components/ui/button"
import Link from "next/link"
// SANITY DATA
async function getData(slug: string) {
const querie = `*[_type == 'job' && slug.current == "${slug}"][0]{
_id,
images,
name,
description,
objetive,
releaseDate,
link,
"slug": slug.current,
"categoryName": category->name,
}`
const data = client.fetch(querie)
return data
}
export default async function JobUI({ params } : { params: {slug: string} }) {
const data: job = await getData(params.slug)
return(
<div className="flex gap-3">
<ImageGallery images={data.images}/>
<Sheet >
<SheetTrigger><Button>Ver Información</Button></SheetTrigger>
<SheetContent className="w-96">
<SheetHeader>
<SheetTitle className="mt-5">
<p>{data.name}</p>
<p className="text-orange-500">Categoria: {data.categoryName}</p>
</SheetTitle>
<SheetDescription>
<p className="text-sm font-bold">Descripción</p>
<p>{data.description}</p>
</SheetDescription>
<SheetDescription>
<p className="text-sm font-bold">Objetivo</p>
<p>{data.objetive}</p>
</SheetDescription>
<Link href={data.link} target="_blank"><Button>Link del Proyecto</Button></Link>
</SheetHeader>
</SheetContent>
</Sheet>
</div>
)
}
The application works without problems, only that from the browser, next js gives me this message of 3 serious errors, related to hydration and components! Message:
Unhandled Runtime Error
Error: Hydration failed because the initial UI does not match what was rendered on the server.
Warning: Expected server HTML to contain a matching <button> in <button>.
See more info here: https://nextjs.org/docs/messages/react-hydration-error
Component Stack
button
button
eval
./node_modules/@radix-ui/react-primitive/dist/index.mjs (39:26)
eval
./node_modules/@radix-ui/react-dialog/dist/index.mjs (90:28)
Provider
./node_modules/@radix-ui/react-context/dist/index.mjs (45:28)
$5d3850c4d0b4e6c7$export$3ddf2d174ce01153
./node_modules/@radix-ui/react-dialog/dist/index.mjs (60:28)
div
Call Stack
throwOnHydrationMismatch
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (7088:8)
throwOnHydrationMismatch
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (7117:6)
tryToClaimNextHydratableInstance
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (16524:4)
updateHostComponent$1
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (18427:13)
apply
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20498:13)
dispatchEvent
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20547:15)
apply
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20622:28)
invokeGuardedCallback
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (26813:6)
beginWork
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25637:11)
performUnitOfWork
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25623:4)
workLoopConcurrent
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25579:8)
renderRootConcurrent
node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (24432:37)
callback
node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (256:33)
workLoop
node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (225:13)
flushWork
node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (534:20)
I would appreciate your help please
I know I'm doing something wrong but I don't know what, I tried to add noHydratation
on some buttons, but it gives me a bad feeling, it's not the solution.
The reason this hydration error occurs is using invalid HTML syntax like a <button>
inside a <button>
.
In your case, this happens because the <SheetTrigger>
renders a <button>
and a <Button>
is nested inside which also renders a <button>
.
To fix this issue, you can add an asChild
prop set to true
to the <SheetTrigger>
. This will not render the default DOM element of SheetTrigger
. Instead, it will render the child component <Button>
and will pass the props and behaviour of <SheetTrigger>
to make it functional.
This is a Radix UI functionality, and you can read more about it in the Radix Composition guide.
For your code, change your default export function to this:
export default async function JobUI({ params }: { params: { slug: string } }) {
const data: job = await getData(params.slug);
return (
<div className="flex gap-3">
<ImageGallery images={data.images} />
<Sheet>
<SheetTrigger asChild>
<Button>Ver Información</Button>
</SheetTrigger>
<SheetContent className="w-96">
<SheetHeader>
<SheetTitle className="mt-5">
<p>{data.name}</p>
<p className="text-orange-500">Categoria: {data.categoryName}</p>
</SheetTitle>
<SheetDescription>
<p className="text-sm font-bold">Descripción</p>
<p>{data.description}</p>
</SheetDescription>
<SheetDescription>
<p className="text-sm font-bold">Objetivo</p>
<p>{data.objetive}</p>
</SheetDescription>
<Link href={data.link} target="_blank">
<Button>Link del Proyecto</Button>
</Link>
</SheetHeader>
</SheetContent>
</Sheet>
</div>
);
}