Search code examples
vue.jsnuxt.jsvuejs3custom-directive

Custom Vue 3 directive updates element's style only after it's been mounted in Nuxt 3


I created a custom Vue 3 directive v-visibility to mimic the behavior of the native v-show and v-if but for the visibility CSS property.

app.directive('visibility', (el: HTMLElement, binding: DirectiveBinding<boolean>) => {
    el.style.visibility = binding.value ? 'visible' : 'hidden'
})

The problem is that when the binding value is false, the element's styles are updated only after it's been mounted and therefore, there is a flash because it is always visible at first.

I tried using the non-shorthand directive syntax to set the style during created as well as beforeMount but the result was the same.

Am I doing something wrong or is it just not possible to do this with a custom directive?

Edit: Here is a reproduction: https://stackblitz.com/edit/nuxt-starter-atssfn?file=plugins%2Fv-visibility.ts

Edit 2: I should have mentioned that I'm using Nuxt 3 with SSR.


Solution

  • I figured it out. After @EstusFlask mentioned that the problem is with SSR, I checked what the Vue documentation had to say about custom directives & SSR and found out that I need to use the getSSRProps hook.

    This is the updated directive (the whole Nuxt plugin), which works fine with SSR:

    export default defineNuxtPlugin((nuxtApp) => {
        nuxtApp.vueApp.directive('visibility', {
            beforeMount: handleVisibility,
            updated: handleVisibility,
            getSSRProps: (binding) => {
                return {
                    style: {
                        visibility: binding.value ? 'visible' : 'hidden',
                    },
                }
            },
        })
    })
    
    const handleVisibility = (el: HTMLElement, binding: DirectiveBinding<boolean>) => {
        el.style.visibility = binding.value ? 'visible' : 'hidden'
    }