Introduction

When Vue encounters a custom element in a template that has not been registered as a component, it logs a warning and renders nothing:

bash
[Vue warn]: Unknown custom element: <user-profile> - did you register the
component correctly? For recursive components, make sure to provide the "name" option.

This is one of the most common errors when building Vue applications, especially as the component tree grows.

Symptoms

  • Console warning: "Unknown custom element" for a component tag
  • Component renders as an empty HTML element or not at all
  • Template shows the component tag but no content appears
  • Error occurs after refactoring component file locations or names
  • Works in some components but not others

Common Causes

  • Component imported but not added to the components option
  • Import path is incorrect, resulting in undefined being imported
  • Component name does not match the template tag (case sensitivity)
  • Circular dependency between components causing one to be undefined at registration time
  • Global component registration happens after the component that uses it is created

Step-by-Step Fix

  1. 1.Register the component locally in the parent component:
  2. 2.```javascript
  3. 3.// ParentComponent.vue
  4. 4.import UserProfile from './UserProfile.vue';

export default { name: 'ParentComponent', components: { UserProfile, // Registers as <user-profile> }, }; ```

  1. 1.Verify the import is not undefined:
  2. 2.```javascript
  3. 3.import UserProfile from './UserProfile.vue';
  4. 4.console.log(UserProfile); // Should be a component object, not undefined

// If undefined, check the export in UserProfile.vue: // Should have: export default { ... } // Not: module.exports = { ... } (unless using CommonJS) ```

  1. 1.Check component naming conventions. Vue accepts both kebab-case and PascalCase:
  2. 2.```javascript
  3. 3.// Local registration
  4. 4.components: {
  5. 5.UserProfile, // Use as <user-profile> or <UserProfile>
  6. 6.'my-button': MyButton, // Use as <my-button>
  7. 7.}
  8. 8.`
  9. 9.For global registration, register before creating the Vue app:
  10. 10.```javascript
  11. 11.// main.js
  12. 12.import Vue from 'vue';
  13. 13.import BaseButton from './components/BaseButton.vue';
  14. 14.import BaseIcon from './components/BaseIcon.vue';

// Register globally Vue.component('BaseButton', BaseButton); Vue.component('BaseIcon', BaseIcon);

new Vue({ render: h => h(App), }).$mount('#app'); ```

  1. 1.Resolve circular dependencies by lazy loading one of the components:
  2. 2.```javascript
  3. 3.// ComponentA.vue
  4. 4.export default {
  5. 5.components: {
  6. 6.ComponentB: () => import('./ComponentB.vue'),
  7. 7.},
  8. 8.};
  9. 9.`

Prevention

  • Use an IDE with Vue language support to catch missing component registrations
  • Run vue-tsc or eslint-plugin-vue to statically analyze component usage
  • Establish a naming convention (PascalCase for imports, kebab-case in templates)
  • Use auto-import plugins like unplugin-vue-components for automatic local registration
  • Keep a component registry document tracking which components are global vs local
  • Test components in isolation to verify they render independently
  • Add a pre-commit hook that runs vue-cli-service lint to catch registration issues early