honox icon indicating copy to clipboard operation
honox copied to clipboard

Nested islands hydrate error 2

Open yusukebe opened this issue 10 months ago • 1 comments

What version of HonoX are you using?

0.1.15

What steps can reproduce the bug?

If we have the following an island component:

// app/islands/badge.tsx
import { PropsWithChildren } from 'react'

export default function Badge({ name, children }: PropsWithChildren<{ name: string }>) {
  return (
    <div>
      Hey {name}
      <hr />
      {children}
    </div>
  )
}

Then, specify the same island as a child of the island:

// app/routes/index.tsx
import { createRoute } from 'honox/factory'
import Badge from '../islands/badge'

export default createRoute((c) => {
  const name = c.req.query('name') ?? 'Hono'
  return c.render(
    <>
      <Badge name="parent">
        <Badge name="child" />
      </Badge>
    </>
  )
})

What is the expected behavior?

It does not throw errors.

What do you see instead?

It seems to be rendered correctly, but a hydration error will be shown with React:

CleanShot 2024-04-21 at 21 08 26@2x

This occurs because the HTML rendered by the server is different from the result hydrated by the client:

Server-rendered HTML:

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script type="module" src="/app/client.ts"></script>
  </head>
  <body>
    <honox-island component-name="/islands/badge.tsx" data-serialized-props='{"name":"parent"}'>
      <div>
        Hey
        <!-- -->
        parent
        <hr />
        <honox-island component-name="/islands/badge.tsx" data-serialized-props='{"name":"child"}'>
          <div>
            Hey
            <!-- -->
            child
            <hr />
          </div>
        </honox-island>
      </div>
      <template data-hono-template="">
        <honox-island component-name="/islands/badge.tsx" data-serialized-props='{"name":"child"}'>
          <div>
            Hey
            <!-- -->
            child
            <hr />
          </div>
        </honox-island>
      </template>
    </honox-island>
  </body>
</html>
<script>
  import('/@vite/client')
</script>

The elements rendered by client:

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script type="module" src="/app/client.ts"></script>
  </head>
  <body data-new-gr-c-s-check-loaded="14.1168.0" data-gr-ext-installed="">
    <honox-island
      component-name="/islands/badge.tsx"
      data-serialized-props='{"name":"parent"}'
      data-hono-hydrated="true"
      ><div>
        Hey parent
        <hr />
        <div>
          Hey child
          <hr />
        </div></div
    ></honox-island>
    <script>
      import('/@vite/client')
    </script>
  </body>
</html>

CleanShot 2024-04-21 at 21 12 29@2x

The minimal project to reproduce it: https://github.com/yusukebe/honox-react-nested-islands/tree/island-in-island

Additional information

#151 solved the problem of normal components in the island, but did not solve this problem.

I think the resulting HTML from the client hydrate should be the same as what the server rendered.

yusukebe avatar Apr 21 '24 12:04 yusukebe

What about the following PR approach? https://github.com/honojs/honox/pull/161

The errors in the following projects will be resolved, and the expected results will be achieved. https://github.com/yusukebe/honox-react-nested-islands/tree/island-in-island

usualoma avatar Apr 29 '24 12:04 usualoma

This was fixed! Thanks @usualoma !

yusukebe avatar May 06 '24 20:05 yusukebe