Subscribe on changes!

Very strange bug in v-if and v-else !!

avatar
Oct 6th 2023

Vue version

3.3.4

Link to minimal reproduction

https://codesandbox.io/p/sandbox/epic-driscoll-dkznwy

Steps to reproduce

What is expected?

What is actually happening?

System Info

System:
    OS: Linux 6.5 Arch Linux
    CPU: (4) x64 Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
    Memory: 1.62 GB / 7.64 GB
    Container: Yes
    Shell: 5.9 - /usr/bin/zsh
  Binaries:
    Node: 20.8.0 - /usr/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 10.1.0 - /usr/bin/npm
  Browsers:
    Brave Browser: 114.1.52.126

Any additional comments?

https://github.com/vuejs/core/assets/53816722/60b3d727-c345-4ae8-8b76-4a01a87453dc

Explain @yyx990803 ...

avatar
Oct 6th 2023

Please provide a minimal reproduction (no Nuxt and other external libraries) and take the time to describe your issue.

avatar
Oct 8th 2023

I think he wants to use Vue events like @click inside the v-if and v-else block

@amirhossein-fzl See this playground, Also this is not the best practice to do something like this

SFC playground


for Icons use @iconify/tailwind (pure css icons) or @iconify/vue (Vue icon component) or @iconify-icon (Web component) instead

avatar
Oct 8th 2023

The root of your issue is that HTML inserted via v-html is not part of the diff-check during hydration (nor is is diffed during rendering).

So when the server renders a different icon than the client (which happens here as the server has no knowledge of the user's previously chosen dark/light mode preference), that goes unnoticed during hydration.

A simple solution would be to only render the icon client-side, as the state coming from the server would be wrong 50% of the time anyway. I believe nuxt provides a handy <ClientOnly> wrapper component for that.

avatar
Oct 9th 2023

This is happening because the content rendered on the server-side with Nuxt is not what is initially rendered on the client-side.

Since your component for the switch uses useDark() which in turns uses localStorage, it will always be light mode on the server and dark mode on the client (if it was selected).

You may, therefore, use <ClientOnly /> component to wrap this component, since server-side rendering it doesn't make sense anyway since it is dealing with a client-side concern.

This is not a bug with Vue or Nuxt.

avatar
Oct 9th 2023

I read all your comments and I think <ClientOnly /> is the answer in Nuxt. Although this is related to Next, I changed my project to vue and vite...

avatar
Oct 14th 2023

A ClientOnly Component is pretty simple, basically render the slot only after mounted:

Playground Example

So with that as a more general solution, I think I can close this.