# Vue User Guide

Vue (`vue`) is **the progressive JavaScript framework for building modern web UI**. This guide covers everything you need to *use* Vue in your applications: installing it, creating reactive state, building components, wiring up the template syntax, rendering on the server, and shipping framework-grade widgets as native custom elements.

This document is built directly from the source of the `vuejs/core` monorepo at version **3.5.35**. Written from the package entry points and implementation. Where Vue exposes the same function from several packages, this guide tells you which import path to use.

> [!NOTE]
> `vuejs/core` is the home of the core Vue runtime and compiler. It is published to npm as the `vue` package, plus a family of scoped packages (`@vue/reactivity`, `@vue/runtime-core`, `@vue/runtime-dom`, `@vue/server-renderer`, `@vue/compiler-sfc`, and more). When this guide says "import from `vue`", it refers to the umbrella package that re-exports the runtime + compiler for browser apps.

---

## 1. Quick Start

The fastest way to a running Vue app is the official scaffolding tool, which sets up Vite, single-file components, and sensible defaults.

```bash
npm create vue@latest
```

```text
Vue.js - The Progressive JavaScript Framework

✔ Project name: … my-app
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
...
Scaffolding project in ./my-app...
Done.
```

Then install and start the dev server:

```bash
cd my-app
npm install
npm run dev
```

```text
  VITE v8.0.0  ready in 312 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
```

If you just want to try Vue on a single HTML page with no build step, load the global build from a CDN and call `createApp`:

```html
<div id="app">{{ message }}</div>

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
  const { createApp, ref } = Vue

  createApp({
    setup() {
      const message = ref('Hello Vue!')
      return { message }
    },
  }).mount('#app')
</script>
```

> [!TIP]
> The CDN snippet above uses the **full build** (`vue.global.js`), which includes the template compiler so that the in-DOM `{{ message }}` template can be compiled in the browser. For production apps with a build step, you use the **runtime-only build**, where templates are compiled ahead of time. See [Choosing a Build](#22-choosing-a-build).

---

## 2. Installation

### 2.1 Install from npm

```bash
npm install vue
```

```text
added 1 package in 1s
```

Vue ships as an ES module and a CommonJS module, with TypeScript declarations included. The published `vue` package depends on the internal Vue packages (`@vue/runtime-dom`, `@vue/compiler-dom`, `@vue/compiler-sfc`, `@vue/server-renderer`, `@vue/shared`), so a single install brings the whole framework.

> [!NOTE]
> Vue's npm package marks `typescript` as an **optional peer dependency**. You do not need TypeScript to use Vue, but if you have it installed, Vue's single-file-component tooling can use it for type-aware compilation of `<script setup lang="ts">`.

### 2.2 Choosing a build

Vue publishes several build formats from the same source. The right one depends on whether you use a bundler and whether you compile templates ahead of time.

| Build file | Format | Includes compiler? | Use when |
|---|---|---|---|
| `vue.runtime.esm-bundler.js` | ESM (bundler) | No | Default for Vite/webpack/Rollup apps with SFCs (the `module` entry) |
| `vue.esm-bundler.js` | ESM (bundler) | Yes | Bundler apps that compile templates at runtime |
| `vue.esm-browser.js` | ESM (browser) | Yes | Native `<script type="module">` with no bundler |
| `vue.runtime.esm-browser.js` | ESM (browser) | No | Native ESM, templates precompiled |
| `vue.global.js` | IIFE (`window.Vue`) | Yes | Plain `<script>` tag, dev |
| `vue.global.prod.js` | IIFE, minified | Yes | Plain `<script>` tag, production |
| `vue.cjs.js` / `vue.cjs.prod.js` | CommonJS | Yes | Node.js `require()` (SSR, tests) |

> [!IMPORTANT]
> The package's default `import` entry resolves to `vue.runtime.esm-bundler.js` — the **runtime-only** build. This is intentional: in a real app your `.vue` single-file components are compiled to render functions at build time, so the in-browser compiler is dead weight. If you pass a string `template` option at runtime with a runtime-only build, Vue cannot compile it and warns you (see [Troubleshooting](#error-component-provided-template-option-but-runtime-compilation-is-not-supported)).

### 2.3 Bundler feature flags

When you use a bundler build, Vue exposes three global compile-time flags you should define for the smallest, most correct production bundle. Define them in your bundler config (Vite's `@vitejs/plugin-vue` sets sensible defaults automatically):

| Flag | Default | Effect |
|---|---|---|
| `__VUE_OPTIONS_API__` | `true` | Set to `false` to tree-shake the Options API if you use only Composition API |
| `__VUE_PROD_DEVTOOLS__` | `false` | Set to `true` to enable devtools support in production builds |
| `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__` | `false` | Set to `true` to get detailed hydration-mismatch warnings in production |

```js
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  define: {
    __VUE_OPTIONS_API__: 'false',
    __VUE_PROD_DEVTOOLS__: 'false',
  },
})
```

> [!TIP]
> Turning off `__VUE_OPTIONS_API__` removes the Options-API compatibility layer from your bundle. Only do this if every component in your app (and your dependencies) uses the Composition API exclusively.

### 2.4 System requirements

| Requirement | Value |
|---|---|
| Node.js (for tooling/SSR) | `>= 20.0.0` |
| Browser target (default builds) | ES2016+ (modern evergreen browsers) |
| TypeScript (optional) | `~5.6` recommended for the SFC type macros |

---

## 3. Creating an Application

Every Vue app starts with `createApp` (or `createSSRApp` for server-rendered apps), imported from `vue`.

```js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.mount('#app')
```

`createApp` returns an **application instance**. You configure the app on this instance *before* calling `.mount()`.

### 3.1 The application instance API

| Method | Purpose |
|---|---|
| `app.mount(rootContainer)` | Render the root component into a DOM element or CSS selector string. Returns the root component instance. |
| `app.unmount()` | Tear down the mounted app. |
| `app.component(name, component?)` | Register (or retrieve) a global component. |
| `app.directive(name, directive?)` | Register (or retrieve) a global custom directive. |
| `app.use(plugin, ...options)` | Install a plugin. |
| `app.mixin(mixin)` | Apply a global mixin (Options API). |
| `app.provide(key, value)` | Provide a value to all descendant components via injection. |
| `app.runWithContext(fn)` | Run a callback with the app as the active injection context. |
| `app.config` | The app-level configuration object (see below). |
| `app.version` | The Vue version string this app was created with. |

```js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import MyButton from './components/MyButton.vue'

const app = createApp(App)

app.use(router)              // install a plugin
app.component('MyButton', MyButton)  // global component
app.provide('apiBase', '/api/v1')    // app-level injection

app.mount('#app')
```

> [!IMPORTANT]
> `app.mount()` clears the existing content of the target container before rendering. Anything already inside `#app` is removed (except when you intentionally use in-DOM templates with the full build).

### 3.2 Application configuration (`app.config`)

| Config key | Purpose |
|---|---|
| `errorHandler` | Global handler for uncaught errors from components, watchers, and lifecycle hooks. |
| `warnHandler` | Custom handler for dev-mode warnings. |
| `performance` | Set `true` to enable component init/render/patch performance markers in the browser timeline. |
| `compilerOptions` | Options passed to the in-browser template compiler (full build only). |
| `globalProperties` | An object whose properties are accessible on every component instance (e.g. `app.config.globalProperties.$http`). |
| `optionMergeStrategies` | Custom merge strategies for custom component options. |

```js
app.config.errorHandler = (err, instance, info) => {
  // report to your error tracking service
  console.error('Vue error:', err, info)
}

app.config.globalProperties.$formatDate = (d) => new Date(d).toLocaleDateString()
```

> [!CAUTION]
> `app.config.isCustomElement` is **deprecated**. To tell Vue which tags are custom elements, use `app.config.compilerOptions.isCustomElement` instead. With a runtime-only build, `isCustomElement` must be configured in your build tool (e.g. `@vitejs/plugin-vue`), because there is no runtime compiler to read the option.

---

## 4. Reactivity

Reactivity is the heart of Vue. The reactivity APIs are exported from both `vue` and the standalone `@vue/reactivity` package, so you can use them in plain JavaScript with no components at all.

### 4.1 Refs

A **ref** is a reactive container for a single value. Read and write it through its `.value` property.

```js
import { ref } from 'vue'

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1
```

```text
0
1
```

In templates, refs are **unwrapped** automatically — you write `{{ count }}`, not `{{ count.value }}`.

| Function | Purpose |
|---|---|
| `ref(value)` | Create a deeply-reactive ref. Objects assigned to a ref are made reactive. |
| `shallowRef(value)` | Create a ref that only tracks `.value` reassignment, not deep mutations. |
| `isRef(r)` | Return `true` if a value is a ref. |
| `unref(r)` | Return `r.value` if `r` is a ref, otherwise `r`. |
| `toRef(source, key?, default?)` | Create a ref that stays in sync with a property of a reactive object (or normalize a value/getter to a ref). |
| `toRefs(object)` | Convert all properties of a reactive object into refs, preserving reactivity (great for destructuring). |
| `toValue(source)` | Normalize a value, ref, or getter function to its plain value. |
| `proxyRefs(object)` | Return a proxy that auto-unwraps any refs accessed on it (used internally for `setup` returns). |
| `customRef(factory)` | Create a ref with custom dependency-tracking and trigger control (e.g. debounced refs). |
| `triggerRef(ref)` | Force effects depending on a `shallowRef` to run, after a deep mutation. |

```js
import { ref, toRefs, reactive } from 'vue'

const state = reactive({ first: 'Ada', last: 'Lovelace' })

// Destructuring a reactive object loses reactivity — use toRefs:
const { first, last } = toRefs(state)
first.value = 'Grace' // still updates state.first
```

> [!TIP]
> Use `customRef` for patterns like a debounced search box. The factory receives `track` and `trigger` callbacks so you control exactly when reads are recorded and when updates fire:
> ```js
> import { customRef } from 'vue'
> function useDebouncedRef(value, delay = 200) {
>   let timeout
>   return customRef((track, trigger) => ({
>     get() { track(); return value },
>     set(newValue) {
>       clearTimeout(timeout)
>       timeout = setTimeout(() => { value = newValue; trigger() }, delay)
>     },
>   }))
> }
> ```

### 4.2 Reactive objects

`reactive` makes an entire object (or array, `Map`, `Set`) deeply reactive using a JavaScript `Proxy`.

```js
import { reactive } from 'vue'

const book = reactive({ title: 'Vue Internals', pages: 320 })
book.pages = 321 // triggers any effect that read book.pages
```

| Function | Purpose |
|---|---|
| `reactive(target)` | Deeply reactive proxy of an object. |
| `shallowReactive(target)` | Only the root-level properties are reactive. |
| `readonly(target)` | A deeply read-only proxy; writes are blocked (and warn in dev). |
| `shallowReadonly(target)` | Only root-level properties are read-only. |
| `isReactive(value)` | `true` for `reactive`/`shallowReactive` proxies. |
| `isReadonly(value)` | `true` for `readonly`/`shallowReadonly` proxies. |
| `isProxy(value)` | `true` if the value is any reactive or readonly proxy. |
| `isShallow(value)` | `true` for shallow reactive/readonly proxies. |
| `toRaw(proxy)` | Return the original, non-reactive object behind a proxy. |
| `markRaw(object)` | Mark an object so it is *never* converted to a proxy. |

```js
import { reactive, readonly, toRaw, markRaw } from 'vue'

const original = { count: 0 }
const copy = reactive(original)
console.log(toRaw(copy) === original) // true

const config = markRaw({ thirdPartyInstance: new SomeBigClass() })
const state = reactive({ config }) // state.config stays raw
```

> [!WARNING]
> `reactive` only works on objects (including arrays and collection types). It cannot make a primitive value (`string`, `number`, `boolean`) reactive — use `ref` for those. Also note: destructuring a `reactive` object disconnects the resulting variables from reactivity; use `toRefs` to keep the link. Finally, replacing a `reactive` variable wholesale (`state = reactive({...})`) breaks reactivity because you discard the original proxy.

> [!NOTE]
> `reactive` silently skips objects that are not extensible (frozen or sealed) — calling `reactive` on a frozen object returns it unchanged rather than wrapping it. This is reported behavior (issue #14893).

### 4.3 Computed properties

`computed` creates a reactive ref whose value is derived from other reactive sources and cached until a dependency changes.

```js
import { ref, computed } from 'vue'

const count = ref(1)
const double = computed(() => count.value * 2)

console.log(double.value) // 2
count.value = 5
console.log(double.value) // 10
```

```text
2
10
```

Pass an object with `get` and `set` to create a **writable** computed:

```js
import { ref, computed } from 'vue'

const first = ref('Ada')
const last = ref('Lovelace')

const fullName = computed({
  get: () => `${first.value} ${last.value}`,
  set: (val) => {
    [first.value, last.value] = val.split(' ')
  },
})

fullName.value = 'Grace Hopper' // updates first and last
```

> [!TIP]
> A computed is *cached* — its getter only re-runs when a reactive dependency it read actually changes. This makes computed the right tool for expensive derivations. For side effects (logging, fetching), use a watcher instead. In development, pass debug options to inspect dependencies:
> ```js
> const double = computed(() => count.value * 2, {
>   onTrack(e) { /* called when a dep is tracked */ },
>   onTrigger(e) { /* called when a dep triggers */ },
> })
> ```

### 4.4 Watchers

Watchers run a callback in response to reactive changes. Vue offers `watch` (explicit sources) and `watchEffect` (automatic dependency tracking).

| Function | Behavior |
|---|---|
| `watch(source, cb, options?)` | Run `cb` when `source` changes. Lazy by default. |
| `watchEffect(effect, options?)` | Run `effect` immediately, re-running whenever any reactive value it reads changes. |
| `watchPostEffect(effect)` | `watchEffect` with `flush: 'post'` (runs after DOM updates). |
| `watchSyncEffect(effect)` | `watchEffect` with `flush: 'sync'` (runs synchronously on each change). |

```js
import { ref, watch, watchEffect } from 'vue'

const query = ref('')
const results = ref([])

// Explicit source, gets old and new values:
watch(query, async (newQuery, oldQuery) => {
  results.value = await search(newQuery)
})

// Automatic tracking — re-runs whenever query.value changes:
watchEffect(() => {
  console.log(`Searching for: ${query.value}`)
})
```

**`watch` options:**

| Option | Type | Purpose |
|---|---|---|
| `immediate` | `boolean` | Run the callback once immediately on creation. |
| `deep` | `boolean \| number` | Deeply traverse objects so nested mutations trigger the callback. A number limits traversal depth. |
| `flush` | `'pre' \| 'post' \| 'sync'` | When to run relative to component updates (default `'pre'`). |
| `once` | `boolean` | Run the callback at most once, then stop. |
| `onTrack` / `onTrigger` | `function` | Dev-only debugging hooks. |

You can watch multiple sources by passing an array, and use getters for reactive-object properties:

```js
import { reactive, watch } from 'vue'

const state = reactive({ a: 1, b: 2 })

watch(
  () => [state.a, state.b],
  ([a, b], [prevA, prevB]) => {
    console.log(`a: ${prevA} -> ${a}, b: ${prevB} -> ${b}`)
  },
)
```

**Cleaning up side effects.** Inside a watcher you can register cleanup work that runs before the next invocation (or when the watcher stops) with `onWatcherCleanup`:

```js
import { ref, watch, onWatcherCleanup } from 'vue'

const id = ref(1)
watch(id, (newId) => {
  const controller = new AbortController()
  fetch(`/api/items/${newId}`, { signal: controller.signal })
  onWatcherCleanup(() => controller.abort()) // cancel stale request
})
```

**Stopping a watcher.** `watch` and `watchEffect` return a stop handle. Calling it stops the watcher; it also exposes `.pause()` and `.resume()`:

```js
const stop = watchEffect(() => { /* ... */ })
stop.pause()   // temporarily stop reacting
stop.resume()  // resume
stop()         // stop permanently
```

> [!WARNING]
> When you set `immediate: true` together with an empty/never-changing array of sources, the callback runs once on creation but never again — there are no sources to react to. This is expected behavior and a common point of confusion (issue #14898). If you want a callback that re-runs on *any* dependency, use `watchEffect`.

> [!IMPORTANT]
> By default `watch` is **shallow** for `reactive` getters that return the object itself — but when you pass a `reactive` object *directly* as the source, Vue forces `deep: true`. To watch a specific nested value efficiently, pass a getter (`() => state.some.nested`) instead of the whole object.

### 4.5 Effect scopes

When you create watchers or computed values outside of a component (for example in a composable used at module scope), you can group their reactive effects into a **scope** so they can be disposed together.

```js
import { effectScope, ref, watchEffect } from 'vue'

const scope = effectScope()

scope.run(() => {
  const count = ref(0)
  watchEffect(() => console.log(count.value))
})

// Later — dispose every effect created inside the scope at once:
scope.stop()
```

| API | Purpose |
|---|---|
| `effectScope(detached?)` | Create a scope. A detached scope is not collected by an outer scope. |
| `scope.run(fn)` | Run a function with this scope active; effects created inside are captured. |
| `scope.stop()` | Stop all effects captured by the scope. |
| `getCurrentScope()` | Return the currently active effect scope, if any. |
| `onScopeDispose(fn)` | Register a callback to run when the current scope is stopped. |

> [!TIP]
> `onScopeDispose` is the composition-friendly way to clean up in a composable without relying on a component lifecycle hook — it works anywhere there is an active scope (including inside a component's `setup`).

### 4.6 Low-level effects

For advanced use, `effect` and `ReactiveEffect` let you run a function that re-executes whenever its reactive dependencies change — this is the primitive that watchers and the renderer are built on.

```js
import { reactive, effect, stop } from 'vue'

const state = reactive({ n: 0 })
const runner = effect(() => console.log(state.n))
// logs 0 immediately

state.n = 1 // logs 1

stop(runner) // stop re-running
```

> [!CAUTION]
> `effect` is a low-level API. In application code prefer `watchEffect`, which integrates with the component lifecycle and is automatically stopped when the component unmounts. Reach for `effect`/`ReactiveEffect`/`stop` only when building reactivity infrastructure.

---

## 5. Components

### 5.1 Single-File Components (SFCs)

The idiomatic way to author Vue components is the `.vue` single-file component, which co-locates template, logic, and scoped styles:

```vue
<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <button @click="count++">Count is: {{ count }}</button>
</template>

<style scoped>
button { font-weight: bold; }
</style>
```

`<script setup>` is compile-time sugar: every top-level binding (imports, variables, functions) is automatically exposed to the template. This is processed by `@vue/compiler-sfc` at build time.

### 5.2 The `setup` function and the Composition API

Without SFC sugar, a component defines a `setup()` function that returns the state and methods used by the template:

```js
import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    const increment = () => count.value++
    return { count, increment }
  },
})
```

`defineComponent` is a no-runtime-cost helper that gives you full TypeScript inference for component options. Use it whenever you define a component as a plain object (especially in TypeScript).

### 5.3 `<script setup>` compiler macros

Inside `<script setup>`, these **compiler macros** declare a component's interface. They are not imported at runtime — the SFC compiler transforms them — but Vue exports runtime stubs for typing and dev warnings.

| Macro | Purpose |
|---|---|
| `defineProps` | Declare the component's props. |
| `defineEmits` | Declare the events the component emits. |
| `defineExpose` | Explicitly expose properties to a parent via template refs. |
| `defineOptions` | Declare component options (e.g. `name`, `inheritAttrs`) inline. |
| `defineSlots` | Declare the slots the component accepts (typing only). |
| `defineModel` | Declare a two-way bindable model backed by a prop + `update:` event. |
| `withDefaults` | Provide default values for type-only `defineProps`. |

```vue
<script setup>
const props = defineProps({
  title: { type: String, required: true },
  count: { type: Number, default: 0 },
})

const emit = defineEmits(['submit', 'cancel'])

function save() {
  emit('submit', props.title)
}
</script>
```

**Two-way binding with `defineModel`:**

```vue
<!-- CustomInput.vue -->
<script setup>
const model = defineModel()  // backed by the `modelValue` prop + `update:modelValue`
</script>

<template>
  <input v-model="model" />
</template>
```

```vue
<!-- Parent -->
<CustomInput v-model="searchText" />
```

### 5.4 Composition helpers

These functions are callable inside `setup` (or `<script setup>`) to access the component's runtime context:

| Function | Returns / Purpose |
|---|---|
| `useAttrs()` | The fallthrough attributes (everything not declared as a prop or emit). |
| `useSlots()` | The slots passed to this component. |
| `useModel(props, name, options?)` | The runtime helper behind `defineModel`. |
| `useTemplateRef(key)` | A ref bound to a template element/component with a matching `ref="key"`. |
| `useId()` | A stable, SSR-safe unique ID (for `for`/`aria-*` attribute pairing). |
| `getCurrentInstance()` | The internal component instance (advanced/plugin use). |
| `hasInjectionContext()` | `true` if called where `inject()` would work. |

```vue
<script setup>
import { useTemplateRef, onMounted, useId } from 'vue'

const inputRef = useTemplateRef('the-input')
const id = useId()

onMounted(() => inputRef.value.focus())
</script>

<template>
  <label :for="id">Name</label>
  <input :id="id" ref="the-input" />
</template>
```

> [!TIP]
> `useId` generates IDs that match between the server-rendered HTML and the client, so it is the correct way to create unique IDs in apps that use SSR — hand-rolled counters cause hydration mismatches.

### 5.5 Lifecycle hooks

Register lifecycle callbacks inside `setup`. Each hook runs at a defined point in a component's life.

| Hook | Fires |
|---|---|
| `onBeforeMount` | Before the component's first render/DOM insertion. |
| `onMounted` | After the component is inserted into the DOM. |
| `onBeforeUpdate` | Before a re-render caused by reactive change. |
| `onUpdated` | After the DOM has been patched. |
| `onBeforeUnmount` | Before the component is removed. |
| `onUnmounted` | After the component and its effects are torn down. |
| `onActivated` | When a `<KeepAlive>`-cached component is re-inserted. |
| `onDeactivated` | When a `<KeepAlive>`-cached component is removed but kept alive. |
| `onErrorCaptured` | When an error from a descendant is captured (return `false` to stop propagation). |
| `onRenderTracked` | (dev) When a reactive dependency is tracked during render. |
| `onRenderTriggered` | (dev) When a dependency triggers a re-render. |
| `onServerPrefetch` | (SSR) Await async data before the component renders on the server. |

```js
import { onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    const onResize = () => { /* ... */ }
    onMounted(() => window.addEventListener('resize', onResize))
    onUnmounted(() => window.removeEventListener('resize', onResize))
  },
}
```

> [!WARNING]
> Lifecycle hooks must be called **synchronously** during `setup`. Calling `onMounted` inside an `await`, a `setTimeout`, or a `.then()` callback registers it against the wrong (or no) component instance and it will not fire.

### 5.6 Provide / Inject

Pass data to deeply nested descendants without prop-drilling.

```js
// Ancestor
import { provide, ref } from 'vue'
const theme = ref('dark')
provide('theme', theme)

// Any descendant
import { inject } from 'vue'
const theme = inject('theme', 'light') // second arg is the default
```

| Function | Purpose |
|---|---|
| `provide(key, value)` | Provide a value to descendants. `key` can be a string or an `InjectionKey<T>` symbol for typing. |
| `inject(key, default?, treatDefaultAsFactory?)` | Read a provided value, with an optional default. |
| `hasInjectionContext()` | Check whether injection is currently possible. |

> [!TIP]
> For type-safe injection in TypeScript, create a typed key with `InjectionKey`:
> ```ts
> import { type InjectionKey, provide, inject } from 'vue'
> const ThemeKey: InjectionKey<Ref<string>> = Symbol('theme')
> provide(ThemeKey, theme)   // value is type-checked
> const theme = inject(ThemeKey)  // inferred as Ref<string> | undefined
> ```

### 5.7 Async components

Load a component lazily — its code is only fetched when it is first rendered.

```js
import { defineAsyncComponent } from 'vue'

const AsyncDashboard = defineAsyncComponent(() =>
  import('./Dashboard.vue'),
)
```

Use the options form for loading/error states and timeouts:

```js
const AsyncDashboard = defineAsyncComponent({
  loader: () => import('./Dashboard.vue'),
  loadingComponent: LoadingSpinner,
  errorComponent: LoadError,
  delay: 200,       // ms before showing the loading component
  timeout: 10000,   // ms before failing with the error component
})
```

> [!WARNING]
> If the loader resolves to something that is not a valid component, Vue throws `Invalid async component load result`. Make sure dynamic `import()` returns a module whose default export is a component (or return the component directly).

---

## 6. Template Syntax & Directives

Vue templates are HTML augmented with directives (`v-` prefixed attributes), interpolation (`{{ }}`), and bindings.

### 6.1 Text interpolation and binding

```vue
<template>
  <!-- text interpolation -->
  <span>{{ message }}</span>

  <!-- attribute binding (shorthand `:`) -->
  <img :src="imageUrl" :alt="caption" />

  <!-- event binding (shorthand `@`) -->
  <button @click="onClick">Go</button>

  <!-- two-way binding on form inputs -->
  <input v-model="name" />
</template>
```

### 6.2 Built-in directives

| Directive | Purpose |
|---|---|
| `v-bind` (`:`) | Bind an attribute or prop to an expression. |
| `v-on` (`@`) | Attach an event listener. |
| `v-model` | Two-way binding on form elements and components. |
| `v-if` / `v-else-if` / `v-else` | Conditionally render an element (removed from DOM when false). |
| `v-show` | Toggle an element's CSS `display` (stays in DOM). |
| `v-for` | Render a list from an array, object, number, or iterable. |
| `v-slot` (`#`) | Define/consume named and scoped slots. |
| `v-html` | Set an element's `innerHTML`. |
| `v-text` | Set an element's text content. |
| `v-pre` | Skip compilation for this element and its children. |
| `v-once` | Render once and never update. |
| `v-memo` | Memoize a subtree, re-rendering only when listed dependencies change. |
| `v-cloak` | Hide un-compiled templates until Vue is ready (full build). |

> [!CAUTION]
> `v-html` renders raw HTML and can expose your app to cross-site scripting (XSS). Never use `v-html` on content that comes from users or any untrusted source. Prefer text interpolation, which is always escaped.

### 6.3 `v-model` modifiers and form binding

`v-model` works on `<input>`, `<textarea>`, `<select>`, and components. Built-in modifiers transform the bound value:

| Modifier | Effect |
|---|---|
| `.lazy` | Sync on `change` instead of `input`. |
| `.number` | Cast the input string to a number. |
| `.trim` | Trim whitespace from the input. |

```vue
<input v-model.trim.number="age" />
```

### 6.4 Event modifiers

`v-on`/`@` supports modifiers for common event handling needs:

```vue
<form @submit.prevent="onSubmit">       <!-- event.preventDefault() -->
  <input @keyup.enter="search" />        <!-- only the Enter key -->
  <button @click.stop="onClick">Go</button> <!-- event.stopPropagation() -->
</form>
```

For programmatic handlers (render functions / JSX), Vue exports `withModifiers` and `withKeys`:

```js
import { withModifiers, withKeys, h } from 'vue'

h('input', {
  onKeyup: withKeys(() => submit(), ['enter']),
  onClick: withModifiers(() => go(), ['stop', 'prevent']),
})
```

### 6.5 List rendering with `v-for`

```vue
<ul>
  <li v-for="(item, index) in items" :key="item.id">
    {{ index }}: {{ item.name }}
  </li>
</ul>
```

> [!IMPORTANT]
> Always provide a `:key` with `v-for` so Vue can track identity across updates. Without keys, Vue falls back to in-place patching, which can produce incorrect results when list items have their own state.

> [!NOTE]
> When iterating an object with `v-for="(value, key) in obj"`, the `key` is typed as `string` (object keys are strings in JS). If you iterate with key/value and expect a numeric key, you must convert it yourself (related: issue #14910).

---

## 7. Built-in Components

Vue ships several components you can use directly in any template. The runtime-core components are available everywhere; `Transition` and `TransitionGroup` are DOM-specific (from `@vue/runtime-dom`, re-exported by `vue`).

| Component | Purpose |
|---|---|
| `<Teleport>` | Render child content to a different place in the DOM (e.g. modals at `<body>`). |
| `<Suspense>` | Coordinate nested async dependencies, showing fallback content while pending. |
| `<KeepAlive>` | Cache toggled components so they keep state instead of being destroyed. |
| `<Transition>` | Apply enter/leave animations to a single element or component. |
| `<TransitionGroup>` | Animate insertion, removal, and reordering of list items. |

### 7.1 Teleport

```vue
<template>
  <button @click="open = true">Open modal</button>
  <Teleport to="body">
    <div v-if="open" class="modal">
      Modal content rendered at &lt;body&gt;
      <button @click="open = false">Close</button>
    </div>
  </Teleport>
</template>
```

`<Teleport>` props: `to` (target selector or element), `disabled` (render in place when `true`), and `defer` (resolve the target after the current render, for targets rendered by Vue itself).

### 7.2 Suspense

```vue
<template>
  <Suspense>
    <template #default>
      <AsyncProfile />   <!-- a component with async setup() -->
    </template>
    <template #fallback>
      <div>Loading profile…</div>
    </template>
  </Suspense>
</template>
```

> [!NOTE]
> `<Suspense>` is an experimental feature; its API may evolve. It works with components that use top-level `await` in `setup` or `onServerPrefetch`.

### 7.3 KeepAlive

```vue
<KeepAlive :include="['Home', 'Profile']" :max="10">
  <component :is="currentView" />
</KeepAlive>
```

`<KeepAlive>` props: `include` / `exclude` (component name matchers — string, RegExp, or array) and `max` (cap the number of cached instances, LRU-evicted). Cached components fire `onActivated`/`onDeactivated` instead of mount/unmount.

### 7.4 Transition

```vue
<template>
  <Transition name="fade">
    <p v-if="show">Hello</p>
  </Transition>
</template>

<style>
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>
```

Key `<Transition>` props include `name`, `mode` (`'in-out'` / `'out-in'`), `appear`, `duration`, and per-phase class overrides (`enter-active-class`, etc.). `<TransitionGroup>` additionally renders a real wrapper element (via `tag`) and applies a `move-class` for reorder animations.

> [!WARNING]
> `<Transition>` animates an element's presence; it does not animate `visibility`. Toggling `visibility` via `v-show` will not produce a transition the way `display`/`opacity` do (related: issue #2531). Animate `opacity`/`transform` instead.

---

## 8. Server-Side Rendering (SSR)

Vue can render components to HTML strings or streams on the server using the `vue/server-renderer` entry (the `@vue/server-renderer` package).

```js
import { createSSRApp } from 'vue'
import { renderToString } from 'vue/server-renderer'

const app = createSSRApp({
  data: () => ({ msg: 'Hello from the server' }),
  template: `<div>{{ msg }}</div>`,
})

const html = await renderToString(app)
console.log(html) // <div>Hello from the server</div>
```

| Function | Output |
|---|---|
| `renderToString(app, context?)` | Render to a complete HTML string (Promise). |
| `renderToNodeStream(app, context?)` | Render to a Node.js `Readable` stream. |
| `pipeToNodeWritable(app, context, writable)` | Pipe rendering into a Node.js `Writable`. |
| `renderToWebStream(app, context?)` | Render to a Web `ReadableStream`. |
| `pipeToWebWritable(app, context, writable)` | Pipe rendering into a Web `WritableStream`. |
| `renderToSimpleStream(app, context, push)` | Render with a minimal push-based stream interface. |

On the **client**, you "hydrate" the server-rendered markup instead of re-rendering it. Use `createSSRApp` (not `createApp`) so Vue attaches to existing DOM:

```js
import { createSSRApp } from 'vue'
import App from './App.vue'

createSSRApp(App).mount('#app') // hydrates the server HTML in place
```

> [!IMPORTANT]
> The client app structure must match the server-rendered output exactly, or you get a **hydration mismatch**. Use `useId()` for any generated IDs and avoid rendering values that differ between server and client (like `Date.now()` or random numbers) without guarding them.

> [!TIP]
> `renderToStream` is deprecated in favor of `renderToNodeStream` / `renderToWebStream`. Prefer the explicit stream variants for new code.

---

## 9. Custom Elements (Web Components)

Vue can compile any component into a standards-based custom element you can use outside Vue — in plain HTML, or in another framework.

```js
import { defineCustomElement } from 'vue'
import MyWidget from './MyWidget.ce.vue'

const MyWidgetElement = defineCustomElement(MyWidget)
customElements.define('my-widget', MyWidgetElement)
```

```html
<my-widget some-prop="value"></my-widget>
```

| API | Purpose |
|---|---|
| `defineCustomElement(component, options?)` | Build a custom-element constructor from a Vue component. |
| `defineSSRCustomElement(component, options?)` | Same, for server-rendered custom elements. |
| `useHost()` | Access the host custom element from inside the component. |
| `useShadowRoot()` | Access the component's shadow root. |
| `VueElement` | The base class custom elements extend. |

> [!NOTE]
> A `.ce.vue` suffix (or `customElement: true` in your build tool) tells the SFC compiler to inject component styles into the shadow root instead of the document head, which is the correct behavior for custom elements.

---

## 10. Render Functions & JSX

When templates are not expressive enough, write render functions directly with `h` (hyperscript).

```js
import { h, defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    return () =>
      h('button', { onClick: () => count.value++ }, `Count: ${count.value}`)
  },
})
```

| API | Purpose |
|---|---|
| `h(type, props?, children?)` | Create a vnode (the main render-function API). |
| `mergeProps(...objects)` | Merge multiple props objects (class/style/handlers merged correctly). |
| `cloneVNode(vnode, extraProps?)` | Clone a vnode with extra props. |
| `isVNode(value)` | Check if a value is a vnode. |
| `resolveComponent(name)` | Resolve a registered component by name in a render function. |
| `resolveDirective(name)` | Resolve a registered directive by name. |
| `withDirectives(vnode, directives)` | Attach custom directives to a vnode. |
| `Fragment`, `Text`, `Comment`, `Static` | Special vnode type markers. |

JSX is supported via the `vue/jsx-runtime` automatic runtime — configure your build's `jsxImportSource` to `vue` and write JSX that compiles to `h` calls.

---

## 11. Utility & Timing APIs

| API | Purpose |
|---|---|
| `nextTick(callback?)` | Wait for the next DOM update flush. Returns a Promise. |
| `version` | The Vue version string (`import { version } from 'vue'`). |
| `toDisplayString(value)` | The interpolation stringifier Vue uses for `{{ }}`. |
| `normalizeClass(value)` | Normalize a class binding (string/array/object) to a class string. |
| `normalizeStyle(value)` | Normalize a style binding to a style object/string. |
| `useCssModule(name?)` | Access CSS Modules classes inside `setup`. |
| `useCssVars(getter)` | Drive `<style>` CSS custom properties from reactive state. |

```js
import { ref, nextTick } from 'vue'

const count = ref(0)
count.value++
await nextTick()
// the DOM now reflects the new count
```

> [!TIP]
> Use `nextTick` when you need to read the DOM *after* a reactive change has been applied — for example, measuring an element's size after its content changed.

---

## 12. How Vue Works (Concepts)

You can be productive with Vue without this section, but understanding the pipeline helps you debug and optimize.

1. **Reactivity** — `ref`/`reactive` wrap your state in proxies. When code reads a reactive value during an effect (like a component's render), Vue records a dependency. When the value changes, Vue re-runs the dependent effects.
2. **Template compilation** — Your `<template>` (or string template) is compiled into a **render function** that returns a tree of **vnodes** (virtual DOM nodes). With SFCs and a build step, this happens ahead of time; with the full build, it happens in the browser.
3. **Rendering & patching** — On first render, Vue creates real DOM from the vnode tree. On updates, it produces a new vnode tree and **patches** only the differences into the real DOM.
4. **Compiler-informed fast paths** — Vue's compiler annotates the output with hints (patch flags, static hoisting, block tracking) so the runtime skips work for parts of the tree that cannot change. This is why Vue templates are typically faster than hand-written render functions.

The packages map onto these stages: `@vue/reactivity` (state), `@vue/compiler-dom` + `@vue/compiler-sfc` (compilation), `@vue/runtime-core` + `@vue/runtime-dom` (rendering), and `@vue/server-renderer` (SSR). The umbrella `vue` package wires them together.

---

## 13. Troubleshooting

### Error: `Failed to mount app: mount target selector "#app" returned null.`

**Cause:** `app.mount('#app')` was called with a CSS selector that matched no element — usually because the script ran before the target existed in the DOM, or the selector is wrong.

**Solution:** Ensure the target element exists before mounting (place the script at the end of `<body>` or load it as a module/deferred), and verify the selector matches your HTML's `id`.

**Prevention:** Use a module bundler entry that runs after DOM parse, or wrap mounting in a `DOMContentLoaded` listener for plain-script setups.

### Error: `Template element not found or is empty: #template`

**Cause:** The full build was told to use an in-DOM template via a selector (`template: '#template'`), but no element with that id was found.

**Solution:** Confirm the referenced template element exists in the page and contains markup. For build-step apps, use SFCs instead of selector templates.

### Error: `invalid template option:`

**Cause:** A `template` option was provided that is neither a string nor a DOM node.

**Solution:** Pass a string template or a real DOM element. If you meant to render a component, supply it as the root component to `createApp`, not as `template`.

### Error: Component provided `template` option but runtime compilation is not supported

**Cause:** You are using the **runtime-only** build (the default `vue` import) but passed a string `template` that needs the compiler.

**Solution:** Either (a) use Single-File Components compiled by your build tool (recommended), or (b) switch to the full build by aliasing `vue` to `vue/dist/vue.esm-bundler.js` in your bundler config.

**Prevention:** Keep templates in `.vue` files so they compile at build time.

### Warning: `Template compilation error: Element is missing end tag`

**Cause:** A template (compiled by the full build) contains malformed HTML, e.g. `<div v-if>` with no closing tag, or a self-closing native element that breaks parsing.

**Solution:** Fix the markup — close all elements. Vue prints a code frame pointing at the offending line.

### Warning: `Set operation on key "…" failed: target is readonly.`

**Cause:** Code attempted to mutate a `readonly`/`shallowReadonly` proxy — commonly a prop (props are readonly) or an injected readonly value.

**Solution:** Do not mutate props directly. Emit an event to ask the parent to change the value, or use `defineModel`/`v-model` for two-way binding. For injected state, provide a setter function alongside the value.

### Warning: `Reactive Map/Set contains both the raw and reactive versions of the same object`

**Cause:** The same object was used as a key/value in a reactive `Map`/`Set` in both its raw and reactive forms, which leads to inconsistent lookups.

**Solution:** Standardize on one form. Convert with `toRaw()` (or always use the reactive proxy) before inserting into a reactive collection.

### Problem: Destructured `reactive` state stopped updating

**Cause:** Destructuring a `reactive` object copies primitive values, severing the reactive connection.

**Solution:** Wrap with `toRefs(state)` before destructuring, then use `.value` (or rely on template unwrapping).

### Problem: Replacing a `ref` object or `reactive` variable breaks reactivity

**Cause:** Reassigning a whole `reactive` variable (`state = reactive({...})`) discards the tracked proxy. For arrays, replacing the array reference inside a `ref` is fine (`list.value = [...]`), but reassigning a `reactive` array variable is not.

**Solution:** Mutate the existing reactive object/array in place, or store the value in a `ref` and replace `ref.value`.

### Problem: `onMounted` (or another hook) never fires

**Cause:** The hook was registered asynchronously (inside `await`, `setTimeout`, or a promise callback), so it was not associated with the active component instance.

**Solution:** Register all lifecycle hooks synchronously at the top level of `setup`.

### Problem: Hydration mismatch warnings after SSR

**Cause:** The client-rendered tree differs from the server HTML — often from non-deterministic values (dates, random IDs) or invalid HTML nesting.

**Solution:** Use `useId()` for generated IDs, guard environment-specific rendering, and ensure valid HTML nesting. Enable `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__` to get specific diagnostics in production.

### Problem: `watch` callback with `immediate: true` only runs once

**Cause:** With an empty or never-changing source, there is nothing to react to after the initial immediate run.

**Solution:** Watch a real reactive source, or use `watchEffect` if you want the callback to track whatever it reads.

---

## 14. FAQ

**Q: Should I use `ref` or `reactive`?**
Use `ref` for primitives and when you want a single, reassignable container (`x.value = ...`). Use `reactive` for objects you mutate in place. Many teams standardize on `ref` everywhere for consistency, since `ref` can hold objects too.

**Q: What's the difference between `watch` and `watchEffect`?**
`watch` takes explicit sources and gives you old + new values, and is lazy by default. `watchEffect` runs immediately and automatically re-runs when any reactive value it reads changes, but doesn't give you previous values.

**Q: When do I need `nextTick`?**
Whenever you must read or act on the DOM *after* a reactive state change has been flushed to it — Vue batches DOM updates, so they aren't applied synchronously when you change state.

**Q: Do I have to use Single-File Components?**
No. You can author components as plain objects with `setup()`/`defineComponent`, or with render functions/JSX. SFCs are the most ergonomic for most apps because they co-locate template, logic, and scoped CSS.

**Q: How do I do two-way binding on a custom component?**
Use `defineModel()` in the child (`<script setup>`) and `v-model` in the parent. Under the hood this is a `modelValue` prop plus an `update:modelValue` event.

**Q: Which build do I get by default from `import ... from 'vue'`?**
The runtime-only ESM bundler build (`vue.runtime.esm-bundler.js`). It excludes the template compiler because your SFC templates are compiled at build time.

**Q: How do I enable the Options API tree-shaking?**
Set the bundler flag `__VUE_OPTIONS_API__` to `false`. Only do this if no component (or dependency) relies on the Options API.

**Q: Can I use Vue without a build step?**
Yes — load `vue.global.js` (or `vue.esm-browser.js`) from a CDN. These are full builds that include the compiler, so in-page templates work.

**Q: How do I share logic between components?**
Write a **composable**: a function that uses the Composition API (`ref`, `computed`, `watch`, lifecycle hooks) and returns reactive state. Call it from any component's `setup`.

**Q: How do I clean up side effects in a composable?**
Use `onScopeDispose` (works with any active effect scope) or a lifecycle hook like `onUnmounted` when inside a component. For per-run cleanup in a watcher, use `onWatcherCleanup`.

**Q: Is `<Suspense>` production-ready?**
It is still marked experimental. It works today for async `setup`/`onServerPrefetch`, but treat its API as subject to change.

**Q: What Node version do I need?**
Vue's tooling requires Node.js `>= 20`. The runtime itself runs in modern browsers (ES2016+) and in Node for SSR.

---

## 15. API Quick Reference

**Reactivity (`vue` / `@vue/reactivity`):** `ref` · `shallowRef` · `isRef` · `unref` · `toRef` · `toRefs` · `toValue` · `proxyRefs` · `customRef` · `triggerRef` · `reactive` · `shallowReactive` · `readonly` · `shallowReadonly` · `isReactive` · `isReadonly` · `isProxy` · `isShallow` · `toRaw` · `markRaw` · `computed` · `watch` · `watchEffect` · `watchPostEffect` · `watchSyncEffect` · `onWatcherCleanup` · `effect` · `stop` · `effectScope` · `getCurrentScope` · `onScopeDispose`

**Component & lifecycle (`vue`):** `defineComponent` · `defineAsyncComponent` · `onBeforeMount` · `onMounted` · `onBeforeUpdate` · `onUpdated` · `onBeforeUnmount` · `onUnmounted` · `onActivated` · `onDeactivated` · `onErrorCaptured` · `onRenderTracked` · `onRenderTriggered` · `onServerPrefetch` · `provide` · `inject` · `hasInjectionContext`

**`<script setup>` macros:** `defineProps` · `defineEmits` · `defineExpose` · `defineOptions` · `defineSlots` · `defineModel` · `withDefaults`

**Composition helpers:** `useAttrs` · `useSlots` · `useModel` · `useTemplateRef` · `useId` · `useCssModule` · `useCssVars` · `getCurrentInstance`

**App & rendering:** `createApp` · `createSSRApp` · `nextTick` · `h` · `mergeProps` · `cloneVNode` · `isVNode` · `resolveComponent` · `resolveDirective` · `withDirectives` · `withModifiers` · `withKeys` · `version`

**Built-in components:** `Teleport` · `Suspense` · `KeepAlive` · `Transition` · `TransitionGroup`

**Custom elements:** `defineCustomElement` · `defineSSRCustomElement` · `useHost` · `useShadowRoot` · `VueElement`

**SSR (`vue/server-renderer`):** `renderToString` · `renderToNodeStream` · `pipeToNodeWritable` · `renderToWebStream` · `pipeToWebWritable` · `renderToSimpleStream`

---

_Generated June 2026 from vuejs/core at 86ad076. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
