I've run into a hydration issue that's really confusing me. I have a simple component that displays a single list element with an email address and name if there is one.
Here's the original component. It works perfectly when running in dev mode, and displays fine in a deployed state, but throws multiple hydration errors:
export default function Email({ email, name }: EmailProps) {
return (
<li className="ml-6">
{name ? `${name} <${email}>` : email}
</li>
);
}
After some trial and error, I was able to pinpoint the issue was with the template literal. When I remove the template literal, and instead use four separate strings, the hydration errors go away.
export default function Email({ email, name }: EmailProps) {
return (
<li className="ml-6">
{name ? (
<>{name}{" <"}{email}{">"}</>
) : (
email
)}
</li>
);
}
I don't see any reason why the template literal should be causing hydration errors. Any ideas?
Edit: Here's the page.tsx
:
export default async function Details({ params: { customerId } }: DetailsProps) {
unstable_noStore();
const customer = await database.customer.findUnique({
where: { id: customerId },
include: {
emails: true,
},
});
if (!customer) {
return notFound();
}
return (
<main className="p-6">
<h1 className="mb-6 text-2xl">{`${customer.lastName}, ${customer.firstName}`}</h1>
<div className="mb-4">
<h2 className="inline font-semibold">Id:</h2> {customer.id}
</div>
<div className="mb-4">
<h2 className="inline font-semibold">Status:</h2> {customer.status}
</div>
<div className="mb-4">
<h2 className="inline font-semibold">Score:</h2> {`${customer.score} (${customer.coefficient})`}
</div>
<div className="mb-4">
<h2 className="inline font-semibold">Email Addresses:</h2>
{customer.emails.length > 0 ? (
<ul className="list-square">
{customer.emails.map(({ email, name }) => {
return (
<Email
key={email}
email={email}
name={name}
/>
);
})}
</ul>
) : (
<p className="ml-4 italic">none</p>
)}
</div>
</main>
);
}
Edit2: Screenshot of errors:
This is the console log showing the hydration errors when using the template literal (right side) versus the console log showing no errors when not using the template literal (left side). The only change between the two is how the string is built as shown in the original post.
These errors only occur when run in a packaged state (either locally in Docker or deployed to GCP). They never occur when running in development mode, so I don't know how to get more clear error logs.
I've run into a hydration issue that's really confusing me. I have a simple component that displays a single list element with an email address and name if there is one.
Here's the original component. It works perfectly when running in dev mode, and displays fine in a deployed state, but throws multiple hydration errors:
export default function Email({ email, name }: EmailProps) {
return (
<li className="ml-6">
{name ? `${name} <${email}>` : email}
</li>
);
}
After some trial and error, I was able to pinpoint the issue was with the template literal. When I remove the template literal, and instead use four separate strings, the hydration errors go away.
export default function Email({ email, name }: EmailProps) {
return (
<li className="ml-6">
{name ? (
<>{name}{" <"}{email}{">"}</>
) : (
email
)}
</li>
);
}
I don't see any reason why the template literal should be causing hydration errors. Any ideas?
Edit: Here's the page.tsx
:
export default async function Details({ params: { customerId } }: DetailsProps) {
unstable_noStore();
const customer = await database.customer.findUnique({
where: { id: customerId },
include: {
emails: true,
},
});
if (!customer) {
return notFound();
}
return (
<main className="p-6">
<h1 className="mb-6 text-2xl">{`${customer.lastName}, ${customer.firstName}`}</h1>
<div className="mb-4">
<h2 className="inline font-semibold">Id:</h2> {customer.id}
</div>
<div className="mb-4">
<h2 className="inline font-semibold">Status:</h2> {customer.status}
</div>
<div className="mb-4">
<h2 className="inline font-semibold">Score:</h2> {`${customer.score} (${customer.coefficient})`}
</div>
<div className="mb-4">
<h2 className="inline font-semibold">Email Addresses:</h2>
{customer.emails.length > 0 ? (
<ul className="list-square">
{customer.emails.map(({ email, name }) => {
return (
<Email
key={email}
email={email}
name={name}
/>
);
})}
</ul>
) : (
<p className="ml-4 italic">none</p>
)}
</div>
</main>
);
}
Edit2: Screenshot of errors:
This is the console log showing the hydration errors when using the template literal (right side) versus the console log showing no errors when not using the template literal (left side). The only change between the two is how the string is built as shown in the original post.
These errors only occur when run in a packaged state (either locally in Docker or deployed to GCP). They never occur when running in development mode, so I don't know how to get more clear error logs.
There seems no issue with your code example, it shouldnt have an hydration error since same data is present/render in both server and client side from what I can see. Nevertheless, I would suggest you following this guide of how setting up a global prisma client, where you dont need to disconnect after querying something. Also from a React standpoint, I dont see a good use case for having the Email
component so far, you could just use the li
element in the page.tsx
file, to avoid prop drilling and so.
Try making these changes and see if it still happens. If so, please share an image of the hydration error instead of your explanation. Hope it helped