Components

Components encapsulate template, data, methods, and lifecycle into reusable units.

Defining & registering

Register in components on the root app or any parent component. Children are available in that component's template and all its descendants.

JavaScript
createApp({
    components: {
        'user-card': {
            template: `<div class="card">{{ name }} — {{ role }}</div>`,
            data: { name: '', role: '' }
        }
    },
    template: `<user-card :name="currentUser" :role="'editor'" />`
}).mount('#app');

Props

Pass reactive data from parent to child via :propName. Parent changes flow down automatically.

JavaScript
<!-- Parent passes props with : prefix -->
<user-card :name="currentUser" :role="'editor'" />

// user-card component
{
    data: { name: '', role: '' },
    template: `<h3>{{ name }}</h3><span>{{ role }}</span>`
}

Emitting events — $emit

Child notifies parent without direct coupling.

JavaScript
// Child emits to parent
{
    methods: {
        close() { this.$emit('close'); },
        submit(data) { this.$emit('submit', data); }
    }
}

<!-- Parent listens -->
<modal @close="onClose" @submit="onSubmit" />

$dispatch — bubbling CustomEvent

Alternative to emit — fires a native CustomEvent that bubbles up the DOM. Any ancestor element can catch it with @event.

JavaScript
// Child dispatches a bubbling CustomEvent from $el
methods: {
    select(item) {
        this.$dispatch('item-selected', { id: item.id });
    }
}

<!-- Any ancestor can listen -->
<div @item-selected="onSelected">
    <product-list />
</div>

Slots

Default slot, named slots, and scoped slots (parent reads component-exposed data via v-slot).

HTML
<!-- Default slot <!-- Default slot -->
<my-panel><p>Content from parent</p></my-panel>

<!-- my-panel template <!-- my-panel template -->
<div class="panel"><slot></slot></div>

<!-- Named slots <!-- Named slots -->
<my-card>
    <span slot="header">Title</span>
    <p>Body content</p>
</my-card>

<!-- my-card template <!-- my-card template -->
<div>
    <header><slot name="header" /></header>
    <main><slot /></main>
</div>

<!-- Scoped slot — component exposes data up to parent <!-- Scoped slot — component exposes data up to parent -->
<item-list :items="products" v-slot="{ item, index }">
    {{ index }}. {{ item.name }}
</item-list>

Dynamic component

Destroys and mounts a new component when the value changes.

HTML
<!-- Mounts the component whose name matches activeView <!-- Mounts the component whose name matches activeView -->
<component :is="activeView" />

data: { activeView: 'tab-home' },
components: {
    'tab-home':     { template: `<p>Home</p>` },
    'tab-settings': { template: `<p>Settings</p>` }
}

$refs

HTML
<!-- On a native element: stores the HTMLElement <!-- On a native element: stores the HTMLElement -->
<input cv-ref="myInput" />

<!-- On a component: stores the child's reactive state <!-- On a component: stores the child's reactive state -->
<counter cv-ref="counter" />
<button @click="$refs.counter.reset()">Reset</button>

cv-model on components

JavaScript
<!-- cv-model on a component -->
<mi-input cv-model="search" />

// Expands to: :modelValue="search" @update:modelValue="search = $event"
// Child emits:
methods: {
    onInput(e) { this.$emit('update:modelValue', e.target.value); }
}

<!-- Multiple cv-model bindings -->
<editor cv-model:title="docTitle" cv-model:body="docBody" />

Other instance properties

PropertyDescription
$elRoot DOM element
$attrsNon-prop, non-event attributes. Set inheritAttrs: false to opt out of auto-inheritance.
$parentParent component's reactive state. Prefer props + emit when possible.
$slotsObject with true for each provided slot name. Use for conditional slot rendering.
← Template Syntax Reactivity →