` element. It contains all the information that we need to create the actual element. It also contains more children vnodes, which makes it the root of a virtual DOM tree.
A runtime renderer can walk a virtual DOM tree and construct a real DOM tree from it. This process is called **mount**.
If we have two copies of virtual DOM trees, the renderer can also walk and compare the two trees, figuring out the differences, and apply those changes to the actual DOM. This process is called **patch**, also known as "diffing" or "reconciliation".
The main benefit of virtual DOM is that it gives the developer the ability to programmatically create, inspect and compose desired UI structures in a declarative way, while leaving the direct DOM manipulation to the renderer.
## Render Pipeline {#render-pipeline}
At the high level, this is what happens when a Vue component is mounted:
1. **Compile**: Vue templates are compiled into **render functions**: functions that return virtual DOM trees. This step can be done either ahead-of-time via a build step, or on-the-fly by using the runtime compiler.
2. **Mount**: The runtime renderer invokes the render functions, walks the returned virtual DOM tree, and creates actual DOM nodes based on it. This step is performed as a [reactive effect](./reactivity-in-depth), so it keeps track of all reactive dependencies that were used.
3. **Patch**: When a dependency used during mount changes, the effect re-runs. This time, a new, updated Virtual DOM tree is created. The runtime renderer walks the new tree, compares it with the old one, and applies necessary updates to the actual DOM.

## Templates vs. Render Functions {#templates-vs-render-functions}
Vue templates are compiled into virtual DOM render functions. Vue also provides APIs that allow us to skip the template compilation step and directly author render functions. Render functions are more flexible than templates when dealing with highly dynamic logic, because you can work with vnodes using the full power of JavaScript.
So why does Vue recommend templates by default? There are a number of reasons:
1. Templates are closer to actual HTML. This makes it easier to reuse existing HTML snippets, apply accessibility best practices, style with CSS, and for designers to understand and modify.
2. Templates are easier to statically analyze due to their more deterministic syntax. This allows Vue's template compiler to apply many compile-time optimizations to improve the performance of the virtual DOM (which we will discuss below).
In practice, templates are sufficient for most use cases in applications. Render functions are typically only used in reusable components that need to deal with highly dynamic rendering logic. Render function usage is discussed in more detail in [Render Functions & JSX](./render-function).
## Compiler-Informed Virtual DOM {#compiler-informed-virtual-dom}
The virtual DOM implementation in React and most other virtual-DOM implementations are purely runtime: the reconciliation algorithm cannot make any assumptions about the incoming virtual DOM tree, so it has to fully traverse the tree and diff the props of every vnode in order to ensure correctness. In addition, even if a part of the tree never changes, new vnodes are always created for them on each re-render, resulting in unnecessary memory pressure. This is one of the most criticized aspect of virtual DOM: the somewhat brute-force reconciliation process sacrifices efficiency in return for declarativeness and correctness.
But it doesn't have to be that way. In Vue, the framework controls both the compiler and the runtime. This allows us to implement many compile-time optimizations that only a tightly-coupled renderer can take advantage of. The compiler can statically analyze the template and leave hints in the generated code so that the runtime can take shortcuts whenever possible. At the same time, we still preserve the capability for the user to drop down to the render function layer for more direct control in edge cases. We call this hybrid approach **Compiler-Informed Virtual DOM**.
Below, we will discuss a few major optimizations done by the Vue template compiler to improve the virtual DOM's runtime performance.
### Cache Static {#cache-static}
Quite often there will be parts in a template that do not contain any dynamic bindings:
```vue-html{2-3}
```
[Inspect in Template Explorer](https://template-explorer.vuejs.org/#eyJzcmMiOiI8ZGl2PlxuICA8ZGl2PmZvbzwvZGl2PiA8IS0tIGNhY2hlZCAtLT5cbiAgPGRpdj5iYXI8L2Rpdj4gPCEtLSBjYWNoZWQgLS0+XG4gIDxkaXY+e3sgZHluYW1pYyB9fTwvZGl2PlxuPC9kaXY+XG4iLCJvcHRpb25zIjp7ImhvaXN0U3RhdGljIjp0cnVlfX0=)
The `foo` and `bar` divs are static - re-creating vnodes and diffing them on each re-render is unnecessary. The renderer creates these vnodes during the initial render, caches them, and reuses the same vnodes for every subsequent re-render. The renderer is also able to completely skip diffing them when it notices the old vnode and the new vnode are the same one.
In addition, when there are enough consecutive static elements, they will be condensed into a single "static vnode" that contains the plain HTML string for all these nodes ([Example](https://template-explorer.vuejs.org/#eyJzcmMiOiI8ZGl2PlxuICA8ZGl2IGNsYXNzPVwiZm9vXCI+Zm9vPC9kaXY+XG4gIDxkaXYgY2xhc3M9XCJmb29cIj5mb288L2Rpdj5cbiAgPGRpdiBjbGFzcz1cImZvb1wiPmZvbzwvZGl2PlxuICA8ZGl2IGNsYXNzPVwiZm9vXCI+Zm9vPC9kaXY+XG4gIDxkaXYgY2xhc3M9XCJmb29cIj5mb288L2Rpdj5cbiAgPGRpdj57eyBkeW5hbWljIH19PC9kaXY+XG48L2Rpdj4iLCJzc3IiOmZhbHNlLCJvcHRpb25zIjp7ImhvaXN0U3RhdGljIjp0cnVlfX0=)). These static vnodes are mounted by directly setting `innerHTML`.
### Patch Flags {#patch-flags}
For a single element with dynamic bindings, we can also infer a lot of information from it at compile time:
```vue-html
{{ dynamic }}
```
[Inspect in Template Explorer](https://template-explorer.vuejs.org/#eyJzcmMiOiI8ZGl2IDpjbGFzcz1cInsgYWN0aXZlIH1cIj48L2Rpdj5cblxuPGlucHV0IDppZD1cImlkXCIgOnZhbHVlPVwidmFsdWVcIj5cblxuPGRpdj57eyBkeW5hbWljIH19PC9kaXY+Iiwib3B0aW9ucyI6e319)
When generating the render function code for these elements, Vue encodes the type of update each of them needs directly in the vnode creation call:
```js{3}
createElementVNode("div", {
class: _normalizeClass({ active: _ctx.active })
}, null, 2 /* CLASS */)
```
The last argument, `2`, is a [patch flag](https://github.com/vuejs/core/blob/main/packages/shared/src/patchFlags.ts). An element can have multiple patch flags, which will be merged into a single number. The runtime renderer can then check against the flags using [bitwise operations](https://en.wikipedia.org/wiki/Bitwise_operation) to determine whether it needs to do certain work:
```js
if (vnode.patchFlag & PatchFlags.CLASS /* 2 */) {
// update the element's class
}
```
Bitwise checks are extremely fast. With the patch flags, Vue is able to do the least amount of work necessary when updating elements with dynamic bindings.
Vue also encodes the type of children a vnode has. For example, a template that has multiple root nodes is represented as a fragment. In most cases, we know for sure that the order of these root nodes will never change, so this information can also be provided to the runtime as a patch flag:
```js{4}
export function render() {
return (_openBlock(), _createElementBlock(_Fragment, null, [
/* children */
], 64 /* STABLE_FRAGMENT */))
}
```
The runtime can thus completely skip child-order reconciliation for the root fragment.
### Tree Flattening {#tree-flattening}
Taking another look at the generated code from the previous example, you'll notice the root of the returned virtual DOM tree is created using a special `createElementBlock()` call:
```js{2}
export function render() {
return (_openBlock(), _createElementBlock(_Fragment, null, [
/* children */
], 64 /* STABLE_FRAGMENT */))
}
```
Conceptually, a "block" is a part of the template that has stable inner structure. In this case, the entire template has a single block because it does not contain any structural directives like `v-if` and `v-for`.
Each block tracks any descendant nodes (not just direct children) that have patch flags. For example:
```vue-html{3,5}
```
The result is a flattened array that contains only the dynamic descendant nodes:
```
div (block root)
- div with :id binding
- div with {{ bar }} binding
```
When this component needs to re-render, it only needs to traverse the flattened tree instead of the full tree. This is called **Tree Flattening**, and it greatly reduces the number of nodes that need to be traversed during virtual DOM reconciliation. Any static parts of the template are effectively skipped.
`v-if` and `v-for` directives will create new block nodes:
```vue-html
```
A child block is tracked inside the parent block's array of dynamic descendants. This retains a stable structure for the parent block.
### Impact on SSR Hydration {#impact-on-ssr-hydration}
Both patch flags and tree flattening also greatly improve Vue's [SSR Hydration](/guide/scaling-up/ssr#client-hydration) performance:
* Single element hydration can take fast paths based on the corresponding vnode's patch flag.
* Only block nodes and their dynamic descendants need to be traversed during hydration, effectively achieving partial hydration at the template level.
---
---
url: /guide/extras/render-function.md
---
# Render Functions & JSX {#render-functions-jsx}
Vue recommends using templates to build applications in the vast majority of cases. However, there are situations where we need the full programmatic power of JavaScript. That's where we can use the **render function**.
> If you are new to the concept of virtual DOM and render functions, make sure to read the [Rendering Mechanism](/guide/extras/rendering-mechanism) chapter first.
## Basic Usage {#basic-usage}
### Creating Vnodes {#creating-vnodes}
Vue provides an `h()` function for creating vnodes:
```js
import { h } from 'vue'
const vnode = h(
'div', // type
{ id: 'foo', class: 'bar' }, // props
[
/* children */
]
)
```
`h()` is short for **hyperscript** - which means "JavaScript that produces HTML (hypertext markup language)". This name is inherited from conventions shared by many virtual DOM implementations. A more descriptive name could be `createVNode()`, but a shorter name helps when you have to call this function many times in a render function.
The `h()` function is designed to be very flexible:
```js
// all arguments except the type are optional
h('div')
h('div', { id: 'foo' })
// both attributes and properties can be used in props
// Vue automatically picks the right way to assign it
h('div', { class: 'bar', innerHTML: 'hello' })
// props modifiers such as `.prop` and `.attr` can be added
// with `.` and `^` prefixes respectively
h('div', { '.name': 'some-name', '^width': '100' })
// class and style have the same object / array
// value support that they have in templates
h('div', { class: [foo, { bar }], style: { color: 'red' } })
// event listeners should be passed as onXxx
h('div', { onClick: () => {} })
// children can be a string
h('div', { id: 'foo' }, 'hello')
// props can be omitted when there are no props
h('div', 'hello')
h('div', [h('span', 'hello')])
// children array can contain mixed vnodes and strings
h('div', ['hello', h('span', 'hello')])
```
The resulting vnode has the following shape:
```js
const vnode = h('div', { id: 'foo' }, [])
vnode.type // 'div'
vnode.props // { id: 'foo' }
vnode.children // []
vnode.key // null
```
:::warning Note
The full `VNode` interface contains many other internal properties, but it is strongly recommended to avoid relying on any properties other than the ones listed here. This avoids unintended breakage in case the internal properties are changed.
:::
### Declaring Render Functions {#declaring-render-functions}
When using templates with Composition API, the return value of the `setup()` hook is used to expose data to the template. When using render functions, however, we can directly return the render function instead:
```js
import { ref, h } from 'vue'
export default {
props: {
/* ... */
},
setup(props) {
const count = ref(1)
// return the render function
return () => h('div', props.msg + count.value)
}
}
```
The render function is declared inside `setup()` so it naturally has access to the props and any reactive state declared in the same scope.
In addition to returning a single vnode, you can also return strings or arrays:
```js
export default {
setup() {
return () => 'hello world!'
}
}
```
```js
import { h } from 'vue'
export default {
setup() {
// use an array to return multiple root nodes
return () => [
h('div'),
h('div'),
h('div')
]
}
}
```
:::tip
Make sure to return a function instead of directly returning values! The `setup()` function is called only once per component, while the returned render function will be called multiple times.
:::
We can declare render functions using the `render` option:
```js
import { h } from 'vue'
export default {
data() {
return {
msg: 'hello'
}
},
render() {
return h('div', this.msg)
}
}
```
The `render()` function has access to the component instance via `this`.
In addition to returning a single vnode, you can also return strings or arrays:
```js
export default {
render() {
return 'hello world!'
}
}
```
```js
import { h } from 'vue'
export default {
render() {
// use an array to return multiple root nodes
return [
h('div'),
h('div'),
h('div')
]
}
}
```
If a render function component doesn't need any instance state, they can also be declared directly as a function for brevity:
```js
function Hello() {
return 'hello world!'
}
```
That's right, this is a valid Vue component! See [Functional Components](#functional-components) for more details on this syntax.
### Vnodes Must Be Unique {#vnodes-must-be-unique}
All vnodes in the component tree must be unique. That means the following render function is invalid:
```js
function render() {
const p = h('p', 'hi')
return h('div', [
// Yikes - duplicate vnodes!
p,
p
])
}
```
If you really want to duplicate the same element/component many times, you can do so with a factory function. For example, the following render function is a perfectly valid way of rendering 20 identical paragraphs:
```js
function render() {
return h(
'div',
Array.from({ length: 20 }).map(() => {
return h('p', 'hi')
})
)
}
```
### Using Vnodes in `
` {#using-vnodes-in-template}
```vue
Hi
Hi
```
A vnode object has been declared in `setup()`, you can use it like a normal component for rendering.
:::warning
A vnode represents an already created render output, not a component definition. Using a vnode in `` does not create a new component instance, and the vnode will be rendered as-is.
This pattern should be used with care and is not a replacement for normal components.
:::
## JSX / TSX {#jsx-tsx}
[JSX](https://facebook.github.io/jsx/) is an XML-like extension to JavaScript that allows us to write code like this:
```jsx
const vnode = hello
```
Inside JSX expressions, use curly braces to embed dynamic values:
```jsx
const vnode = hello, {userName}
```
`create-vue` and Vue CLI both have options for scaffolding projects with pre-configured JSX support. If you are configuring JSX manually, please refer to the documentation of [`@vue/babel-plugin-jsx`](https://github.com/vuejs/jsx-next) for details.
Although first introduced by React, JSX actually has no defined runtime semantics and can be compiled into various different outputs. If you have worked with JSX before, do note that **Vue JSX transform is different from React's JSX transform**, so you can't use React's JSX transform in Vue applications. Some notable differences from React JSX include:
* You can use HTML attributes such as `class` and `for` as props - no need to use `className` or `htmlFor`.
* Passing children to components (i.e. slots) [works differently](#passing-slots).
Vue's type definition also provides type inference for TSX usage. When using TSX, make sure to specify `"jsx": "preserve"` in `tsconfig.json` so that TypeScript leaves the JSX syntax intact for Vue JSX transform to process.
### JSX Type Inference {#jsx-type-inference}
Similar to the transform, Vue's JSX also needs different type definitions.
Starting in Vue 3.4, Vue no longer implicitly registers the global `JSX` namespace. To instruct TypeScript to use Vue's JSX type definitions, make sure to include the following in your `tsconfig.json`:
```json
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "vue"
// ...
}
}
```
You can also opt-in per file by adding a `/* @jsxImportSource vue */` comment at the top of the file.
If there is code that depends on the presence of the global `JSX` namespace, you can retain the exact pre-3.4 global behavior by explicitly importing or referencing `vue/jsx` in your project, which registers the global `JSX` namespace.
## Render Function Recipes {#render-function-recipes}
Below we will provide some common recipes for implementing template features as their equivalent render functions / JSX.
### `v-if` {#v-if}
Template:
```vue-html
```
Equivalent render function / JSX:
```js
h('div', [ok.value ? h('div', 'yes') : h('span', 'no')])
```
```jsx
```
```js
h('div', [this.ok ? h('div', 'yes') : h('span', 'no')])
```
```jsx
```
### `v-for` {#v-for}
Template:
```vue-html
```
Equivalent render function / JSX:
```js
h(
'ul',
// assuming `items` is a ref with array value
items.value.map(({ id, text }) => {
return h('li', { key: id }, text)
})
)
```
```jsx
{items.value.map(({ id, text }) => {
return {text}
})}
```
```js
h(
'ul',
this.items.map(({ id, text }) => {
return h('li', { key: id }, text)
})
)
```
```jsx
{this.items.map(({ id, text }) => {
return {text}
})}
```
### `v-on` {#v-on}
Props with names that start with `on` followed by an uppercase letter are treated as event listeners. For example, `onClick` is the equivalent of `@click` in templates.
```js
h(
'button',
{
onClick(event) {
/* ... */
}
},
'Click Me'
)
```
```jsx
{
/* ... */
}}
>
Click Me
```
#### Event Modifiers {#event-modifiers}
For the `.passive`, `.capture`, and `.once` event modifiers, they can be concatenated after the event name using camelCase.
For example:
```js
h('input', {
onClickCapture() {
/* listener in capture mode */
},
onKeyupOnce() {
/* triggers only once */
},
onMouseoverOnceCapture() {
/* once + capture */
}
})
```
```jsx
{}}
onKeyupOnce={() => {}}
onMouseoverOnceCapture={() => {}}
/>
```
For other event and key modifiers, the [`withModifiers`](/api/render-function#withmodifiers) helper can be used:
```js
import { withModifiers } from 'vue'
h('div', {
onClick: withModifiers(() => {}, ['self'])
})
```
```jsx
{}, ['self'])} />
```
### Components {#components}
To create a vnode for a component, the first argument passed to `h()` should be the component definition. This means when using render functions, it is unnecessary to register components - you can just use the imported components directly:
```js
import Foo from './Foo.vue'
import Bar from './Bar.jsx'
function render() {
return h('div', [h(Foo), h(Bar)])
}
```
```jsx
function render() {
return (
)
}
```
As we can see, `h` can work with components imported from any file format as long as it's a valid Vue component.
Dynamic components are straightforward with render functions:
```js
import Foo from './Foo.vue'
import Bar from './Bar.jsx'
function render() {
return ok.value ? h(Foo) : h(Bar)
}
```
```jsx
function render() {
return ok.value ?
:
}
```
If a component is registered by name and cannot be imported directly (for example, globally registered by a library), it can be programmatically resolved by using the [`resolveComponent()`](/api/render-function#resolvecomponent) helper.
### Rendering Slots {#rendering-slots}
In render functions, slots can be accessed from the `setup()` context. Each slot on the `slots` object is a **function that returns an array of vnodes**:
```js
export default {
props: ['message'],
setup(props, { slots }) {
return () => [
// default slot:
//
h('div', slots.default()),
// named slot:
//
h(
'div',
slots.footer({
text: props.message
})
)
]
}
}
```
JSX equivalent:
```jsx
// default
{slots.default()}
// named
{slots.footer({ text: props.message })}
```
In render functions, slots can be accessed from [`this.$slots`](/api/component-instance#slots):
```js
export default {
props: ['message'],
render() {
return [
//
h('div', this.$slots.default()),
//
h(
'div',
this.$slots.footer({
text: this.message
})
)
]
}
}
```
JSX equivalent:
```jsx
//
{this.$slots.default()}
//
{this.$slots.footer({ text: this.message })}
```
### Passing Slots {#passing-slots}
Passing children to components works a bit differently from passing children to elements. Instead of an array, we need to pass either a slot function, or an object of slot functions. Slot functions can return anything a normal render function can return - which will always be normalized to arrays of vnodes when accessed in the child component.
```js
// single default slot
h(MyComponent, () => 'hello')
// named slots
// notice the `null` is required to avoid
// the slots object being treated as props
h(MyComponent, null, {
default: () => 'default slot',
foo: () => h('div', 'foo'),
bar: () => [h('span', 'one'), h('span', 'two')]
})
```
JSX equivalent:
```jsx
// default
{() => 'hello'}
// named
{{
default: () => 'default slot',
foo: () => foo
,
bar: () => [one , two ]
}}
```
Passing slots as functions allows them to be invoked lazily by the child component. This leads to the slot's dependencies being tracked by the child instead of the parent, which results in more accurate and efficient updates.
### Scoped Slots {#scoped-slots}
To render a scoped slot in the parent component, a slot is passed to the child. Notice how the slot now has a parameter `text`. The slot will be called in the child component and the data from the child component will be passed up to the parent component.
```js
// parent component
export default {
setup() {
return () => h(MyComp, null, {
default: ({ text }) => h('p', text)
})
}
}
```
Remember to pass `null` so the slots will not be treated as props.
```js
// child component
export default {
setup(props, { slots }) {
const text = ref('hi')
return () => h('div', null, slots.default({ text: text.value }))
}
}
```
JSX equivalent:
```jsx
{{
default: ({ text }) => { text }
}}
```
### Built-in Components {#built-in-components}
[Built-in components](/api/built-in-components) such as `
`, ``, ``, `` and `` must be imported for use in render functions:
```js
import { h, KeepAlive, Teleport, Transition, TransitionGroup } from 'vue'
export default {
setup () {
return () => h(Transition, { mode: 'out-in' }, /* ... */)
}
}
```
```js
import { h, KeepAlive, Teleport, Transition, TransitionGroup } from 'vue'
export default {
render () {
return h(Transition, { mode: 'out-in' }, /* ... */)
}
}
```
### `v-model` {#v-model}
The `v-model` directive is expanded to `modelValue` and `onUpdate:modelValue` props during template compilation—we will have to provide these props ourselves:
```js
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
setup(props, { emit }) {
return () =>
h(SomeComponent, {
modelValue: props.modelValue,
'onUpdate:modelValue': (value) => emit('update:modelValue', value)
})
}
}
```
```js
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
render() {
return h(SomeComponent, {
modelValue: this.modelValue,
'onUpdate:modelValue': (value) => this.$emit('update:modelValue', value)
})
}
}
```
### Custom Directives {#custom-directives}
Custom directives can be applied to a vnode using [`withDirectives`](/api/render-function#withdirectives):
```js
import { h, withDirectives } from 'vue'
// a custom directive
const pin = {
mounted() { /* ... */ },
updated() { /* ... */ }
}
//
const vnode = withDirectives(h('div'), [
[pin, 200, 'top', { animate: true }]
])
```
If the directive is registered by name and cannot be imported directly, it can be resolved using the [`resolveDirective`](/api/render-function#resolvedirective) helper.
### Template Refs {#template-refs}
With the Composition API, when using [`useTemplateRef()`](/api/composition-api-helpers#usetemplateref) template refs are created by passing the string value as prop to the vnode:
```js
import { h, useTemplateRef } from 'vue'
export default {
setup() {
const divEl = useTemplateRef('my-div')
//
return () => h('div', { ref: 'my-div' })
}
}
```
In versions before 3.5 where useTemplateRef() was not introduced, template refs are created by passing the ref() itself as a prop to the vnode:
```js
import { h, ref } from 'vue'
export default {
setup() {
const divEl = ref()
//
return () => h('div', { ref: divEl })
}
}
```
With the Options API, template refs are created by passing the ref name as a string in the vnode props:
```js
export default {
render() {
//
return h('div', { ref: 'divEl' })
}
}
```
## Functional Components {#functional-components}
Functional components are an alternative form of component that don't have any state of their own. They act like pure functions: props in, vnodes out. They are rendered without creating a component instance (i.e. no `this`), and without the usual component lifecycle hooks.
To create a functional component we use a plain function, rather than an options object. The function is effectively the `render` function for the component.
The signature of a functional component is the same as the `setup()` hook:
```js
function MyComponent(props, { slots, emit, attrs }) {
// ...
}
```
As there is no `this` reference for a functional component, Vue will pass in the `props` as the first argument:
```js
function MyComponent(props, context) {
// ...
}
```
The second argument, `context`, contains three properties: `attrs`, `emit`, and `slots`. These are equivalent to the instance properties [`$attrs`](/api/component-instance#attrs), [`$emit`](/api/component-instance#emit), and [`$slots`](/api/component-instance#slots) respectively.
Most of the usual configuration options for components are not available for functional components. However, it is possible to define [`props`](/api/options-state#props) and [`emits`](/api/options-state#emits) by adding them as properties:
```js
MyComponent.props = ['value']
MyComponent.emits = ['click']
```
If the `props` option is not specified, then the `props` object passed to the function will contain all attributes, the same as `attrs`. The prop names will not be normalized to camelCase unless the `props` option is specified.
For functional components with explicit `props`, [attribute fallthrough](/guide/components/attrs) works much the same as with normal components. However, for functional components that don't explicitly specify their `props`, only the `class`, `style`, and `onXxx` event listeners will be inherited from the `attrs` by default. In either case, `inheritAttrs` can be set to `false` to disable attribute inheritance:
```js
MyComponent.inheritAttrs = false
```
Functional components can be registered and consumed just like normal components. If you pass a function as the first argument to `h()`, it will be treated as a functional component.
### Typing Functional Components {#typing-functional-components}
Functional Components can be typed based on whether they are named or anonymous. [Vue - Official extension](https://github.com/vuejs/language-tools) also supports type checking properly typed functional components when consuming them in SFC templates.
**Named Functional Component**
```tsx
import type { SetupContext } from 'vue'
type FComponentProps = {
message: string
}
type Events = {
sendMessage(message: string): void
}
function FComponent(
props: FComponentProps,
context: SetupContext
) {
return (
context.emit('sendMessage', props.message)}>
{props.message} {' '}
)
}
FComponent.props = {
message: {
type: String,
required: true
}
}
FComponent.emits = {
sendMessage: (value: unknown) => typeof value === 'string'
}
```
**Anonymous Functional Component**
```tsx
import type { FunctionalComponent } from 'vue'
type FComponentProps = {
message: string
}
type Events = {
sendMessage(message: string): void
}
const FComponent: FunctionalComponent = (
props,
context
) => {
return (
context.emit('sendMessage', props.message)}>
{props.message} {' '}
)
}
FComponent.props = {
message: {
type: String,
required: true
}
}
FComponent.emits = {
sendMessage: (value) => typeof value === 'string'
}
```
---
---
url: /guide/extras/web-components.md
---
# Vue and Web Components {#vue-and-web-components}
[Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) is an umbrella term for a set of web native APIs that allows developers to create reusable custom elements.
We consider Vue and Web Components to be primarily complementary technologies. Vue has excellent support for both consuming and creating custom elements. Whether you are integrating custom elements into an existing Vue application, or using Vue to build and distribute custom elements, you are in good company.
## Using Custom Elements in Vue {#using-custom-elements-in-vue}
Vue [scores a perfect 100% in the Custom Elements Everywhere tests](https://custom-elements-everywhere.com/libraries/vue/results/results.html). Consuming custom elements inside a Vue application largely works the same as using native HTML elements, with a few things to keep in mind:
### Skipping Component Resolution {#skipping-component-resolution}
By default, Vue will attempt to resolve a non-native HTML tag as a registered Vue component before falling back to rendering it as a custom element. This will cause Vue to emit a "failed to resolve component" warning during development. To let Vue know that certain elements should be treated as custom elements and skip component resolution, we can specify the [`compilerOptions.isCustomElement` option](/api/application#app-config-compileroptions).
If you are using Vue with a build setup, the option should be passed via build configs since it is a compile-time option.
#### Example In-Browser Config {#example-in-browser-config}
```js
// Only works if using in-browser compilation.
// If using build tools, see config examples below.
app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-')
```
#### Example Vite Config {#example-vite-config}
```js [vite.config.js]
import vue from '@vitejs/plugin-vue'
export default {
plugins: [
vue({
template: {
compilerOptions: {
// treat all tags with a dash as custom elements
isCustomElement: (tag) => tag.includes('-')
}
}
})
]
}
```
#### Example Vue CLI Config {#example-vue-cli-config}
```js [vue.config.js]
module.exports = {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => ({
...options,
compilerOptions: {
// treat any tag that starts with ion- as custom elements
isCustomElement: (tag) => tag.startsWith('ion-')
}
}))
}
}
```
### Passing DOM Properties {#passing-dom-properties}
Since DOM attributes can only be strings, we need to pass complex data to custom elements as DOM properties. When setting props on a custom element, Vue 3 automatically checks DOM-property presence using the `in` operator and will prefer setting the value as a DOM property if the key is present. This means that, in most cases, you won't need to think about this if the custom element follows the [recommended best practices](https://web.dev/custom-elements-best-practices/).
However, there could be rare cases where the data must be passed as a DOM property, but the custom element does not properly define/reflect the property (causing the `in` check to fail). In this case, you can force a `v-bind` binding to be set as a DOM property using the `.prop` modifier:
```vue-html
```
## Building Custom Elements with Vue {#building-custom-elements-with-vue}
The primary benefit of custom elements is that they can be used with any framework, or even without a framework. This makes them ideal for distributing components where the end consumer may not be using the same frontend stack, or when you want to insulate the end application from the implementation details of the components it uses.
### defineCustomElement {#definecustomelement}
Vue supports creating custom elements using exactly the same Vue component APIs via the [`defineCustomElement`](/api/custom-elements#definecustomelement) method. The method accepts the same argument as [`defineComponent`](/api/general#definecomponent), but instead returns a custom element constructor that extends `HTMLElement`:
```vue-html
```
```js
import { defineCustomElement } from 'vue'
const MyVueElement = defineCustomElement({
// normal Vue component options here
props: {},
emits: {},
template: `...`,
// defineCustomElement only: CSS to be injected into shadow root
styles: [`/* inlined css */`]
})
// Register the custom element.
// After registration, all `` tags
// on the page will be upgraded.
customElements.define('my-vue-element', MyVueElement)
// You can also programmatically instantiate the element:
// (can only be done after registration)
document.body.appendChild(
new MyVueElement({
// initial props (optional)
})
)
```
#### Lifecycle {#lifecycle}
* A Vue custom element will mount an internal Vue component instance inside its shadow root when the element's [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks) is called for the first time.
* When the element's `disconnectedCallback` is invoked, Vue will check whether the element is detached from the document after a microtask tick.
* If the element is still in the document, it's a move and the component instance will be preserved;
* If the element is detached from the document, it's a removal and the component instance will be unmounted.
#### Props {#props}
* All props declared using the `props` option will be defined on the custom element as properties. Vue will automatically handle the reflection between attributes / properties where appropriate.
* Attributes are always reflected to corresponding properties.
* Properties with primitive values (`string`, `boolean` or `number`) are reflected as attributes.
* Vue also automatically casts props declared with `Boolean` or `Number` types into the desired type when they are set as attributes (which are always strings). For example, given the following props declaration:
```js
props: {
selected: Boolean,
index: Number
}
```
And the custom element usage:
```vue-html
```
In the component, `selected` will be cast to `true` (boolean) and `index` will be cast to `1` (number).
#### Events {#events}
Events emitted via `this.$emit` or setup `emit` are dispatched as native [CustomEvents](https://developer.mozilla.org/en-US/docs/Web/Events/Creating_and_triggering_events#adding_custom_data_%E2%80%93_customevent) on the custom element. Additional event arguments (payload) will be exposed as an array on the CustomEvent object as its `detail` property.
#### Slots {#slots}
Inside the component, slots can be rendered using the ` ` element as usual. However, when consuming the resulting element, it only accepts [native slots syntax](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots):
* [Scoped slots](/guide/components/slots#scoped-slots) are not supported.
* When passing named slots, use the `slot` attribute instead of the `v-slot` directive:
```vue-html
hello
```
#### Provide / Inject {#provide-inject}
The [Provide / Inject API](/guide/components/provide-inject#provide-inject) and its [Composition API equivalent](/api/composition-api-dependency-injection#provide) also work between Vue-defined custom elements. However, note that this works **only between custom elements**. i.e. a Vue-defined custom element won't be able to inject properties provided by a non-custom-element Vue component.
#### App Level Config {#app-level-config}
You can configure the app instance of a Vue custom element using the `configureApp` option:
```js
defineCustomElement(MyComponent, {
configureApp(app) {
app.config.errorHandler = (err) => {
/* ... */
}
}
})
```
### SFC as Custom Element {#sfc-as-custom-element}
`defineCustomElement` also works with Vue Single-File Components (SFCs). However, with the default tooling setup, the `
This could be e.g. documentation for the component.
```
## Language Blocks {#language-blocks}
### `` {#template}
* Each `*.vue` file can contain at most one top-level `` block.
* Contents will be extracted and passed on to `@vue/compiler-dom`, pre-compiled into JavaScript render functions, and attached to the exported component as its `render` option.
### `
```
`lang` can be applied to any block - for example we can use `
```
Note that integration with various pre-processors may differ by toolchain. Check out the respective documentation for examples:
* [Vite](https://vite.dev/guide/features.html#css-pre-processors)
* [Vue CLI](https://cli.vuejs.org/guide/css.html#pre-processors)
* [webpack + vue-loader](https://vue-loader.vuejs.org/guide/pre-processors.html#using-pre-processors)
## `src` Imports {#src-imports}
If you prefer splitting up your `*.vue` components into multiple files, you can use the `src` attribute to import an external file for a language block:
```vue
```
Beware that `src` imports follow the same path resolution rules as webpack module requests, which means:
* Relative paths need to start with `./`
* You can import resources from npm dependencies:
```vue
```
`src` imports also work with custom blocks, e.g.:
```vue
```
:::warning Note
While using aliases in `src`, don't start with `~`, anything after it is interpreted as a module request. This means you can reference assets inside node modules:
```vue
```
:::
## Comments {#comments}
Inside each block you shall use the comment syntax of the language being used (HTML, CSS, JavaScript, Pug, etc.). For top-level comments, use HTML comment syntax: ``
---
---
url: /api/sfc-script-setup.md
---
# \
```
The code inside is compiled as the content of the component's `setup()` function. This means that unlike normal `
{{ msg }}
```
Imports are exposed in the same fashion. This means you can directly use an imported helper function in template expressions without having to expose it via the `methods` option:
```vue
{{ capitalize('hello') }}
```
## Reactivity {#reactivity}
Reactive state needs to be explicitly created using [Reactivity APIs](./reactivity-core). Similar to values returned from a `setup()` function, refs are automatically unwrapped when referenced in templates:
```vue
{{ count }}
```
## Using Components {#using-components}
Values in the scope of `
```
Think of `MyComponent` as being referenced as a variable. If you have used JSX, the mental model is similar here. The kebab-case equivalent `` also works in the template - however PascalCase component tags are strongly recommended for consistency. It also helps differentiating from native custom elements.
### Dynamic Components {#dynamic-components}
Since components are referenced as variables instead of registered under string keys, we should use dynamic `:is` binding when using dynamic components inside `
```
Note how the components can be used as variables in a ternary expression.
### Recursive Components {#recursive-components}
An SFC can implicitly refer to itself via its filename. E.g. a file named `FooBar.vue` can refer to itself as ` ` in its template.
Note this has lower priority than imported components. If you have a named import that conflicts with the component's inferred name, you can alias the import:
```js
import { FooBar as FooBarChild } from './components'
```
### Namespaced Components {#namespaced-components}
You can use component tags with dots like `` to refer to components nested under object properties. This is useful when you import multiple components from a single file:
```vue
label
```
## Using Custom Directives {#using-custom-directives}
Globally registered custom directives just work as normal. Local custom directives don't need to be explicitly registered with `
This is a Heading
```
If you're importing a directive from elsewhere, it can be renamed to fit the required naming scheme:
```vue
```
## defineProps() & defineEmits() {#defineprops-defineemits}
To declare options like `props` and `emits` with full type inference support, we can use the `defineProps` and `defineEmits` APIs, which are automatically available inside `
```
* `defineProps` and `defineEmits` are **compiler macros** only usable inside `
```
```vue [Parent.vue]
```
Also, when using `withDefaults` with `defineProps`, default values for mutable reference types (like arrays or objects) should be wrapped in functions in `defineModel` to avoid accidental modification and external side effects.
:::
### Modifiers and Transformers {#modifiers-and-transformers}
To access modifiers used with the `v-model` directive, we can destructure the return value of `defineModel()` like this:
```js
const [modelValue, modelModifiers] = defineModel()
// corresponds to v-model.trim
if (modelModifiers.trim) {
// ...
}
```
When a modifier is present, we likely need to transform the value when reading or syncing it back to the parent. We can achieve this by using the `get` and `set` transformer options:
```js
const [modelValue, modelModifiers] = defineModel({
// get() omitted as it is not needed here
set(value) {
// if the .trim modifier is used, return trimmed value
if (modelModifiers.trim) {
return value.trim()
}
// otherwise, return the value as-is
return value
}
})
```
### Usage with TypeScript {#usage-with-typescript}
Like `defineProps` and `defineEmits`, `defineModel` can also receive type arguments to specify the types of the model value and the modifiers:
```ts
const modelValue = defineModel()
// ^? Ref
// default model with options, required removes possible undefined values
const modelValue = defineModel({ required: true })
// ^? Ref
const [modelValue, modifiers] = defineModel()
// ^? Record<'trim' | 'uppercase', true | undefined>
```
## defineExpose() {#defineexpose}
Components using `
```
When a parent gets an instance of this component via template refs, the retrieved instance will be of the shape `{ a: number, b: number }` (refs are automatically unwrapped just like on normal instances).
## defineOptions() {#defineoptions}
* Only supported in 3.3+
This macro can be used to declare component options directly inside `
```
* This is a macro. The options will be hoisted to module scope and cannot access local variables in `
```
## `useSlots()` & `useAttrs()` {#useslots-useattrs}
Usage of `slots` and `attrs` inside `
```
`useSlots` and `useAttrs` are actual runtime functions that return the equivalent of `setupContext.slots` and `setupContext.attrs`. They can be used in normal composition API functions as well.
## Usage alongside normal `
```
Support for combining `
```
In addition, the awaited expression will be automatically compiled in a format that preserves the current component instance context after the `await`.
:::warning Note
`async setup()` must be used in combination with [`Suspense`](/guide/built-ins/suspense.html), which is currently still an experimental feature. We plan to finalize and document it in a future release - but if you are curious now, you can refer to its [tests](https://github.com/vuejs/core/blob/main/packages/runtime-core/__tests__/components/Suspense.spec.ts) to see how it works.
:::
## Import Statements {#imports-statements}
Import statements in vue follow [ECMAScript module specification](https://nodejs.org/api/esm.html).
In addition, you can use aliases defined in your build tool configuration:
```vue
```
## Generics {#generics}
Generic type parameters can be declared using the `generic` attribute on the `
```
The value of `generic` works exactly the same as the parameter list between `<...>` in TypeScript. For example, you can use multiple parameters, `extends` constraints, default types, and reference imported types:
```vue
```
You can use `@vue-generic` the directive to pass in explicit types, for when the type cannot be inferred:
```vue
```
In order to use a reference to a generic component in a `ref` you need to use the [`vue-component-type-helpers`](https://www.npmjs.com/package/vue-component-type-helpers) library as `InstanceType` won't work.
```vue
red
```
## `v-bind()` in CSS {#v-bind-in-css}
SFC `
```
The syntax works with [`
hello
```
The actual value will be compiled into a hashed CSS custom property, so the CSS is still static. The custom property will be applied to the component's root element via inline styles and reactively updated if the source value changes.
---
---
url: /api/custom-elements.md
---
# Custom Elements API {#custom-elements-api}
## defineCustomElement() {#definecustomelement}
This method accepts the same argument as [`defineComponent`](#definecomponent), but instead returns a native [Custom Element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) class constructor.
* **Type**
```ts
function defineCustomElement(
component:
| (ComponentOptions & CustomElementsOptions)
| ComponentOptions['setup'],
options?: CustomElementsOptions
): {
new (props?: object): HTMLElement
}
interface CustomElementsOptions {
styles?: string[]
// the following options are 3.5+
configureApp?: (app: App) => void
shadowRoot?: boolean
nonce?: string
}
```
> Type is simplified for readability.
* **Details**
In addition to normal component options, `defineCustomElement()` also supports a number of options that are custom-elements-specific:
* **`styles`**: an array of inlined CSS strings for providing CSS that should be injected into the element's shadow root.
* **`configureApp`** : a function that can be used to configure the Vue app instance for the custom element.
* **`shadowRoot`** : `boolean`, defaults to `true`. Set to `false` to render the custom element without a shadow root. This means `
```
```vue-html
×
```
```vue-html
×
```
```vue-html
×
```
---
---
url: /style-guide/rules-strongly-recommended.md
---
# Priority B Rules: Strongly Recommended {#priority-b-rules-strongly-recommended}
These rules have been found to improve readability and/or developer experience in most projects. Your code will still run if you violate them, but violations should be rare and well-justified.
## Component files {#component-files}
**Whenever a build system is available to concatenate files, each component should be in its own file.**
This helps you to more quickly find a component when you need to edit it or review how to use it.
```js
app.component('TodoList', {
// ...
})
app.component('TodoItem', {
// ...
})
```
```
components/
|- TodoList.js
|- TodoItem.js
```
```
components/
|- TodoList.vue
|- TodoItem.vue
```
## Single-file component filename casing {#single-file-component-filename-casing}
**Filenames of [Single-File Components](/guide/scaling-up/sfc) should either be always PascalCase or always kebab-case.**
PascalCase works best with autocompletion in code editors, as it's consistent with how we reference components in JS(X) and templates, wherever possible. However, mixed case filenames can sometimes create issues on case-insensitive file systems, which is why kebab-case is also perfectly acceptable.
```
components/
|- mycomponent.vue
```
```
components/
|- myComponent.vue
```
```
components/
|- MyComponent.vue
```
```
components/
|- my-component.vue
```
## Base component names {#base-component-names}
**Base components (a.k.a. presentational, dumb, or pure components) that apply app-specific styling and conventions should all begin with a specific prefix, such as `Base`, `App`, or `V`.**
::: details Detailed Explanation
These components lay the foundation for consistent styling and behavior in your application. They may **only** contain:
* HTML elements,
* other base components, and
* 3rd-party UI components.
But they'll **never** contain global state (e.g. from a [Pinia](https://pinia.vuejs.org/) store).
Their names often include the name of an element they wrap (e.g. `BaseButton`, `BaseTable`), unless no element exists for their specific purpose (e.g. `BaseIcon`). If you build similar components for a more specific context, they will almost always consume these components (e.g. `BaseButton` may be used in `ButtonSubmit`).
Some advantages of this convention:
* When organized alphabetically in editors, your app's base components are all listed together, making them easier to identify.
* Since component names should always be multi-word, this convention prevents you from having to choose an arbitrary prefix for simple component wrappers (e.g. `MyButton`, `VueButton`).
* Since these components are so frequently used, you may want to simply make them global instead of importing them everywhere. A prefix makes this possible with Vite:
```js
const modules = import.meta.glob('./src/**/Base*.vue', { eager: true })
for (const path in modules) {
const config = modules[path].default
const name = config.name || path.match(/Base[A-Z]\w+/)[0]
app.component(name, config)
}
```
:::
```
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
```
```
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
```
```
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
```
```
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
```
## Tightly coupled component names {#tightly-coupled-component-names}
**Child components that are tightly coupled with their parent should include the parent component name as a prefix.**
If a component only makes sense in the context of a single parent component, that relationship should be evident in its name. Since editors typically organize files alphabetically, this also keeps these related files next to each other.
::: details Detailed Explanation
You might be tempted to solve this problem by nesting child components in directories named after their parent. For example:
```
components/
|- TodoList/
|- Item/
|- index.vue
|- Button.vue
|- index.vue
```
or:
```
components/
|- TodoList/
|- Item/
|- Button.vue
|- Item.vue
|- TodoList.vue
```
This isn't recommended, as it results in:
* Many files with similar names, making rapid file switching in code editors more difficult.
* Many nested sub-directories, which increases the time it takes to browse components in an editor's sidebar.
:::
```
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
```
```
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
```
```
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
```
```
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
```
## Order of words in component names {#order-of-words-in-component-names}
**Component names should start with the highest-level (often most general) words and end with descriptive modifying words.**
::: details Detailed Explanation
You may be wondering:
> "Why would we force component names to use less natural language?"
In natural English, adjectives and other descriptors do typically appear before the nouns, while exceptions require connector words. For example:
* Coffee *with* milk
* Soup *of the* day
* Visitor *to the* museum
You can definitely include these connector words in component names if you'd like, but the order is still important.
Also note that **what's considered "highest-level" will be contextual to your app**. For example, imagine an app with a search form. It may include components like this one:
```
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
```
As you might notice, it's quite difficult to see which components are specific to the search. Now let's rename the components according to the rule:
```
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
```
Since editors typically organize files alphabetically, all the important relationships between components are now evident at a glance.
You might be tempted to solve this problem differently, nesting all the search components under a "search" directory, then all the settings components under a "settings" directory. We only recommend considering this approach in very large apps (e.g. 100+ components), for these reasons:
* It generally takes more time to navigate through nested sub-directories, than scrolling through a single `components` directory.
* Name conflicts (e.g. multiple `ButtonDelete.vue` components) make it more difficult to quickly navigate to a specific component in a code editor.
* Refactoring becomes more difficult, because find-and-replace often isn't sufficient to update relative references to a moved component.
:::
```
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
```
```
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
```
## Self-closing components {#self-closing-components}
**Components with no content should be self-closing in [Single-File Components](/guide/scaling-up/sfc), string templates, and [JSX](/guide/extras/render-function#jsx-tsx) - but never in in-DOM templates.**
Components that self-close communicate that they not only have no content, but are **meant** to have no content. It's the difference between a blank page in a book and one labeled "This page intentionally left blank." Your code is also cleaner without the unnecessary closing tag.
Unfortunately, HTML doesn't allow custom elements to be self-closing - only [official "void" elements](https://html.spec.whatwg.org/multipage/syntax.html#void-elements). That's why the strategy is only possible when Vue's template compiler can reach the template before the DOM, then serve the DOM spec-compliant HTML.
```vue-html
```
```vue-html
```
```vue-html
```
```vue-html
```
## Component name casing in templates {#component-name-casing-in-templates}
**In most projects, component names should always be PascalCase in [Single-File Components](/guide/scaling-up/sfc) and string templates - but kebab-case in in-DOM templates.**
PascalCase has a few advantages over kebab-case:
* Editors can autocomplete component names in templates, because PascalCase is also used in JavaScript.
* `` is more visually distinct from a single-word HTML element than ``, because there are two character differences (the two capitals), rather than just one (a hyphen).
* If you use any non-Vue custom elements in your templates, such as a web component, PascalCase ensures that your Vue components remain distinctly visible.
Unfortunately, due to HTML's case insensitivity, in-DOM templates must still use kebab-case.
Also note that if you've already invested heavily in kebab-case, consistency with HTML conventions and being able to use the same casing across all your projects may be more important than the advantages listed above. In those cases, **using kebab-case everywhere is also acceptable.**
```vue-html
```
```vue-html
```
```vue-html
```
```vue-html
```
```vue-html
```
OR
```vue-html
```
## Component name casing in JS/JSX {#component-name-casing-in-js-jsx}
**Component names in JS/[JSX](/guide/extras/render-function#jsx-tsx) should always be PascalCase, though they may be kebab-case inside strings for simpler applications that only use global component registration through `app.component`.**
::: details Detailed Explanation
In JavaScript, PascalCase is the convention for classes and prototype constructors - essentially, anything that can have distinct instances. Vue components also have instances, so it makes sense to also use PascalCase. As an added benefit, using PascalCase within JSX (and templates) allows readers of the code to more easily distinguish between components and HTML elements.
However, for applications that use **only** global component definitions via `app.component`, we recommend kebab-case instead. The reasons are:
* It's rare that global components are ever referenced in JavaScript, so following a convention for JavaScript makes less sense.
* These applications always include many in-DOM templates, where [kebab-case **must** be used](#component-name-casing-in-templates).
:::
```js
app.component('myComponent', {
// ...
})
```
```js
import myComponent from './MyComponent.vue'
```
```js
export default {
name: 'myComponent'
// ...
}
```
```js
export default {
name: 'my-component'
// ...
}
```
```js
app.component('MyComponent', {
// ...
})
```
```js
app.component('my-component', {
// ...
})
```
```js
import MyComponent from './MyComponent.vue'
```
```js
export default {
name: 'MyComponent'
// ...
}
```
## Full-word component names {#full-word-component-names}
**Component names should prefer full words over abbreviations.**
The autocompletion in editors make the cost of writing longer names very low, while the clarity they provide is invaluable. Uncommon abbreviations, in particular, should always be avoided.
```
components/
|- SdSettings.vue
|- UProfOpts.vue
```
```
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
```
## Prop name casing {#prop-name-casing}
**Prop names should always use camelCase during declaration. When used inside in-DOM templates, props should be kebab-cased. Single-File Components templates and [JSX](/guide/extras/render-function#jsx-tsx) can use either kebab-case or camelCase props. Casing should be consistent - if you choose to use camelCased props, make sure you don't use kebab-cased ones in your application**
```js
props: {
'greeting-text': String
}
```
```js
const props = defineProps({
'greeting-text': String
})
```
```vue-html
// for in-DOM templates
```
```js
props: {
greetingText: String
}
```
```js
const props = defineProps({
greetingText: String
})
```
```vue-html
// for SFC - please make sure your casing is consistent throughout the project
// you can use either convention but we don't recommend mixing two different casing styles
// or
```
```vue-html
// for in-DOM templates
```
## Multi-attribute elements {#multi-attribute-elements}
**Elements with multiple attributes should span multiple lines, with one attribute per line.**
In JavaScript, splitting objects with multiple properties over multiple lines is widely considered a good convention, because it's much easier to read. Our templates and [JSX](/guide/extras/render-function#jsx-tsx) deserve the same consideration.
```vue-html
```
```vue-html
```
```vue-html
```
```vue-html
```
## Simple expressions in templates {#simple-expressions-in-templates}
**Component templates should only include simple expressions, with more complex expressions refactored into computed properties or methods.**
Complex expressions in your templates make them less declarative. We should strive to describe *what* should appear, not *how* we're computing that value. Computed properties and methods also allow the code to be reused.
```vue-html
{{
fullName.split(' ').map((word) => {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
```
```vue-html
{{ normalizedFullName }}
```
```js
// The complex expression has been moved to a computed property
computed: {
normalizedFullName() {
return this.fullName.split(' ')
.map(word => word[0].toUpperCase() + word.slice(1))
.join(' ')
}
}
```
```js
// The complex expression has been moved to a computed property
const normalizedFullName = computed(() =>
fullName.value
.split(' ')
.map((word) => word[0].toUpperCase() + word.slice(1))
.join(' ')
)
```
## Simple computed properties {#simple-computed-properties}
**Complex computed properties should be split into as many simpler properties as possible.**
::: details Detailed Explanation
Simpler, well-named computed properties are:
* **Easier to test**
When each computed property contains only a very simple expression, with very few dependencies, it's much easier to write tests confirming that it works correctly.
* **Easier to read**
Simplifying computed properties forces you to give each value a descriptive name, even if it's not reused. This makes it much easier for other developers (and future you) to focus in on the code they care about and figure out what's going on.
* **More adaptable to changing requirements**
Any value that can be named might be useful to the view. For example, we might decide to display a message telling the user how much money they saved. We might also decide to calculate sales tax, but perhaps display it separately, rather than as part of the final price.
Small, focused computed properties make fewer assumptions about how information will be used, so require less refactoring as requirements change.
:::
```js
computed: {
price() {
const basePrice = this.manufactureCost / (1 - this.profitMargin)
return (
basePrice -
basePrice * (this.discountPercent || 0)
)
}
}
```
```js
const price = computed(() => {
const basePrice = manufactureCost.value / (1 - profitMargin.value)
return basePrice - basePrice * (discountPercent.value || 0)
})
```
```js
computed: {
basePrice() {
return this.manufactureCost / (1 - this.profitMargin)
},
discount() {
return this.basePrice * (this.discountPercent || 0)
},
finalPrice() {
return this.basePrice - this.discount
}
}
```
```js
const basePrice = computed(
() => manufactureCost.value / (1 - profitMargin.value)
)
const discount = computed(
() => basePrice.value * (discountPercent.value || 0)
)
const finalPrice = computed(() => basePrice.value - discount.value)
```
## Quoted attribute values {#quoted-attribute-values}
**Non-empty HTML attribute values should always be inside quotes (single or double, whichever is not used in JS).**
While attribute values without any spaces are not required to have quotes in HTML, this practice often leads to *avoiding* spaces, making attribute values less readable.
```vue-html
```
```vue-html
```
```vue-html
```
```vue-html
```
## Directive shorthands {#directive-shorthands}
**Directive shorthands (`:` for `v-bind:`, `@` for `v-on:` and `#` for `v-slot`) should be used always or never.**
```vue-html
```
```vue-html
```
```vue-html
Here might be a page title
Here's some contact info
```
```vue-html
```
```vue-html
```
```vue-html
```
```vue-html
```
```vue-html
Here might be a page title
Here's some contact info
```
```vue-html
Here might be a page title
Here's some contact info
```
---
---
url: /style-guide/rules-recommended.md
---
# Priority C Rules: Recommended {#priority-c-rules-recommended}
Where multiple, equally good options exist, an arbitrary choice can be made to ensure consistency. In these rules, we describe each acceptable option and suggest a default choice. That means you can feel free to make a different choice in your own codebase, as long as you're consistent and have a good reason. Please do have a good reason though! By adapting to the community standard, you will:
1. Train your brain to more easily parse most of the community code you encounter
2. Be able to copy and paste most community code examples without modification
3. Often find new hires are already accustomed to your preferred coding style, at least in regards to Vue
## Component/instance options order {#component-instance-options-order}
**Component/instance options should be ordered consistently.**
This is the default order we recommend for component options. They're split into categories, so you'll know where to add new properties from plugins.
1. **Global Awareness** (requires knowledge beyond the component)
* `name`
2. **Template Compiler Options** (changes the way templates are compiled)
* `compilerOptions`
3. **Template Dependencies** (assets used in the template)
* `components`
* `directives`
4. **Composition** (merges properties into the options)
* `extends`
* `mixins`
* `provide`/`inject`
5. **Interface** (the interface to the component)
* `inheritAttrs`
* `props`
* `emits`
* `expose`
6. **Composition API** (the entry point for using the Composition API)
* `setup`
7. **Local State** (local reactive properties)
* `data`
* `computed`
8. **Events** (callbacks triggered by reactive events)
* `watch`
* Lifecycle Events (in the order they are called)
* `beforeCreate`
* `created`
* `beforeMount`
* `mounted`
* `beforeUpdate`
* `updated`
* `activated`
* `deactivated`
* `beforeUnmount`
* `unmounted`
* `errorCaptured`
* `renderTracked`
* `renderTriggered`
* `serverPrefetch` (SSR only)
9. **Non-Reactive Properties** (instance properties independent of the reactivity system)
* `methods`
10. **Rendering** (the declarative description of the component output)
* `template`/`render`
## Element attribute order {#element-attribute-order}
**The attributes of elements (including components) should be ordered consistently.**
This is the default order we recommend for component options. They're split into categories, so you'll know where to add custom attributes and directives.
1. **Definition** (provides the component options)
* `is`
2. **List Rendering** (creates multiple variations of the same element)
* `v-for`
3. **Conditionals** (whether the element is rendered/shown)
* `v-if`
* `v-else-if`
* `v-else`
* `v-show`
* `v-cloak`
4. **Render Modifiers** (changes the way the element renders)
* `v-pre`
* `v-once`
5. **Global Awareness** (requires knowledge beyond the component)
* `id`
6. **Unique Attributes** (attributes that require unique values)
* `ref`
* `key`
7. **Two-Way Binding** (combining binding and events)
* `v-model`
8. **Other Attributes** (all unspecified bound & unbound attributes)
9. **Events** (component event listeners)
* `v-on`
10. **Content** (overrides the content of the element)
* `v-html`
* `v-text`
## Empty lines in component/instance options {#empty-lines-in-component-instance-options}
**You may want to add one empty line between multi-line properties, particularly if the options can no longer fit on your screen without scrolling.**
When components begin to feel cramped or difficult to read, adding spaces between multi-line properties can make them easier to skim again. In some editors, such as Vim, formatting options like this can also make them easier to navigate with the keyboard.
```js
props: {
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
},
computed: {
formattedValue() {
// ...
},
inputClasses() {
// ...
}
}
```
```js
// No spaces are also fine, as long as the component
// is still easy to read and navigate.
props: {
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
},
computed: {
formattedValue() {
// ...
},
inputClasses() {
// ...
}
}
```
```js
defineProps({
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
})
const formattedValue = computed(() => {
// ...
})
const inputClasses = computed(() => {
// ...
})
```
```js
defineProps({
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
})
const formattedValue = computed(() => {
// ...
})
const inputClasses = computed(() => {
// ...
})
```
## Single-file component top-level element order {#single-file-component-top-level-element-order}
**[Single-File Components](/guide/scaling-up/sfc) should always order `
...
```
```vue-html [ComponentA.vue]
...
```
```vue-html [ComponentB.vue]
...
```
```vue-html [ComponentA.vue]
...
```
```vue-html [ComponentB.vue]
...
```
or
```vue-html [ComponentA.vue]
...
```
```vue-html [ComponentB.vue]
...
```
---
---
url: /style-guide/rules-use-with-caution.md
---
# Priority D Rules: Use with Caution {#priority-d-rules-use-with-caution}
Some features of Vue exist to accommodate rare edge cases or smoother migrations from a legacy code base. When overused however, they can make your code more difficult to maintain or even become a source of bugs. These rules shine a light on potentially risky features, describing when and why they should be avoided.
## Element selectors with `scoped` {#element-selectors-with-scoped}
**Element selectors should be avoided with `scoped`.**
Prefer class selectors over element selectors in `scoped` styles, because large numbers of element selectors are slow.
::: details Detailed Explanation
To scope styles, Vue adds a unique attribute to component elements, such as `data-v-f3f3eg9`. Then selectors are modified so that only matching elements with this attribute are selected (e.g. `button[data-v-f3f3eg9]`).
The problem is that large numbers of element-attribute selectors (e.g. `button[data-v-f3f3eg9]`) will be considerably slower than class-attribute selectors (e.g. `.btn-close[data-v-f3f3eg9]`), so class selectors should be preferred whenever possible.
:::
```vue-html
×
```
```vue-html
×
```
## Implicit parent-child communication {#implicit-parent-child-communication}
**Props and events should be preferred for parent-child component communication, instead of `this.$parent` or mutating props.**
An ideal Vue application is props down, events up. Sticking to this convention makes your components much easier to understand. However, there are edge cases where prop mutation or `this.$parent` can simplify two components that are already deeply coupled.
The problem is, there are also many *simple* cases where these patterns may offer convenience. Beware: do not be seduced into trading simplicity (being able to understand the flow of your state) for short-term convenience (writing less code).
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
template: ' '
})
```
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
methods: {
removeTodo() {
this.$parent.todos = this.$parent.todos.filter(
(todo) => todo.id !== vm.todo.id
)
}
},
template: `
{{ todo.text }}
×
`
})
```
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
emits: ['input'],
template: `
`
})
```
```js
app.component('TodoItem', {
props: {
todo: {
type: Object,
required: true
}
},
emits: ['delete'],
template: `
{{ todo.text }}
×
`
})
```
```vue
```
```vue
{{ todo.text }}
rename
```
```vue
```
```vue
{{ todo.text }}
rename
```
---
---
url: /api.md
---
---
---
url: /about/community-guide.md
---
# Community Guide {#community-guide}
Vue's community is growing incredibly fast and if you're reading this, there's a good chance you're ready to join it. So... welcome!
Now we'll answer both what the community can do for you and what you can do for the community.
## Resources {#resources}
### Code of Conduct {#code-of-conduct}
Our [Code of Conduct](/about/coc) is a guide to make it easier to enrich all of us and the technical communities in which we participate.
### Stay in the Know {#stay-in-the-know}
* Follow our [official Twitter account](https://x.com/vuejs).
* Follow our [team members](./team) on Twitter or GitHub.
* Follow the [RFC discussions](https://github.com/vuejs/rfcs).
* Subscribe to the [official blog](https://blog.vuejs.org/).
### Get Support {#get-support}
* [Discord Chat](https://discord.com/invite/vue): A place for Vue devs to meet and chat in real time.
* [Forum](https://forum.vuejs.org/): The best place to ask questions and get answers about Vue and its ecosystem.
* [DEV Community](https://dev.to/t/vue): Share and discuss Vue related topics on Dev.to.
* [Meetups](https://events.vuejs.org/meetups): Want to find local Vue enthusiasts like yourself? Interested in becoming a community leader? We have the help and support you need right here!
* [GitHub](https://github.com/vuejs): If you have a bug to report or feature to request, that's what the GitHub issues are for. Please respect the rules specified in each repository's issue template.
* [Twitter Community (unofficial)](https://x.com/i/communities/1516368750634840064): A Twitter community, where you can meet other Vue enthusiasts, get help, or just chat about Vue.
### Explore the Ecosystem {#explore-the-ecosystem}
* [The Awesome Vue Page](https://github.com/vuejs/awesome-vue): See what other awesome resources have been published by other awesome people.
* [Vue Telescope Explorer](https://vuetelescope.com/explore): Explore websites made with Vue, with insights on what framework / libraries they use.
* [Made with Vue.js](https://madewithvuejs.com/): showcases of projects and libraries made with Vue.
* [The "Show and Tell" Subforum](https://github.com/vuejs/core/discussions/categories/show-and-tell): Another great place to check out what others have built with and for the growing Vue ecosystem.
## What You Can Do {#what-you-can-do}
### Help Fellow Users {#help-fellow-users}
Code contribution is not the only form of contribution to the Vue community. Answering a question for a fellow Vue user on Discord or the forum is also considered a valuable contribution.
### Help Triage Issues {#help-triage-issues}
Triaging an issue means gathering missing information, running the reproduction, verifying the issue's validity, and investigating the cause of the issue.
We receive many issues in [our repositories on GitHub](https://github.com/vuejs) every single day. Our bandwidth is limited compared to the amount of users we have, so issue triaging alone can take an enormous amount of effort from the team. By helping us triage the issues, you are helping us become more efficient, allowing us to spend time on higher priority work.
You don't have to triage an issue with the goal of fixing it (although that would be nice too). Sharing the result of your investigation, for example the commit that led to the bug, can already save us a ton of time.
### Contribute Code {#contribute-code}
Contributing bug fixes or new features is the most direct form of contribution you can make.
The Vue core repository provides a [contributing guide](https://github.com/vuejs/core/blob/main/.github/contributing.md), which contains pull request guidelines and information regarding build setup and high-level architecture. Other sub-project repositories may also contain its own contribution guide - please make sure to read them before submitting pull requests.
Bug fixes are welcome at any time. For new features, it is best to discuss the use case and implementation details first in the [RFC repo](https://github.com/vuejs/rfcs/discussions).
### Share (and Build) Your Experience {#share-and-build-your-experience}
Apart from answering questions and sharing resources in the forum and chat, there are a few other less obvious ways to share and expand what you know:
* **Develop learning materials.** It's often said that the best way to learn is to teach. If there's something interesting you're doing with Vue, strengthen your expertise by writing a blog post, developing a workshop, or even publishing a gist that you share on social media.
* **Watch a repo you care about.** This will send you notifications whenever there's activity in that repository, giving you insider knowledge about ongoing discussions and upcoming features. It's a fantastic way to build expertise so that you're eventually able to help address issues and pull requests.
### Translate Docs {#translate-docs}
I hope that right now, you're reading this sentence in your preferred language. If not, would you like to help us get there?
See the [Translations guide](/translations/) for more details on how you can get involved.
### Become a Community Leader {#become-a-community-leader}
There's a lot you can do to help Vue grow in your community:
* **Present at your local meetup.** Whether it's giving a talk or running a workshop, you can bring a lot of value to your community by helping both new and experienced Vue developers continue to grow.
* **Start your own meetup.** If there's not already a Vue meetup in your area, you can start your own! Use the [resources at events.vuejs.org](https://events.vuejs.org/resources/#getting-started) to help you succeed!
* **Help meetup organizers.** There can never be too much help when it comes to running an event, so offer a hand to help out local organizers to help make every event a success.
If you have any questions on how you can get more involved with your local Vue community, reach out on Twitter at [@vuejs\_events](https://x.com/vuejs_events)!
---
---
url: /ecosystem/newsletters.md
---
# Community Newsletters {#community-newsletters}
There are many great newsletters / Vue-dedicated blogs from the community bringing you latest news and happenings in the Vue ecosystem. Here is a non-exhaustive list of active ones that we have come across:
* [Vue.js Feed](https://vuejsfeed.com/)
* [Michael Thiessen](https://michaelnthiessen.com/newsletter)
* [Jakub Andrzejewski](https://dev.to/jacobandrewsky)
* [Weekly Vue News](https://weekly-vue.news/)
* [Vue.js Developers Newsletter](https://vuejsdevelopers.com/newsletter/)
If you know a great one that isn't already included, please submit a pull request using the link below!
---
---
url: /about/faq.md
---
# Frequently Asked Questions {#frequently-asked-questions}
## Who maintains Vue? {#who-maintains-vue}
Vue is an independent, community-driven project. It was created by [Evan You](https://x.com/evanyou) in 2014 as a personal side project. Today, Vue is actively maintained by [a team of both full-time and volunteer members from all around the world](/about/team), where Evan serves as the project lead. You can learn more about the story of Vue in this [documentary](https://www.youtube.com/watch?v=OrxmtDw4pVI).
Vue's development is primarily funded through sponsorships and we have been financially sustainable since 2016. If you or your business benefit from Vue, consider [sponsoring us](/sponsor/) to support Vue's development!
## What's the difference between Vue 2 and Vue 3? {#what-s-the-difference-between-vue-2-and-vue-3}
Vue 3 is the current, latest major version of Vue. It contains new features that are not present in Vue 2, such as Teleport, Suspense, and multiple root elements per template. It also contains breaking changes that make it incompatible with Vue 2. Full details are documented in the [Vue 3 Migration Guide](https://v3-migration.vuejs.org/).
Despite the differences, the majority of Vue APIs are shared between the two major versions, so most of your Vue 2 knowledge will continue to work in Vue 3. Notably, Composition API was originally a Vue-3-only feature, but has now been backported to Vue 2 and is available in [Vue 2.7](https://github.com/vuejs/vue/blob/main/CHANGELOG.md#270-2022-07-01).
In general, Vue 3 provides smaller bundle sizes, better performance, better scalability, and better TypeScript / IDE support. If you are starting a new project today, Vue 3 is the recommended choice. There are only a few reasons for you to consider Vue 2 as of now:
* You need to support IE11. Vue 3 leverages modern JavaScript features and does not support IE11.
If you intend to migrate an existing Vue 2 app to Vue 3, consult the [migration guide](https://v3-migration.vuejs.org/).
## Is Vue 2 Still Supported? {#is-vue-2-still-supported}
Vue 2.7, which was shipped in July 2022, is the final minor release of the Vue 2 version range. Vue 2 has entered maintenance mode: it will no longer ship new features, but will continue to receive critical bug fixes and security updates for 18 months starting from the 2.7 release date. This means **Vue 2 reached End of Life on December 31st, 2023**.
We believe this should provide plenty of time for most of the ecosystem to migrate over to Vue 3. However, we also understand that there could be teams or projects that cannot upgrade by this timeline while still needing to fulfill security and compliance requirements. We are partnering with industry experts to provide extended support for Vue 2 for teams with such needs - if your team expects to be using Vue 2 beyond the end of 2023, make sure to plan ahead and learn more about [Vue 2 Extended LTS](https://v2.vuejs.org/lts/).
## What license does Vue use? {#what-license-does-vue-use}
Vue is a free and open source project released under the [MIT License](https://opensource.org/licenses/MIT).
## What browsers does Vue support? {#what-browsers-does-vue-support}
The latest version of Vue (3.x) only supports [browsers with native ES2016 support](https://caniuse.com/es2016). This excludes IE11. Vue 3.x uses ES2016 features that cannot be polyfilled in legacy browsers, so if you need to support legacy browsers, you will need to use Vue 2.x instead.
## Is Vue reliable? {#is-vue-reliable}
Vue is a mature and battle-tested framework. It is one of the most widely used JavaScript frameworks in production today, with over 1.5 million users worldwide, and is downloaded close to 10 million times a month on npm.
Vue is used in production by renowned organizations in varying capacities all around the world, including Wikimedia Foundation, NASA, Apple, Google, Microsoft, GitLab, Zoom, Tencent, Weibo, Bilibili, Kuaishou, and many more.
## Is Vue fast? {#is-vue-fast}
Vue 3 is one of the most performant mainstream frontend frameworks, and handles most web application use cases with ease, without the need for manual optimizations.
In stress-testing scenarios, Vue outperforms React and Angular by a decent margin in the [js-framework-benchmark](https://krausest.github.io/js-framework-benchmark/current.html). It also goes neck-and-neck against some of the fastest production-level non-Virtual-DOM frameworks in the benchmark.
Do note that synthetic benchmarks like the above focus on raw rendering performance with dedicated optimizations and may not be fully representative of real-world performance results. If you care more about page load performance, you are welcome to audit this very website using [WebPageTest](https://www.webpagetest.org/lighthouse) or [PageSpeed Insights](https://pagespeed.web.dev/). This website is powered by Vue itself, with SSG pre-rendering, full page hydration and SPA client-side navigation. It scores 100 in performance on an emulated Moto G4 with 4x CPU throttling over slow 4G networks.
You can learn more about how Vue automatically optimizes runtime performance in the [Rendering Mechanism](/guide/extras/rendering-mechanism) section, and how to optimize a Vue app in particularly demanding cases in the [Performance Optimization Guide](/guide/best-practices/performance).
## Is Vue lightweight? {#is-vue-lightweight}
When you use a build tool, many of Vue's APIs are ["tree-shakable"](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking). For example, if you don't use the built-in `` component, it won't be included in the final production bundle.
A hello world Vue app that only uses the absolutely minimal APIs has a baseline size of only around **16kb**, with minification and brotli compression. The actual size of the application will depend on how many optional features you use from the framework. In the unlikely case where an app uses every single feature that Vue provides, the total runtime size is around **27kb**.
When using Vue without a build tool, we not only lose tree-shaking, but also have to ship the template compiler to the browser. This bloats up the size to around **41kb**. Therefore, if you are using Vue primarily for progressive enhancement without a build step, consider using [petite-vue](https://github.com/vuejs/petite-vue) (only **6kb**) instead.
Some frameworks, such as Svelte, use a compilation strategy that produces extremely lightweight output in single-component scenarios. However, [our research](https://github.com/yyx990803/vue-svelte-size-analysis) shows that the size difference heavily depends on the number of components in the application. While Vue has a heavier baseline size, it generates less code per component. In real-world scenarios, a Vue app may very well end up being lighter.
## Does Vue scale? {#does-vue-scale}
Yes. Despite a common misconception that Vue is only suitable for simple use cases, Vue is perfectly capable of handling large scale applications:
* [Single-File Components](/guide/scaling-up/sfc) provide a modularized development model that allows different parts of an application to be developed in isolation.
* [Composition API](/guide/reusability/composables) provides first-class TypeScript integration and enables clean patterns for organizing, extracting and reusing complex logic.
* [Comprehensive tooling support](/guide/scaling-up/tooling) ensures a smooth development experience as the application grows.
* Lower barrier to entry and excellent documentation translate to lower onboarding and training costs for new developers.
## How do I contribute to Vue? {#how-do-i-contribute-to-vue}
We appreciate your interest! Please check out our [Community Guide](/about/community-guide).
## Should I use Options API or Composition API? {#should-i-use-options-api-or-composition-api}
If you are new to Vue, we provide a high-level comparison between the two styles [here](/guide/introduction#which-to-choose).
If you have previously used Options API and are currently evaluating Composition API, check out [this FAQ](/guide/extras/composition-api-faq).
## Should I use JavaScript or TypeScript with Vue? {#should-i-use-javascript-or-typescript-with-vue}
While Vue itself is implemented in TypeScript and provides first-class TypeScript support, it does not enforce an opinion on whether you should use TypeScript as a user.
TypeScript support is an important consideration when new features are added to Vue. APIs that are designed with TypeScript in mind are typically easier for IDEs and linters to understand, even if you aren't using TypeScript yourself. Everybody wins. Vue APIs are also designed to work the same way in both JavaScript and TypeScript as much as possible.
Adopting TypeScript involves a trade-off between onboarding complexity and long-term maintainability gains. Whether such a trade-off can be justified can vary depending on your team's background and project scale, but Vue isn't really an influencing factor in making that decision.
## How does Vue compare to Web Components? {#how-does-vue-compare-to-web-components}
Vue was created before Web Components were natively available, and some aspects of Vue's design (e.g. slots) were inspired by the Web Components model.
The Web Components specs are relatively low-level, as they are centered around defining custom elements. As a framework, Vue addresses additional higher-level concerns such as efficient DOM rendering, reactive state management, tooling, client-side routing, and server-side rendering.
Vue also fully supports consuming or exporting to native custom elements - check out the [Vue and Web Components Guide](/guide/extras/web-components) for more details.
---
---
url: /glossary.md
---
# Glossary {#glossary}
This glossary is intended to provide some guidance about the meanings of technical terms that are in common usage when talking about Vue. It is intended to be *descriptive* of how terms are commonly used, not a *prescriptive* specification of how they must be used. Some terms may have slightly different meanings or nuances depending on the surrounding context.
\[\[TOC]]
## async component {#async-component}
An *async component* is a wrapper around another component that allows for the wrapped component to be lazy loaded. This is typically used as a way to reduce the size of the built `.js` files, allowing them to be split into smaller chunks that are loaded only when required.
Vue Router has a similar feature for the [lazy loading of route components](https://router.vuejs.org/guide/advanced/lazy-loading.html), though this does not use Vue's async components feature.
For more details see:
* [Guide - Async Components](/guide/components/async.html)
## compiler macro {#compiler-macro}
A *compiler macro* is special code that is processed by a compiler and converted into something else. They are effectively a clever form of string replacement.
Vue's [SFC](#single-file-component) compiler supports various macros, such as `defineProps()`, `defineEmits()` and `defineExpose()`. These macros are intentionally designed to look like normal JavaScript functions so that they can leverage the same parser and type inference tooling around JavaScript / TypeScript. However, they are not actual functions that are run in the browser. These are special strings that the compiler detects and replaces with the real JavaScript code that will actually be run.
Macros have limitations on their use that don't apply to normal JavaScript code. For example, you might think that `const dp = defineProps` would allow you to create an alias for `defineProps`, but it'll actually result in an error. There are also limitations on what values can be passed to `defineProps()`, as the 'arguments' have to be processed by the compiler and not at runtime.
For more details see:
* [`
{{ count }}
```
The `$ref()` method here is a **compile-time macro**: it is not an actual method that will be called at runtime. Instead, the Vue compiler uses it as a hint to treat the resulting `count` variable as a **reactive variable.**
Reactive variables can be accessed and re-assigned just like normal variables, but these operations are compiled into refs with `.value`. For example, the `
```
The above will be compiled into the following runtime declaration equivalent:
```js
export default {
props: {
msg: { type: String, required: true },
count: { type: Number, default: 1 },
foo: String
},
setup(props) {
watchEffect(() => {
console.log(props.msg, props.count, props.foo)
})
}
}
```
## Retaining Reactivity Across Function Boundaries {#retaining-reactivity-across-function-boundaries}
While reactive variables relieve us from having to use `.value` everywhere, it creates an issue of "reactivity loss" when we pass reactive variables across function boundaries. This can happen in two cases:
### Passing into function as argument {#passing-into-function-as-argument}
Given a function that expects a ref as an argument, e.g.:
```ts
function trackChange(x: Ref) {
watch(x, (x) => {
console.log('x changed!')
})
}
let count = $ref(0)
trackChange(count) // doesn't work!
```
The above case will not work as expected because it compiles to:
```ts
let count = ref(0)
trackChange(count.value)
```
Here `count.value` is passed as a number, whereas `trackChange` expects an actual ref. This can be fixed by wrapping `count` with `$$()` before passing it:
```diff
let count = $ref(0)
- trackChange(count)
+ trackChange($$(count))
```
The above compiles to:
```js
import { ref } from 'vue'
let count = ref(0)
trackChange(count)
```
As we can see, `$$()` is a macro that serves as an **escape hint**: reactive variables inside `$$()` will not get `.value` appended.
### Returning inside function scope {#returning-inside-function-scope}
Reactivity can also be lost if reactive variables are used directly in a returned expression:
```ts
function useMouse() {
let x = $ref(0)
let y = $ref(0)
// listen to mousemove...
// doesn't work!
return {
x,
y
}
}
```
The above return statement compiles to:
```ts
return {
x: x.value,
y: y.value
}
```
In order to retain reactivity, we should be returning the actual refs, not the current value at return time.
Again, we can use `$$()` to fix this. In this case, `$$()` can be used directly on the returned object - any reference to reactive variables inside the `$$()` call will retain the reference to their underlying refs:
```ts
function useMouse() {
let x = $ref(0)
let y = $ref(0)
// listen to mousemove...
// fixed
return $$({
x,
y
})
}
```
### Using `$$()` on destructured props {#using-on-destructured-props}
`$$()` works on destructured props since they are reactive variables as well. The compiler will convert it with `toRef` for efficiency:
```ts
const { count } = defineProps<{ count: number }>()
passAsRef($$(count))
```
compiles to:
```js
setup(props) {
const __props_count = toRef(props, 'count')
passAsRef(__props_count)
}
```
## TypeScript Integration {#typescript-integration}
Vue provides typings for these macros (available globally) and all types will work as expected. There are no incompatibilities with standard TypeScript semantics, so the syntax will work with all existing tooling.
This also means the macros can work in any files where valid JS / TS are allowed - not just inside Vue SFCs.
Since the macros are available globally, their types need to be explicitly referenced (e.g. in a `env.d.ts` file):
```ts
///
```
When explicitly importing the macros from `vue/macros`, the type will work without declaring the globals.
## Explicit Opt-in {#explicit-opt-in}
:::danger No longer supported in core
The following only applies up to Vue version 3.3 and below. Support has been removed in Vue core 3.4 and above, and `@vitejs/plugin-vue` 5.0 and above. If you intend to continue using the transform, please migrate to [Vue Macros](https://vue-macros.sxzz.moe/features/reactivity-transform.html) instead.
:::
### Vite {#vite}
* Requires `@vitejs/plugin-vue@>=2.0.0`
* Applies to SFCs and js(x)/ts(x) files. A fast usage check is performed on files before applying the transform so there should be no performance cost for files not using the macros.
* Note `reactivityTransform` is now a plugin root-level option instead of nested as `script.refSugar`, since it affects not just SFCs.
```js [vite.config.js]
export default {
plugins: [
vue({
reactivityTransform: true
})
]
}
```
### `vue-cli` {#vue-cli}
* Currently only affects SFCs
* Requires `vue-loader@>=17.0.0`
```js [vue.config.js]
module.exports = {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => {
return {
...options,
reactivityTransform: true
}
})
}
}
```
### Plain `webpack` + `vue-loader` {#plain-webpack-vue-loader}
* Currently only affects SFCs
* Requires `vue-loader@>=17.0.0`
```js [webpack.config.js]
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
reactivityTransform: true
}
}
]
}
}
```
---
---
url: /about/releases.md
---
# Releases {#releases}
A full changelog of past releases is available on [GitHub](https://github.com/vuejs/core/blob/main/CHANGELOG.md).
## Release Cycle {#release-cycle}
Vue does not have a fixed release cycle.
* Patch releases are released as needed.
* Minor releases always contain new features, with a typical time frame of 3~6 months in between. Minor releases always go through a beta pre-release phase.
* Major releases will be announced ahead of time, and will go through an early discussion phase and alpha / beta pre-release phases.
## Semantic Versioning Edge Cases {#semantic-versioning-edge-cases}
Vue releases follow [Semantic Versioning](https://semver.org/) with a few edge cases.
### TypeScript Definitions {#typescript-definitions}
We may ship incompatible changes to TypeScript definitions between **minor** versions. This is because:
1. Sometimes TypeScript itself ships incompatible changes between minor versions, and we may have to adjust types to support newer versions of TypeScript.
2. Occasionally we may need to adopt features that are only available in a newer version of TypeScript, raising the minimum required version of TypeScript.
If you are using TypeScript, you can use a semver range that locks the current minor and manually upgrade when a new minor version of Vue is released.
### Compiled Code Compatibility with Older Runtime {#compiled-code-compatibility-with-older-runtime}
A newer **minor** version of Vue compiler may generate code that isn't compatible with the Vue runtime from an older minor version. For example, code generated by Vue 3.2 compiler may not be fully compatible if consumed by the runtime from Vue 3.1.
This is only a concern for library authors, because in applications, the compiler version and the runtime version is always the same. A version mismatch can only happen if you ship pre-compiled Vue component code as a package, and a consumer uses it in a project using an older version of Vue. As a result, your package may need to explicitly declare a minimum required minor version of Vue.
## Pre Releases {#pre-releases}
Minor and major releases typically go through a series of pre-release phases: **alpha**, **beta**, and **release candidate (RC)**. The number and type of pre-releases depend on the scope of changes. For example, a minor release with limited updates may only have a beta phase, while a major release will usually include all three phases to allow for thorough testing and community feedback.
You can install the latest pre-releases from npm using `npx install-vue@alpha`, `npx install-vue@beta`, or `npx install-vue@rc`. For testing changes not yet included in tagged pre-releases, every commit to the [vuejs/core](https://github.com/vuejs/core) repository is published as a temporary continuous-release preview, which you can install using `npx install-vue@edge`.
Pre-releases are meant for integration / stability testing, and for early adopters to provide feedback for unstable features. Do not use pre-releases in production. All pre-releases are considered unstable and may ship breaking changes in between, so always pin to exact versions when using pre-releases.
## Deprecations {#deprecations}
We may periodically deprecate features that have new, better replacements in minor releases. Deprecated features will continue to work, and will be removed in the next major release after it entered deprecated status.
## RFCs {#rfcs}
New features with substantial API surface and major changes to Vue will go through the **Request for Comments** (RFC) process. The RFC process is intended to provide a consistent and controlled path for new features to enter the framework, and give the users an opportunity to participate and offer feedback in the design process.
The RFC process is conducted in the [vuejs/rfcs](https://github.com/vuejs/rfcs) repo on GitHub.
## Experimental Features {#experimental-features}
Some features are shipped and documented in a stable version of Vue, but marked as experimental. Experimental features are typically features that have an associated RFC discussion with most of the design problems resolved on paper, but still lacking feedback from real-world usage.
The goal of experimental features is to allow users to provide feedback for them by testing them in a production setting, without having to use an unstable version of Vue. Experimental features themselves are considered unstable, and should only be used in a controlled manner, with the expectation that the feature may change between any release types.
---
---
url: /translations.md
---
# Translations {#translations}
## Available Languages {#available-languages}
* [English](https://vuejs.org/) \[[source](https://github.com/vuejs/docs)]
* [简体中文 / Simplified Chinese](https://cn.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-zh-cn)]
* [日本語 / Japanese](https://ja.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-ja)]
* [Українська / Ukrainian](https://ua.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-uk)]
* [Français / French](https://fr.vuejs.org) \[[source](https://github.com/vuejs-translations/docs-fr)]
* [Deutsch / German](https://de.vuejs.org) \[[source](https://github.com/vuejs-translations/docs-de)]
* [한국어 / Korean](https://ko.vuejs.org) \[[source](https://github.com/vuejs-translations/docs-ko)]
* [Português / Portuguese](https://pt.vuejs.org) \[[source](https://github.com/vuejs-translations/docs-pt)]
* [বাংলা / Bengali](https://bn.vuejs.org) \[[source](https://github.com/vuejs-translations/docs-bn)]
* [Italiano / Italian](https://it.vuejs.org) \[[source](https://github.com/vuejs-translations/docs-it)]
* [فارسی / Persian](https://fa.vuejs.org) \[[source](https://github.com/vuejs-translations/docs-fa)]
* [Русский / Russian](https://ru.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-ru)]
* [Čeština / Czech](https://cs.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-cs)]
* [繁體中文 / Traditional Chinese](https://zh-hk.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-zh-hk)]
* [Polski / Polish](https://pl.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-pl)]
## Work in Progress Languages {#work-in-progress-languages}
* [العربية / Arabic](https://ar.vuejs.org/) \[[source](https://github.com/vuejs-translations/docs-ar)]
* [Español / Spanish](https://vue3-spanish-docs.netlify.app/) \[[source](https://github.com/icarusgk/vuejs-spanish-docs)]
## Contributing to Translations {#contributing-to-translations}
The Vue documentation has recently undergone a major revision, so translations in other languages are still missing or work-in-progress.
We welcome community efforts to provide more translations. Translation efforts are managed in the [vuejs-translations](https://github.com/vuejs-translations/) GitHub organization. If you are interested in contributing, please check out the [Translation Guidelines](https://github.com/vuejs-translations/guidelines/blob/main/README.md) to get started.
---
---
url: /tutorial.md
---