Files
stack/packages/mosaic/framework/skills/vue-best-practices/reference/render-function-event-modifiers.md
T
fargo 1a822493ba format: apply repo prettier (3.8.1) to the folded skills tree
963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
2026-08-19 14:37:17 -05:00

4.9 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Use withModifiers for Event Modifiers in Render Functions MEDIUM Manual modifier implementation is error-prone; use withModifiers helper best-practice
vue3
render-function
events
modifiers

Use withModifiers for Event Modifiers in Render Functions

Impact: MEDIUM - When using render functions, event modifiers like .stop, .prevent, or .self require special handling. Use Vue's withModifiers helper function instead of manually implementing modifier logic, which is error-prone.

In templates, you can use modifiers like @click.stop.prevent. In render functions, you must either use camelCase concatenation for simple modifiers or the withModifiers helper for more complex ones.

Task Checklist

  • Use camelCase concatenation for capture, once, and passive modifiers
  • Use withModifiers() helper for stop, prevent, self, and key modifiers
  • Import withModifiers from 'vue' when needed
  • Combine multiple modifiers by passing them as an array

Incorrect:

import { h } from 'vue';

export default {
  setup() {
    const handleClick = (e) => {
      // WRONG: Manual modifier implementation is error-prone
      e.stopPropagation();
      e.preventDefault();
      // ... actual handler logic
    };

    return () => h('button', { onClick: handleClick }, 'Click');
  },
};
// WRONG: Forgetting to handle modifier in complex scenarios
const handleDivClick = (e) => {
  // Intended: only trigger when clicking div itself, not children
  // This manual check is easy to get wrong
  if (e.target !== e.currentTarget) return;
  // ...
};

Correct:

import { h, withModifiers } from 'vue';

export default {
  setup() {
    const handleClick = () => {
      console.log('clicked!');
    };

    return () =>
      h(
        'button',
        {
          // CORRECT: Use withModifiers for stop and prevent
          onClick: withModifiers(handleClick, ['stop', 'prevent']),
        },
        'Click',
      );
  },
};

CamelCase Modifiers (No Helper Needed)

For capture, once, and passive modifiers, use camelCase concatenation:

import { h } from 'vue';

export default {
  setup() {
    const handler = () => console.log('event!');

    return () =>
      h('div', {
        // @click.capture -> onClickCapture
        onClickCapture: handler,

        // @keyup.once -> onKeyupOnce
        onKeyupOnce: handler,

        // @scroll.passive -> onScrollPassive
        onScrollPassive: handler,

        // @mouseover.once.capture -> onMouseoverOnceCapture
        onMouseoverOnceCapture: handler,
      });
  },
};

withModifiers Examples

import { h, withModifiers } from 'vue';

export default {
  setup() {
    const handleClick = () => console.log('clicked');
    const handleSubmit = () => console.log('submitted');

    return () =>
      h('div', [
        // .stop modifier
        h(
          'button',
          {
            onClick: withModifiers(handleClick, ['stop']),
          },
          'Stop Propagation',
        ),

        // .prevent modifier
        h(
          'form',
          {
            onSubmit: withModifiers(handleSubmit, ['prevent']),
          },
          [h('button', { type: 'submit' }, 'Submit')],
        ),

        // .self modifier - only trigger if event.target is the element itself
        h(
          'div',
          {
            onClick: withModifiers(handleClick, ['self']),
            style: { padding: '20px', background: '#eee' },
          },
          [h('button', "Click me (won't trigger parent)")],
        ),

        // Multiple modifiers
        h(
          'a',
          {
            href: '/path',
            onClick: withModifiers(handleClick, ['stop', 'prevent']),
          },
          'Link',
        ),
      ]);
  },
};

Key Modifiers

For keyboard events, use withKeys for key modifiers:

import { h, withKeys } from 'vue';

export default {
  setup() {
    const handleEnter = () => console.log('Enter pressed');
    const handleEscape = () => console.log('Escape pressed');

    return () =>
      h('input', {
        // @keyup.enter
        onKeyup: withKeys(handleEnter, ['enter']),

        // Multiple keys
        onKeydown: withKeys(handleEscape, ['escape', 'esc']),
      });
  },
};

JSX Equivalent

import { withModifiers, withKeys } from 'vue';

export default {
  setup() {
    const handleClick = () => console.log('clicked');

    return () => (
      <div>
        <button onClick={withModifiers(handleClick, ['stop'])}>Stop</button>

        <div onClick={withModifiers(handleClick, ['self'])}>
          <span>Child content</span>
        </div>

        <input onKeyup={withKeys(() => {}, ['enter'])} />
      </div>
    );
  },
};

Reference