recorded decision · public · no signup

accepted2026-06-23vuejs/rfcs

Summary

What was chosen

  • Vue 3.0 performs Custom Elements whitelisting during template compilation for improved performance.adr
  • Vue 3.0 configures Custom Elements whitelisting via compiler options instead of runtime configuration.adr
  • Vue 3.0 restricts the special `is` prop usage exclusively to the reserved `<component>` tag.adr
  • Vue 3.0 introduces a new `v-is` directive for 2.x use cases where `is` was used on native elements.adr

What was ruled out· 1

  • Vue 3.0 no longer configures Custom Elements whitelisting via runtime `Vue.config.ignoredElements`.adr

Constraints

  • A motivation for Vue 3.0 is to provide more performant native custom element support.adr
  • A motivation for Vue 3.0 is to improve support for customized built-in elements.adr

Consequences

  • In Vue 3.0, the `is` prop on normal components will behave like a standard prop, not a special directive.adr
  • In Vue 3.0, the `is` prop on plain elements is passed to `createElement` to support customized built-in elements.adr
  • The `v-is` directive requires a JavaScript string literal value to render a component by its registered name.adr

The recorded why

  • Start Date: 2020-03-25
  • Target Major Version: 3.x
  • Reference Issues: N/A

Summary

  • Breaking: Custom Elements whitelisting is now performed during template compilation, and should be configured via compiler options instead of runtime config.

  • Breaking: Restrict the special is prop usage to the reserved <component> tag only.

  • Introduce a new v-is directive to support 2.x use cases where is was used on native elements to work around native HTML parsing restrictions.

Motivation

Detailed design

§ Autonomous Custom Elements

In Vue 2.x, whitelisting tags as custom elements is done via Vue.config.ignoredElements. The downside is that when this config option is used, a check needs to be performed on every vnode creation call.

In Vue 3.0, this check is performed during template compilation. For example, given the following template:

<plastic-button></plastic-button>

The default generated render function code is (pseudo code):

function render() {
  const component_plastic_button = resolveComponent('plastic-button')
  return createVNode(component_plastic_button)
}

And it will emit a warning if no component named plastic-button was found.

If the user wishes to use a native custom element named plastic-button, the desired generated code should be:

function render() {
  return createVNode('plastic-button') // render as native element
}

To instruct the compiler to treat <plastic-button> as a custom element:

  • If using a build step: pass the isCustomElement option to the Vue template compiler. If using vue-loader, this should be passed via vue-loader's compilerOptions option:

    // in webpack config
    rules: [
      {
        test: /\.vue$/,
        use: 'vue-loader',
        options: {
          compilerOptions: {
            isCustomElement: tag => tag === 'plastic-button'
          }
        }
      }
      // ...
    ]
    
  • If using on-the-fly template compilation, pass it via app.config:

    const app = Vue.createApp(/* ... */)
    
    app.config.isCustomElement = tag => tag === 'plastic-button'
    

    Note the runtime config only affects runtime template compilation - it won't affect pre-compiled templates.

§ Customized Built-in Elements

The Custom Elements specification provides a way to use custom elements as Customized Built-in Element by adding the is attribute to a built-in element:

<button is="plastic-button">Click Me!</button>

Vue's usage of the is special prop was simulating what the native attribute does before it was made universally available in browsers. However, in 2.x it is interpreted as rendering a Vue component with the name plastic-button. This blocks the native usage of Customized Built-in Element mentioned above.

In 3.0, we are limiting Vue's special treatment of the is prop to the <component> tag only.

  • When used on the reserved <component> tag, it will behave exactly the same as in 2.x.

  • When used on normal components, it will behave like a normal prop:

    <foo is="bar" />
    
    • 2.x behavior: renders the bar component.
    • 3.x behavior: renders the foo component and passing the is prop.
  • When used on plain elements, it will be passed to the createElement call as the is option, and also rendered as a native attribute. This supports the usage of customized built-in elements.

    <button is="plastic-button">Click Me!</button>
    
    • 2.x behavior: renders the plastic-button component.

    • 3.x behavior: renders a native button by calling

      document.createElement('button', { is: 'plastic-button' })
      

§ v-is for In-DOM Template Parsing Workarounds

Note: this section only affects cases where Vue templates are directly written in the page's HTML.

When using in-DOM templates, the template is subject to native HTML parsing rules. Some HTML elements, such as <ul>, <ol>, <table> and <select> have restrictions on what elements can appear inside them, and some elements such as <li>, <tr>, and <option> can only appear inside certain other elements.

In 2.x we recommended working around with these restrictions by using the is prop on a native tag:

<table>
  <tr is="blog-post-row"></tr>
</table>

With the behavior change of is proposed above, we need to introduce a new directive v-is for working around these cases:

<table>
  <tr v-is="'blog-post-row'"></tr>
</table>

Note v-is functions like a dynamic 2.x :is binding - so to render a component by its registered name, its value should be a JavaScript string literal.

Adoption strategy

  • Compat build can detect use of config.ignoredElements and provide appropriate warning and guidance.

  • A codemod can automatically convert all 2.x non <component> tags with is usage to <component is> (for SFC templates) or v-is (for in-DOM templates).

Source: active-rfcs/0027-custom-elements-interop.md