solid-start icon indicating copy to clipboard operation
solid-start copied to clipboard

[Bug?]: reading JSX.Element prop outside JSX or > 1 time causes SSR hydration mismatch errors

Open illiaChaban opened this issue 2 months ago • 3 comments

Duplicates

  • [x] I have searched the existing issues

Latest version

  • [x] I have tested the latest version

Current behavior 😯

Hydration fails if you read element prop outside JSX like

const BrokenTest = (p: { children?: JSXElement }) => {
  console.log(p.children);
  return <div/>;
};

or more than once like p.children && <div>{p.children}</div> or <Show ...> equivalent

However, if you use a pipe or IIFE where underlying component reads the (jsx.element) signal only once inside template, it works.

Example:

(() => {
  const el = p.children;
  return  <div>{el && <div>{el}</div>}</div>
})()

or

{pipe(p.children, el => el && <div>{el}</div>}

I'm guessing SolidJs creates a hydration key on each read action and then fails to find it inside the DOM.

Another thing to note:

  • this seems to cause issues when passed children contains an html element (<div>hi</div>), but not if i just pass a string by itself

Expected behavior 🤔

hydration should work in all those cases

Steps to reproduce 🕹

stackblitz: https://codesandbox.io/p/devbox/423f4v

select any "not working" option and refresh the demo window -> see hydration error

Short example:

...
<Route href="/" component={() => <BrokenTest><div>boom</div></BrokenTest>} />
...

const BrokenTest = (p: { children?: JSXElement }) => {
  console.log(p.children);
  return <div/>;
}

// OR

const BrokenTest = (p: { children?: JSXElement }) => {
  return <div>{p.children && <div>{p.children}</div>}</div>
}

Context 🔦

No response

Your environment 🌎

all latest:
    "@solidjs/meta": "^0.29.4",
    "@solidjs/router": "^0.15.3",
    "@solidjs/start": "^1.2.0",
    "solid-js": "^1.9.9",

illiaChaban avatar Oct 21 '25 19:10 illiaChaban

Is there a github repo i can use to replicate this?

brenelz avatar Oct 24 '25 02:10 brenelz

I feel this is intended behavior. I think you have to use the children helper.

https://docs.solidjs.com/reference/component-apis/children

brenelz avatar Oct 24 '25 02:10 brenelz

@brenelz there's a stackblitz link you to project you can fork (top right corner): https://codesandbox.io/p/devbox/423f4v

I don't find this behavior intuitive. In my mind, when i pass <div/> as props.children or props.left (e.g. <Header left={<div/>}), the div should be created immediately and only once. Subsequent access through props.left should merely return the created element, not recreate it every time.

However, even assuming this behavior is correct, I still don't believe it should result in hydration errors. The div being console.logged is not rendered in a template and is absent from the resulting HTML, so why is it being hydrated?

P.S. I would also expect the <Show /> component to handle cases like this out of the box without needing for utilities. Seems like a very common use case that results in a confusing error

illiaChaban avatar Oct 24 '25 17:10 illiaChaban

Using the children helper can temporarily solve this.

g-mero avatar Dec 25 '25 08:12 g-mero