Files
agent-skills/skills/vue-best-practices/reference/render-function-custom-directives.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

4.4 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Use withDirectives for Custom Directives in Render Functions LOW Custom directives require special handling with withDirectives helper best-practice
vue3
render-function
directives
custom-directive

Use withDirectives for Custom Directives in Render Functions

Impact: LOW - To apply custom directives to vnodes in render functions, use the withDirectives helper. Attempting to apply directives through props or other means will not work.

The withDirectives function wraps a vnode and applies directives with their values, arguments, and modifiers.

Task Checklist

  • Import withDirectives from 'vue' when using custom directives
  • Import or define the directive object
  • Pass directive as array: [directive, value, argument, modifiers]
  • Multiple directives can be applied at once

Incorrect:

import { h } from 'vue'

const vFocus = { mounted: (el) => el.focus() }

export default {
  setup() {
    return () => {
      // WRONG: Directives don't work as props
      return h('input', {
        'v-focus': true  // This doesn't work
      })
    }
  }
}

Correct:

import { h, withDirectives } from 'vue'

// Custom directive
const vFocus = {
  mounted: (el) => el.focus()
}

export default {
  setup() {
    return () => {
      // CORRECT: Use withDirectives
      return withDirectives(
        h('input'),
        [[vFocus]]  // Array of directive tuples
      )
    }
  }
}

Directive with Value, Argument, and Modifiers

The directive tuple format: [directive, value, argument, modifiers]

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

// Directive definition
// Usage in template: <div v-pin:top.animate="200">
const vPin = {
  mounted(el, binding) {
    console.log(binding.value)    // 200
    console.log(binding.arg)      // 'top'
    console.log(binding.modifiers) // { animate: true }

    el.style.position = 'fixed'
    el.style[binding.arg] = binding.value + 'px'
  }
}

export default {
  setup() {
    return () => withDirectives(
      h('div', 'Pinned content'),
      [
        // [directive, value, argument, modifiers]
        [vPin, 200, 'top', { animate: true }]
      ]
    )
  }
}

Multiple Directives

import { h, withDirectives } from 'vue'

const vFocus = { mounted: (el) => el.focus() }
const vTooltip = {
  mounted(el, { value }) {
    el.title = value
  }
}

export default {
  setup() {
    return () => withDirectives(
      h('input', { placeholder: 'Enter text' }),
      [
        [vFocus],
        [vTooltip, 'This is a tooltip']
      ]
    )
  }
}

Using Registered Directives

For globally or locally registered directives, use resolveDirective:

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

// Assuming v-focus and v-tooltip are registered globally
export default {
  setup() {
    return () => {
      const focus = resolveDirective('focus')
      const tooltip = resolveDirective('tooltip')

      return withDirectives(
        h('input'),
        [
          [focus],
          [tooltip, 'Helpful tip']
        ]
      )
    }
  }
}

Practical Example: Click Outside Directive

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

const vClickOutside = {
  mounted(el, binding) {
    el._clickOutside = (event) => {
      if (!el.contains(event.target)) {
        binding.value(event)
      }
    }
    document.addEventListener('click', el._clickOutside)
  },
  unmounted(el) {
    document.removeEventListener('click', el._clickOutside)
  }
}

export default {
  setup() {
    const isOpen = ref(true)
    const closeDropdown = () => { isOpen.value = false }

    return () => isOpen.value
      ? withDirectives(
          h('div', { class: 'dropdown' }, 'Dropdown content'),
          [[vClickOutside, closeDropdown]]
        )
      : null
  }
}

JSX with Directives

Directives in JSX also require withDirectives:

import { withDirectives } from 'vue'

const vFocus = { mounted: (el) => el.focus() }

export default {
  setup() {
    return () => withDirectives(
      <input placeholder="Search..." />,
      [[vFocus]]
    )
  }
}

Reference