Skip to content

Intlayer html function rendering duplicate nested tags causing invalid HTML and NextJS hydration issues. #393

@jvst-aj

Description

@jvst-aj

Today I started experimenting with Intlayer on NextJS 16, after following the setup from Intlayer docs, I noticed some errors on my NextJS application when using the html function to render some components. Here are two examples that cause issues:

The first one is using the use method on description and passing it a component for rendering, when you inspect the output on the browser, you will notice a nested tag of the same element (duplicate). This could produce invalid html when using "p" elements, NextJS Link component or a combination that could produce let's say a div inside a p element.

NOTE: FieldDescription component is what renders the p element.

// demo.content.ts

description: t({
  en: html("Already have an account? <SignIn>Sign in<SignIn/>"),
  es: html("¿Ya tienes una cuenta? <SignIn>Inicia sesión</SignIn>")
})
// component.ts

<FieldDescription>
  {t.description.use({
    SignIn: ({ children }) => {
      return <a>{children}</a>
    },
  })}
</FieldDescription>
// renders

<p>
  Already have an account?{" "}
  <a>
    Sign in
    <a></a>
  </a>
</p>

The same happens even without using "use" method.

// demo.content.ts

description: t({
  en: html("Already have an account? <a>Sign in<a/>"),
  es: html("¿Ya tienes una cuenta? <a>Inicia sesión</a>"),
}),
// component.ts

<FieldDescription>
  {t.description}
</FieldDescription>
// renders

<p>
  Already have an account?{" "}
  <a>
    Sign in
    <a></a>
  </a>
</p>

Here are some NextJS logs from the first example:

## Error Type
Console Error

## Error Message
In HTML, <a> cannot be a descendant of <a>.
This will cause a hydration error.

  ...
    <m fallback={[...]}>
      <f>
      <p locale="en">
        <SignUpForm>
          <div className="flex w-ful...">
            <form onSubmit={function onSubmit}>
              <FieldGroup>
                <div data-slot="field-group" className="group/fiel...">
                  <div className="flex flex-...">
                    <h1>
                    <FieldDescription>
                      <p data-slot="field-desc..." className={"text-lef..."}>
                        <s dictionaryKey="sign-up-form" dictionaryPath={undefined} keyPath={[...]} html={"Already ..."} ...>
                          <f dictionaryKey="sign-up-form" dictionaryPath={undefined} keyPath={[...]} html={"Already ..."} ...>
>                           <a>
>                             <a>
                  ...



    at a (<anonymous>:null:null)
    at SignUpForm (src/packages/auth-react/users/sign-up/components/form.tsx:95:30)
    at Page (src/app/[locale]/(auth)/sign-up/page.tsx:12:9)

## Code Frame
  93 |
  94 |             <FieldDescription>
> 95 |               {t.description.use({
     |                              ^
  96 |                 SignIn: ({ children }) => {
  97 |                   return <a>{children}</a>
  98 |                 },

Next.js version: 16.1.6 (Turbopack)
## Error Type
Recoverable Error

## Error Message
Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:

- A server/client branch `if (typeof window !== 'undefined')`.
- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.
- Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.

It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.

https://react.dev/link/hydration-mismatch


    at a (<anonymous>:null:null)
    at SignUpForm (src/packages/auth-react/users/sign-up/components/form.tsx:95:30)
    at Page (src/app/[locale]/(auth)/sign-up/page.tsx:12:9)

## Code Frame
  93 |
  94 |             <FieldDescription>
> 95 |               {t.description.use({
     |                              ^
  96 |                 SignIn: ({ children }) => {
  97 |                   return <a>{children}</a>
  98 |                 },

Next.js version: 16.1.6 (Turbopack)

This is my first day using Intlayer so I'm not sure if I am missing something, and I couldn't find anything related to this on the repo issues, intlayer docs or online. Does someone else have the same issue?

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions