Description
Steps to reproduce:
- Use
<style jsx>
. - Use React 18+.
- Pass
document
as the first argument to ReactDOM client'shydrateRoot
. - Have a hydration mismatch between server and client.
hydrateRoot
will try to recover by client rendering, removing all elements in the DOM, including <head>
. useInsertionEffect
runs before DOM mutations, so document.head
is undefined
when the effect fires.
Styled-jsx's StyleSheet.makeStyleTag
method assumes document.head
is not null
and is called during the useInsertionEffect
callback. This causes the error: Cannot read properties of null (reading 'appendChild')
and client rendering fails, resulting in a blank page.
My opinion:
Yes, the underlying issue is a bug in the app. Completely preventing hydration mismatch is difficult, especially when mismatches can be caused by unexpected browser behavior, extensions, and race conditions. The ability to log mismatches and fix them later is a good workflow for this as testing every case is not feasible.
The reason I think styled-jsx should change is due to it causing client rendering to fail. React will try to recover from a mismatch and styled-jsx prevents recovery. Instead of a performance regression, users experience a broken page.
I'm happy to discuss this and will open a PR "fixing" this issue. I don't expect the PR to be merged as I'm not familiar with the codebase and someone probably knows of a better way to address this.
Thanks!