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.
[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
- In
<script setup>, confirm the component is imported — the import is the registration. - Verify the import path resolves and default-vs-named matches the file (
.vuefiles are default exports). - Check the tag name against the registered name for typos and casing.
- For globally used components, confirm
app.component()runs beforeapp.mount(). - If it is a genuine web component, add it to
compilerOptions.isCustomElementinstead.
Frequently asked questions
Why does <script setup> say "Failed to resolve component"?
<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?
<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?
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.