[Bug?]: Hydration Mismatch for passed JSX elements
Duplicates
- [X] I have searched the existing issues
Latest version
- [X] I have tested the latest version
Current behavior 😯
When i pass jsx elements as a prop to my separation component I get hydration errors
export default function Home() {
return (
<main class="text-white">
{/* works --> */}
<Separate items={['hello 1']} />
<Separate items={['hello 1', 'hello 2']} />
{/* doesn't work --> */}
<Separate items={[<span>Hello</span>, <span>world</span>]} />
</main>
);
}
const Separate = (p: { items: JSX.Element[] }) => {
return (
<p>
<For each={p.items}>
{(item, i) =>
i() === p.items.length - 1 ? <>{item}</> : <>{item} • </>
}
</For>
</p>
);
};
Expected behavior 🤔
it should work without hydration errors "Hydration Mismatch. Unable to find DOM nodes for hydration key ..."
Steps to reproduce 🕹
Stackblitz link
Steps:
- Use this component:
export default function Home() {
return (
<main class="text-white">
{/* works --> */}
<Separate items={['hello 1']} />
<Separate items={['hello 1', 'hello 2']} />
{/* doesn't work --> */}
<Separate items={[<span>Hello</span>, <span>world</span>]} />
</main>
);
}
const Separate = (p: { items: JSX.Element[] }) => {
return (
<p>
<For each={p.items}>
{(item, i) =>
i() === p.items.length - 1 ? <>{item}</> : <>{item} • </>
}
</For>
</p>
);
};
Context 🔦
error "Hydration Mismatch. Unable to find DOM nodes for hydration key: 0-0-0-0-0-0-0-0-1-0-0-0-0-0-0-0-0-0-0-3-1-2"
Your environment 🌎
"dependencies": {
"@solidjs/router": "^0.13.3",
"@solidjs/start": "^1.0.2",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"solid-js": "^1.8.17",
"tailwindcss": "^3.4.3",
"vinxi": "^0.3.12"
},
"engines": {
"node": ">=18"
},
"devDependencies": {
"sass": "^1.77.6"
}
Update: if i pass a function that returns JSXElement instead of JSXElement instead, it works Although i don't think it's a fully expected behavior and I would still expect passing JSXElements to work i.e
<Separate items={[() => <span>Hello</span>, () => <span>world</span>]} />
This is expected with current state of hydration.
<Separate items={[<span>Hello</span>, <span>world</span>]} />
compiles to
_$createComponent(Separate, {
get items() {
return [_tmpl$(), _tmpl$2()];
}
})
and accessing p.items generates new elements.
as far as the hydration is concerned, there is nothing wrong with that as long as these elements can be found in the DOM.
👉 But new elements created while evaluating p.items.length won't exist in the dom hence the hydration error.
i() === p.items.length - 1 ? <>{item}</> : <>{item} • </>
this doesn't happen with
<Separate items={[() => <span>Hello</span>, () => <span>world</span>]} />
or
/* 🛑 use at your own risk */
const items = [<span>Hello</span>, <span>world</span>]
<Separate items={items} /> // no getter
or
function Separate(p) {
/* ❌ don't do this */
const items = p.items // just for the sake of the point
const slightlyCorrect = createMemo(() => p.items)
/* ✅ do this */
const correct = children(() => p.items)
}
@mdynnl how is this expected? From the DX point of view it doesn't make much sense and this behavior is not documented anywhere. Thanks for explaining on why it doesn't work due to some internal implementation details, however, IMO this is not an expected behavior, since i would expect SSR and SPA versions of Solid to work the same way
why can't i generate a new elements and not append them to the DOM? Why can't they be hydrated as part of the javascript?
Yeah.. this is hard in general. The code above would be not good even in a purely client side app.. recreating elements over and over again. I'm going to close this as a duplicate of the linked issue in Solid core repo. But in general don't do this. Use the children helper if you are going to access a JSX rendering prop more than once.