Blog

Blog · Error Fixes

How to Fix Vue "Failed to Resolve Component"

[Vue warn]: Failed to resolve component means Vue found a component tag but nothing registered under that name. Here are the six causes — missing import, registration, casing — and the fix.

XCODX Team · 3 min read

[Vue warn]: Failed to resolve component: X means Vue’s compiler saw a tag it treats as a component, but at render time no component with that name was registered locally or globally — so it renders nothing. It is a dev-mode warning (silent in production); the real trigger is almost always a missing import or a name that does not match the registration.

The 6 common causes and their fixes

1. Missing import in <script setup>

In <script setup>, only imported bindings are available to the template — importing is registering.

<template><MyButton /></template>
<script setup>
// ✅ the import IS the registration
import MyButton from './MyButton.vue';
</script>

2. Not registered (Options / setup() API)

Outside <script setup>, you must add the component to the components option.

<script>
import MyButton from './MyButton.vue';
export default {
  components: { MyButton }, // ✅ don't forget this
};
</script>

3. Wrong import — default vs named export

A .vue single-file component is a default export; a named import gets undefined.

// ❌ file uses a default export -> MyButton is undefined
import { MyButton } from './MyButton.vue';
// ✅
import MyButton from './MyButton.vue';

4. A name / registration-key mismatch

Registering under one key and using another tag name breaks resolution.

// ❌ registered as 'my-button', used as <MyBtn>
export default { components: { 'my-button': MyButton } };
// template: <MyBtn />

// ✅ register the component; use either <MyButton> or <my-button>
export default { components: { MyButton } };

5. Global registration that runs after mount

app.component() must be called before app.mount().

// ❌ registered too late
const app = createApp(App);
app.mount('#app');
app.component('MyButton', MyButton);

// ✅ register, then mount
const app = createApp(App);
app.component('MyButton', MyButton);
app.mount('#app');

6. An async component that is not defined properly

A bare dynamic-import function is not a component — wrap it in defineAsyncComponent.

<script setup>
// ❌ a plain function, not a component
// const Chart = () => import('./Chart.vue');

// ✅
import { defineAsyncComponent } from 'vue';
const Chart = defineAsyncComponent(() => import('./Chart.vue'));
</script>

Quick diagnostic checklist

  1. In <script setup>, confirm the component is imported — the import is the registration.
  2. Verify the import path resolves and default-vs-named matches the file (.vue files are default exports).
  3. Check the tag name against the registered name for typos and casing.
  4. For globally used components, confirm app.component() runs before app.mount().
  5. If it is a genuine web component, add it to compilerOptions.isCustomElement instead.

Frequently asked questions

Why does <script setup> say "Failed to resolve component"?
In <script setup> only imported bindings are exposed to the template. If you use <MyButton> without import MyButton from "./MyButton.vue", nothing is registered under that name. Add the import.
Does component name casing matter in Vue?
Vue normalizes PascalCase and kebab-case to the same registration, so <MyButton> and <my-button> both work. The warning is about the registered name not existing or not matching — check the registration key and the import, not the tag casing.
Why does a web component trigger this warning?
Vue tries to resolve non-native tags as components first. For a real custom element, tell Vue to skip it via app.config.compilerOptions.isCustomElement (or your build tool’s compilerOptions.isCustomElement) rather than registering it.

Build and preview Vue components live in XCODX Studio — it compiles single-file components in the browser, so a registration mistake shows up immediately in the preview instead of a console you might miss.