Why do I get this hydration warning when using useState in Nuxt 3?
Asked Answered
R

2

8

MRE: https://github.com/Morpheu5/nuxt3-hydration-mre (was https://box.morpheu5.net/s/H6WXBfCebrRyTa8)

According to the docs, useState in NuxtJS 3 is supposed to be the SSR-friendly lightweight way of creating state. I need to use this so that any page/[somepage].vue can set some data to be used by layout/default.vue. This is a page:

<template>
    <div>Some page content.</div>
</template>

<script setup>
    useState('page_title', () => 'Some page title');
</script>

and this is the default layout:

<template>
    <div>
        <h1>{{ page_title }}</h1>
        <slot />
    </div>
</template>

<script setup>
    const page_title = useState('page_title');
</script>

When I run this in development mode, I get this warning in my browser's console:

runtime-core.esm-bundler.js:38 [Vue warn]: Hydration text content mismatch in <h1>:
- Client: 
- Server: Some page title 
  at <Default > 
  at <AsyncComponentWrapper > 
  at <BaseTransition mode="out-in" appear=false persisted=false  ... > 
  at <Transition name="layout" mode="out-in" > 
  at <Anonymous> 
  at <App key=1 > 
  at <NuxtRoot>

I know it's just a warning, and I don't get the warning if I generate and deploy statically (which is my goal anyway) but I was wondering if this is indicative of a deeper issue somewhere that I'm overlooking.

Also worth pointing out that the generated html does not "pre-render" the default value that I specify in the page, but this may just be my own misconception of how this works and I may be wrong to expect this to happen.

Rainstorm answered 17/8, 2022 at 20:15 Comment(0)
P
12

The hydration warning is caused by the fact that the server had rendered an empty <h1> where the client renders some text in it.

On SSR passing up state (e.g. here from page to layout) won't work.

You can avoid the hydration error by wrapping the title with <client-only>...</client-only> which will disable the SSR for this part. Thus there is no surprise (aka hydration warning) that the content differs.

Premedical answered 8/9, 2022 at 10:20 Comment(3)
What is the best practice in such cases?Rexanne
That is the best practice, at least that' what I know from the Mastering Nuxt 3 Course.Boutwell
@DarkoRomanov What I do is have a named slot in the layout, then use <NuxtLayout><template #my-slot> my page title </template> page content </NuxtLayout> in the page (instead of in app.vue). Or use a prop instead of a slot if you want. The benefit is that it's SSR friendlyIngeborgingelbert
A
0

The hydration mismatch error occurs when the HTML on the server-side and client-side do not match. There are multiple reasons for this mismatch, and one common reason is inconsistent data, as in your example.

You are using useState to reference the value of page_title twice, once with an initialization function and once without. If you want to use the same key state across components, it is recommended to use pinia, which allows multiple components to use the same state instance. This can prevent hydration mismatch caused by different initialization values.

However, there are other scenarios, such as using pinia-plugin-persistedstate and setting the persistence to localStorage, which can also cause data inconsistency between the server-side and client-side. To solve this issue, you can define a local variable within the component, initialize it with a fixed value, and then update it by watching piniaState in the onMounted hook:

<template>
   <p>{{name}}</p>
</template>
<script setup lang="ts">
const name = ref('init value');
onMounted(() => {
  watch(() => store.name, (new_val) => {
    name.value = new_val
  }, {immediate: true})
})
</script>

For more explanations about hydration mismatch, you can refer to the official documentation: https://vuejs.org/guide/scaling-up/ssr.html#hydration-mismatch

Antimicrobial answered 24/7, 2023 at 13:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.