P4sca1 / nuxt-headlessui

Headless UI integration for Nuxt. Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.
MIT License
168 stars 7 forks source link

Update to version 1.7.18 of healessui/vue #41

Closed gkkirilov closed 6 months ago

gkkirilov commented 8 months ago

HeadlessUI deployed a fix for Nuxt hydration error for generated ids.

Reference: https://github.com/tailwindlabs/headlessui/issues/2913

P4sca1 commented 8 months ago

This module uses ^1.0.0 syntax for the headless UI version, so that you can install whatever version you like and can update headless UI without updating this dependency. Just refresh your lock file, or install the latest version manually.

Remember that the fix requires you to add code to your app.vue file to provide the id generation function. This module can't do this for you due to limitations with the useId hook by nuxt. Headless UI 2 will add this feature by default using Vue 3.5 builtin useId function.

mwohlan commented 8 months ago

@P4sca1

I just made a reproduction because I still had mismatches after using the recommended fix. Turns out it only happens if I am using the Components through your module. Importing the components explicitly fixes the mismatches. You can easily check by un-commenting the imports.

https://stackblitz.com/edit/github-bbxtfw?file=app.vue

simonmaass commented 8 months ago

i can confirm this issue!

P4sca1 commented 8 months ago

I can reproduce the issue, but did not yet find the reason for it. My guess right now is a symbol mismatch due to different imports, but I am not sure yet. I will provide an update once I know more.

P4sca1 commented 8 months ago

I invested a few hours today, but was unable to find out why this is not working. For some reason the provided useId function is called on the server, but not on the client. So on the client the IDs are still generated using -i suffixes, leading to hydration mismatches. I also tried calling provideUseId in a plugin added by this module without success.

simonmaass commented 8 months ago

@P4sca1 yeah i also could the in the DOM that the ids with the -i suffixe where generated

gkkirilov commented 7 months ago

adding this to app.vue fixed it for me, at least until we get a proper fix

import { provideUseId } from '@headlessui/vue'

provideUseId(() => useId())
lights0123 commented 7 months ago

Looking at the network logs, it looks like my import statement within app.vue is fetching Headless UI from _nuxt/node_modules/.cache/vite/client/deps/@headlessui_vue.js—which is fully bundled—but the component from /_nuxt/node_modules/@headlessui/vue/dist/components/menu/menu.js. Therefore, there are two separate, incompatible copies of the library included. I also noticed a Vite log that it was "optimizing dependencies" when I added that line to app.vue for the first time.

To fix it, exclude the library from being optimized by Vite:

vite: {
    optimizeDeps: {
        exclude: ['@headlessui/vue'],
    },
},

It is my understanding that dependencies are not pre-bundled during production at all (i.e. this is a dev only problem), so this should have no performance or size impact on the final build.

P4sca1 commented 7 months ago

Interesting find. Because there are 2 different bundles, the symbols that get injected likely don't match, which is causing the issue. Thank you for posting a workaround. I will check if it makes sense to include it by default.

P4sca1 commented 6 months ago

This issue is likely related https://github.com/vitejs/vite/issues/3910 and mentions the same workaround.

P4sca1 commented 6 months ago

nuxt-headssui version 1.2.0 exposes a new provideHeadlessUseId composable, which should fix the issue. Add the following to your app.vue file in <script setup> (everything is auto-imported):

// Use SSR-safe IDs for Headless UI
provideHeadlessUseId(() => useId())
geofany commented 4 months ago

nuxt-headssui version 1.2.0 exposes a new provideHeadlessUseId composable, which should fix the issue. Add the following to your app.vue file in <script setup> (everything is auto-imported):

// Use SSR-safe IDs for Headless UI
provideHeadlessUseId(() => useId())

is this auto import ?

Screenshot 2024-06-02 at 08 31 07
P4sca1 commented 4 months ago

@geofany Ensure you are using Nuxt 2.10 or newer. If you have auto imports disabled, import the composables manually.

simonmaass commented 1 month ago

can this be useful now: https://blog.vuejs.org/posts/vue-3-5#useid

P4sca1 commented 1 month ago

can this be useful now: https://blog.vuejs.org/posts/vue-3-5#useid

Yes, but it needs to be integrated into @headlessui/vue directly.

P4sca1 commented 1 month ago

See https://github.com/tailwindlabs/headlessui/discussions/3457

P4sca1 commented 1 month ago

If you upgrade vue to 3.5.0 or newer and @headlessui/vue to 1.7.23 or newer, nuxt-headlessui will automatically use the native useId function provided by vue. There is no need to update nuxt-headlessui. You can remove provideHeadlessUseId() from your app.vue function. If you keep it, it will just do nothing.