Fix: withDirectives Can Only Be Used Inside Render Functions in Vue.js

Error message:
withDirectives can only be used inside render functions.
Directives 2025-01-25

What Causes This Error?

This error occurs when you call withDirectives() outside of a render function context. The withDirectives helper is designed to apply directives to vnodes during the render process.

The Problem

import { h, withDirectives, resolveDirective } from 'vue'

// ❌ Called outside render context
const vFocus = resolveDirective('focus')
const vnode = withDirectives(h('input'), [[vFocus]]) // Error!

export default {
  setup() {
    // Component setup
  }
}

The Fix

Use Inside Render Function

import { h, withDirectives, resolveDirective } from 'vue'

export default {
  setup() {
    // ✅ Return render function
    return () => {
      const vFocus = resolveDirective('focus')
      return withDirectives(
        h('input', { type: 'text' }),
        [[vFocus]]
      )
    }
  }
}

With JSX

import { withDirectives, resolveDirective } from 'vue'

export default {
  setup() {
    return () => {
      const vFocus = resolveDirective('focus')
      // ✅ Inside render function
      return withDirectives(
        <input type="text" />,
        [[vFocus]]
      )
    }
  }
}

Common Scenarios

Applying v-show

import { h, withDirectives, vShow, ref } from 'vue'

export default {
  setup() {
    const isVisible = ref(true)

    return () => withDirectives(
      h('div', 'Content'),
      [[vShow, isVisible.value]]
    )
  }
}

Custom Directive

import { h, withDirectives, resolveDirective } from 'vue'

// Register directive first
app.directive('tooltip', {
  mounted(el, binding) {
    el.title = binding.value
  }
})

export default {
  setup() {
    return () => {
      const vTooltip = resolveDirective('tooltip')

      return withDirectives(
        h('button', 'Hover me'),
        [[vTooltip, 'This is a tooltip']]
      )
    }
  }
}

With Directive Modifiers

import { h, withDirectives, resolveDirective } from 'vue'

export default {
  setup() {
    return () => {
      const vCustom = resolveDirective('custom')

      // [directive, value, argument, modifiers]
      return withDirectives(
        h('div', 'Content'),
        [[vCustom, 'value', 'arg', { modifier1: true, modifier2: true }]]
      )
    }
  }
}

Multiple Directives

import { h, withDirectives, vShow, resolveDirective, ref } from 'vue'

export default {
  setup() {
    const isVisible = ref(true)

    return () => {
      const vFocus = resolveDirective('focus')
      const vTooltip = resolveDirective('tooltip')

      return withDirectives(
        h('input', { type: 'text' }),
        [
          [vShow, isVisible.value],
          [vFocus],
          [vTooltip, 'Enter text here']
        ]
      )
    }
  }
}

v-model with withDirectives

import { h, withDirectives, vModelText, ref } from 'vue'

export default {
  setup() {
    const text = ref('')

    return () => withDirectives(
      h('input', {
        'onUpdate:modelValue': (val) => { text.value = val }
      }),
      [[vModelText, text.value]]
    )
  }
}

Functional Component

import { h, withDirectives, vShow } from 'vue'

// ✅ Functional component returns vnode directly
const ConditionalBox = (props, { slots }) => {
  return withDirectives(
    h('div', { class: 'box' }, slots.default?.()),
    [[vShow, props.visible]]
  )
}

ConditionalBox.props = ['visible']

Local Directive Definition

import { h, withDirectives } from 'vue'

// Define directive locally
const focusDirective = {
  mounted: (el) => el.focus()
}

export default {
  setup() {
    return () => withDirectives(
      h('input'),
      [[focusDirective]] // Use directive object directly
    )
  }
}

Alternative: Use Template

If you don’t need render functions, use template syntax:

<template>
  <!-- Simpler for most cases -->
  <input v-focus v-tooltip="'Tooltip text'" />
</template>

<script setup>
// Directive will be auto-resolved
</script>

Quick Checklist

  • Call withDirectives inside a render function
  • Return render function from setup() when using withDirectives
  • Use resolveDirective() for registered directives
  • Built-in directives: vShow, vModelText, vModelCheckbox, etc.
  • Directive format: [directive, value, argument, modifiers]
  • Consider template syntax for simpler cases