Meituan-Dianping/mpvue · warning

Unknown custom element: <${tag}> - did you register the comp

Error message

Unknown custom element: <${tag}> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

What it means

During patching, Vue encountered an element tag that is not a registered component, not a reserved HTML element, and not in `ignoredElements`, so `config.isUnknownElement(tag)` returns true. The element renders as a plain unknown custom element (behaves like a generic inline element with no component logic).

Source

Thrown at src/core/vdom/patch.js:129

    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
      return
    }

    const data = vnode.data
    const children = vnode.children
    const tag = vnode.tag
    if (isDef(tag)) {
      if (process.env.NODE_ENV !== 'production') {
        if (data && data.pre) {
          inPre++
        }
        if (
          !inPre &&
          !vnode.ns &&
          !(config.ignoredElements.length && config.ignoredElements.indexOf(tag) > -1) &&
          config.isUnknownElement(tag)
        ) {
          warn(
            'Unknown custom element: <' + tag + '> - did you ' +
            'register the component correctly? For recursive components, ' +
            'make sure to provide the "name" option.',
            vnode.context
          )
        }
      }
      vnode.elm = vnode.ns
        ? nodeOps.createElementNS(vnode.ns, tag)
        : nodeOps.createElement(tag, vnode)
      setScope(vnode)

      /* istanbul ignore if */
      if (__WEEX__) {
        // in Weex, the default insertion order is parent-first.
        // List items can be optimized to use children-first insertion
        // with append="tree".
        const appendAsTree = isDef(data) && isTrue(data.appendAsTree)

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Register the component globally (`Vue.component('my-widget', MyWidget)`) or locally in the parent's `components` option
  2. Give recursive components a `name` option matching the tag
  3. Match the tag casing exactly (kebab-case in DOM templates, PascalCase allowed in SFC templates)
  4. If it's a native/Web Component, add it to `Vue.config.ignoredElements = [/^my-/]`

Example fix

// before
<template><my-widget /></template>
// after
import MyWidget from './MyWidget.vue'
export default {
  components: { MyWidget },
  template: '<my-widget />'
}
Defensive patterns

Strategy: validation

Validate before calling

function ensureRegistered (Vue, names) {
  names.forEach(name => {
    if (!Vue.options.components[name]) {
      throw new Error('Component not registered: ' + name)
    }
  })
}

Type guard

function isKnownTag (tag, components, reserved) {
  return Object.prototype.hasOwnProperty.call(components, tag) || reserved.includes(tag)
}

Prevention

When it happens

Trigger: Using `<my-widget>` in a template without `Vue.component('my-widget', ...)` or a local `components: { 'my-widget': ... }` registration; recursive component without a `name` option; unregistered kebab/case-mismatched component names; native Web Components not listed in `ignoredElements`.

Common situations: Case mismatch (`MyComponent` used as `<my-component>` without registration); forgetting `components: {}` local registration in SFC-less setups; upgrading from global registration to module-scoped components; using custom elements from other frameworks without configuring `Vue.config.ignoredElements`.

Related errors


AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02). Data as JSON: /api/errors/e54f130a3cc63846. Report an issue: GitHub.