Hydration Mismatch. Unable to find DOM nodes for hydration key

Hi everyone After create my "card" to list all projects I starting having the error Hydration Mismatch. Unable to find DOM nodes for hydration key, initial I suspect that is because I'm trying to use createPagination primitive, but after comment the code related with the pagination the error persists. I will leave the code on the thread
24 Replies
Daniel Sousa @TutoDS
routes/projects/index.tsx
import { createAsync } from '@solidjs/router';
import { For, Show } from 'solid-js';
import { getProjects } from '~/cms/services/projects/get-projects';
import { ProjectCard } from '~/components/cards/project';

export default function Projects() {
// const [searchParams, setSearchParams] = useSearchParams();
//
// const totalOfProjects = createAsync(() => getTotalOfProjects());
//
// const [paginationProps, page, setPage] = createPagination({
// pages: Math.ceil((totalOfProjects() ?? 0) / DEFAULT_PAGINATION_OFFSET),
// });

const projects = createAsync(() => getProjects(1));
//
// createEffect(() => {
// setSearchParams({
// page: page(),
// });
// });

return (
<>
<header class={'py-12'}>
<div class="container flex flex-col items-center justify-center text-center">
<h1>Projetos</h1>
</div>
</header>

<section>
<Show when={projects()}>
<div class="container grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<For each={projects()}>
{(project) => (
<ProjectCard
title={project.title}
headline={project.headline}
thumbnail={project.thumbnail}
services={project.services}
slug={project.slug}
/>
)}
</For>
</div>
</Show>
</section>
</>
);
}
import { createAsync } from '@solidjs/router';
import { For, Show } from 'solid-js';
import { getProjects } from '~/cms/services/projects/get-projects';
import { ProjectCard } from '~/components/cards/project';

export default function Projects() {
// const [searchParams, setSearchParams] = useSearchParams();
//
// const totalOfProjects = createAsync(() => getTotalOfProjects());
//
// const [paginationProps, page, setPage] = createPagination({
// pages: Math.ceil((totalOfProjects() ?? 0) / DEFAULT_PAGINATION_OFFSET),
// });

const projects = createAsync(() => getProjects(1));
//
// createEffect(() => {
// setSearchParams({
// page: page(),
// });
// });

return (
<>
<header class={'py-12'}>
<div class="container flex flex-col items-center justify-center text-center">
<h1>Projetos</h1>
</div>
</header>

<section>
<Show when={projects()}>
<div class="container grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<For each={projects()}>
{(project) => (
<ProjectCard
title={project.title}
headline={project.headline}
thumbnail={project.thumbnail}
services={project.services}
slug={project.slug}
/>
)}
</For>
</div>
</Show>
</section>
</>
);
}
components/cards/project.tsx
import { For, Show } from 'solid-js';
import { urlFor } from '~/cms';
import type { GetProjectsQueryResult } from '~/shared/types';
import { cn } from '~/shared/utils';

type Props = Pick<
GetProjectsQueryResult[number],
'title' | 'slug' | 'thumbnail' | 'headline' | 'services'
>;

function ProjectCard({ title, slug, headline, thumbnail, services }: Props) {
return (
<a href={`/projetos/${slug}`} class={'group'}>
<article
class={cn(['relative isolate', 'overflow-hidden', 'h-[500px] rounded-lg'])}
>
<img
src={urlFor(thumbnail).format('webp').maxWidth(1500).url()}
alt={title}
class={
'-z-10 absolute inset-0 object-cover object-center transition-all duration-300 ease-in-out group-hover:scale-110'
}
/>

<section class="flex size-full flex-col justify-end bg-gradient-to-b from-transparent to-gray-900/90 px-6 py-8 text-gray-50">
<h3 class={'font-semibold text-white'}>{title}</h3>

<Show when={headline}>
<p class={'mt-1 mb-4 line-clamp-3 text-sm'}>{headline}</p>
</Show>

<Show when={services}>
<ul>
<For each={services}>
{(service) => (
<li>
<a
class={
'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'
}
href={`/servicos/${service.slug}`}
>
{service.title}
</a>
</li>
)}
</For>
</ul>
</Show>
</section>
</article>
</a>
);
}

export { ProjectCard };
import { For, Show } from 'solid-js';
import { urlFor } from '~/cms';
import type { GetProjectsQueryResult } from '~/shared/types';
import { cn } from '~/shared/utils';

type Props = Pick<
GetProjectsQueryResult[number],
'title' | 'slug' | 'thumbnail' | 'headline' | 'services'
>;

function ProjectCard({ title, slug, headline, thumbnail, services }: Props) {
return (
<a href={`/projetos/${slug}`} class={'group'}>
<article
class={cn(['relative isolate', 'overflow-hidden', 'h-[500px] rounded-lg'])}
>
<img
src={urlFor(thumbnail).format('webp').maxWidth(1500).url()}
alt={title}
class={
'-z-10 absolute inset-0 object-cover object-center transition-all duration-300 ease-in-out group-hover:scale-110'
}
/>

<section class="flex size-full flex-col justify-end bg-gradient-to-b from-transparent to-gray-900/90 px-6 py-8 text-gray-50">
<h3 class={'font-semibold text-white'}>{title}</h3>

<Show when={headline}>
<p class={'mt-1 mb-4 line-clamp-3 text-sm'}>{headline}</p>
</Show>

<Show when={services}>
<ul>
<For each={services}>
{(service) => (
<li>
<a
class={
'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'
}
href={`/servicos/${service.slug}`}
>
{service.title}
</a>
</li>
)}
</For>
</ul>
</Show>
</section>
</article>
</a>
);
}

export { ProjectCard };
Brendonovich
Brendonovich3mo ago
It may be because you're destructuring props
Daniel Sousa @TutoDS
Already try both ways
import { For, Show } from 'solid-js';
import { urlFor } from '~/cms';
import type { GetProjectsQueryResult } from '~/shared/types';
import { cn } from '~/shared/utils';

type Props = Pick<
GetProjectsQueryResult[number],
'title' | 'slug' | 'thumbnail' | 'headline' | 'services'
>;

function ProjectCard(props: Props) {
return (
<a href={`/projetos/${props.slug}`} class={'group'}>
<article
class={cn(['relative isolate', 'overflow-hidden', 'h-[500px] rounded-lg'])}
>
<img
src={urlFor(props.thumbnail).format('webp').maxWidth(1500).url()}
alt={props.title}
class={
'-z-10 absolute inset-0 object-cover object-center transition-all duration-300 ease-in-out group-hover:scale-110'
}
/>

<section class="flex size-full flex-col justify-end bg-gradient-to-b from-transparent to-gray-900/90 px-6 py-8 text-gray-50">
<h3 class={'font-semibold text-white'}>{props.title}</h3>

<Show when={props.headline}>
<p class={'mt-1 mb-4 line-clamp-3 text-sm'}>{props.headline}</p>
</Show>

<Show when={props.services}>
<ul>
<For each={props.services}>
{(service) => (
<li>
<a
class={
'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'
}
href={`/servicos/${service.slug}`}
>
{service.title}
</a>
</li>
)}
</For>
</ul>
</Show>
</section>
</article>
</a>
);
}

export { ProjectCard };
import { For, Show } from 'solid-js';
import { urlFor } from '~/cms';
import type { GetProjectsQueryResult } from '~/shared/types';
import { cn } from '~/shared/utils';

type Props = Pick<
GetProjectsQueryResult[number],
'title' | 'slug' | 'thumbnail' | 'headline' | 'services'
>;

function ProjectCard(props: Props) {
return (
<a href={`/projetos/${props.slug}`} class={'group'}>
<article
class={cn(['relative isolate', 'overflow-hidden', 'h-[500px] rounded-lg'])}
>
<img
src={urlFor(props.thumbnail).format('webp').maxWidth(1500).url()}
alt={props.title}
class={
'-z-10 absolute inset-0 object-cover object-center transition-all duration-300 ease-in-out group-hover:scale-110'
}
/>

<section class="flex size-full flex-col justify-end bg-gradient-to-b from-transparent to-gray-900/90 px-6 py-8 text-gray-50">
<h3 class={'font-semibold text-white'}>{props.title}</h3>

<Show when={props.headline}>
<p class={'mt-1 mb-4 line-clamp-3 text-sm'}>{props.headline}</p>
</Show>

<Show when={props.services}>
<ul>
<For each={props.services}>
{(service) => (
<li>
<a
class={
'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'
}
href={`/servicos/${service.slug}`}
>
{service.title}
</a>
</li>
)}
</For>
</ul>
</Show>
</section>
</article>
</a>
);
}

export { ProjectCard };
Daniel Sousa @TutoDS
If I click here it's working
No description
Daniel Sousa @TutoDS
But only after click, if I refresh the page the error appears again
No description
Brendonovich
Brendonovich3mo ago
Using a top-level fragment could also be causing it. You'll still want to avoid destructring props though Try changing the fragment to a div or something
Daniel Sousa @TutoDS
but I'm not destructuring props now
Brendonovich
Brendonovich3mo ago
Destructuring props is just a bad practice with Solid, as it can break reactivity
Daniel Sousa @TutoDS
Yeah I understand that, but if you see the code that I send now, I'm using props.<property>
Brendonovich
Brendonovich3mo ago
Yeah, my other suspicion is the top-level fragment
Daniel Sousa @TutoDS
But everytime I refresh the page I have that error Already try too and the error keeps appearing
Brendonovich
Brendonovich3mo ago
Hmm do you have a Suspense in app.tsx?
Daniel Sousa @TutoDS
Yes
export default function App() {
return (
<Router
preload={true}
root={(props) => (
<>
<Header />
<Suspense>{props.children}</Suspense>

<CtaSection />
</>
)}
>
<FileRoutes />
</Router>
);
}
export default function App() {
return (
<Router
preload={true}
root={(props) => (
<>
<Header />
<Suspense>{props.children}</Suspense>

<CtaSection />
</>
)}
>
<FileRoutes />
</Router>
);
}
Initially I suspect ti's because I'm trying to do pagination with the createPagination primitive (I'm using createAsync and on the documentation they are using createResource), but after comment that piece of code the erro keeps happening
Brendonovich
Brendonovich3mo ago
fwiw createAsync is just createResource under the hood. What happens if you return some dummy data inside createAsync instead of calling getProjects?
Daniel Sousa @TutoDS
I think I found the problem
{(service) => (
<li
class={
'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'
}
>
{/*<a*/}
{/* class={*/}
{/* 'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'*/}
{/* }*/}
{/* href={`/servicos/${service.slug}`}*/}
{/*>*/}
{service.title}
{/*</a>*/}
</li>
)}
{(service) => (
<li
class={
'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'
}
>
{/*<a*/}
{/* class={*/}
{/* 'inline-flex rounded-full border-white bg-gray-400/30 px-2 py-0.5 font-semibold text-gray-50 text-sm'*/}
{/* }*/}
{/* href={`/servicos/${service.slug}`}*/}
{/*>*/}
{service.title}
{/*</a>*/}
</li>
)}
I remove this <a> inside the main <a> of the card and works
Brendonovich
Brendonovich3mo ago
Ohhh interesting
Brendonovich
Brendonovich3mo ago
Yeah i think solid won't usually let you do <a> in <a>, maybe that check isn't being done on the server
No description
Daniel Sousa @TutoDS
Thanks @Brendonovich Now only need to figure out how to handle the pagination and put tabs to filter the content Any suggestion for handle query parameters like page and in this case filters It’s like a portfolio and I want to filter by service, showing like a tab for each service
Brendonovich
Brendonovich3mo ago
I'd just use useSearchParams and read/write through it
Daniel Sousa @TutoDS
Thanks
Madaxen86
Madaxen863mo ago
Also for anyone else finding this through search. I always use Suspense in components which have a createAsync primitive. This solves most of the hydration issues. Additionally you can also use a Show component so that children will only render once the promise has resolved.
mdynnl
mdynnl3mo ago
tbh the validation is pretty basic. it's probably better to pursue server/client comparison instead. it could also be worth/easy to just validate the ssr result which should catch cases like this one.
Daniel Sousa @TutoDS
But you add the suspense on the usage or inside the component as first element of the return?
Madaxen86
Madaxen863mo ago
In the same component which has the createAsync. They’re supposed to work together and this way you don’t suspense at the top of your app all the time. So any of the components which not rely on async data can be mounted right away e.g. layouts.
Want results from more Discord servers?
Add your server