Professional Documents
Culture Documents
Vue Collate 100121
Vue Collate 100121
Vue Collate 100121
NOTE
Already know Vue 2 and just want to learn about what's new in Vue 3? Check out the Migration Guide!
What is Vue.js?
Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is
designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up
and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-
Page Applications when used in combination with modern tooling and supporting libraries .
If you’d like to learn more about Vue before diving in, we created a video walking through the core principles and a sample project.
Getting Started
Installation
TIP
The official guide assumes intermediate level knowledge of HTML, CSS, and JavaScript. If you are totally new to frontend
development, it might not be the best idea to jump right into a framework as your first step - grasp the basics then come back!
Prior experience with other frameworks helps, but is not required.
The easiest way to try out Vue.js is using the Hello World example . Feel free to open it in another tab and follow along as we go
through some basic examples.
The Installation page provides more options of installing Vue. Note: We do notrecommend that beginners start with vue-cli , especially
if you are not yet familiar with Node.js-based build tools.
Declarative Rendering
At the core of Vue.js is a system that enables us to declaratively render data to the DOM using straightforward template syntax:
1 <div id="counter">
2 Counter: {{ counter }}
3 </div>
1 const Counter = {
2 data() {
3 return {
4 counter: 0
5 }
6 }
7 }
8
9 Vue.createApp(Counter).mount('#counter')
We have already created our very first Vue app! This looks pretty similar to rendering a string template, but Vue has done a lot of work
under the hood. The data and the DOM are now linked, and everything is now reactive. How do we know? Take a look at the example
below where counter property increments every second and you will see how rendered DOM changes:
1 const CounterApp = {
2 data() {
3 return {
4 counter: 0
5 }
6 },
7 mounted() {
8 setInterval(() => {
9 this.counter++
10 }, 1000)
11 }
12 }
Counter: 185
Stop timer
In addition to text interpolation, we can also bind element attributes like this:
1 <div id="bind-attribute">
2 <span v-bind:title="message">
3 Hover your mouse over me for a few seconds to see my dynamically bound
4 title!
5 </span>
6 </div>
1 const AttributeBinding = {
2 data() {
3 return {
4 message: 'You loaded this page on ' + new Date().toLocaleString()
5 }
6 }
7 }
8
9 Vue.createApp(AttributeBinding).mount('#bind-attribute')
Here we're encountering something new. The v-bind attribute you're seeing is called a directive. Directives are prefixed with v- to
indicate that they are special attributes provided by Vue, and as you may have guessed, they apply special reactive behavior to the
rendered DOM. Here we are basically saying "keep this element's title attribute up-to-date with the message property on the current
active instance."
1 <div id="event-handling">
2 <p>{{ message }}</p>
3 <button v-on:click="reverseMessage">Reverse Message</button>
4 </div>
1 const EventHandling = {
2 data() {
3 return {
4 message: 'Hello Vue.js!'
5 }
6 },
7 methods: {
8 reverseMessage() {
9 this.message = this.message
10 .split('')
11 .reverse()
12 .join('')
13 }
14 }
15 }
16
17 Vue.createApp(EventHandling).mount('#event-handling')
Note that in this method we update the state of our app without touching the DOM - all DOM manipulations are handled by Vue, and the
code you write is focused on the underlying logic.
Vue also provides the v-model directive that makes two-way binding between form input and app state a breeze:
1 <div id="two-way-binding">
2 <p>{{ message }}</p>
3 <input v-model="message" />
4 </div>
1 const TwoWayBinding = {
2 data() {
3 return {
4 message: 'Hello Vue!'
5 }
6 }
7 }
8
9 Vue.createApp(TwoWayBinding).mount('#two-way-binding')
1 <div id="conditional-rendering">
2 <span v-if="seen">Now you see me</span>
3 </div>
1 const ConditionalRendering = {
2 data() {
3 return {
4 seen: true
5 }
6 }
7 }
8
9 Vue.createApp(ConditionalRendering).mount('#conditional-rendering')
This example demonstrates that we can bind data to not only text and attributes, but also the structure of the DOM. Moreover, Vue also
provides a powerful transition effect system that can automatically apply transition effects when elements are inserted/updated/removed
by Vue.
You can change seen from true to false in the sandbox below to check the effect:
There are quite a few other directives, each with its own special functionality. For example, the v-for directive can be used to display
a list of items using the data from an array:
1 <div id="list-rendering">
2 <ol>
3 <li v-for="todo in todos">
4 {{ todo.text }}
5 </li>
6 </ol>
7 </div>
1 const ListRendering = {
2 data() {
3 return {
4 todos: [
5 { text: 'Learn JavaScript' },
6 { text: 'Learn Vue' },
7 { text: 'Build something awesome' }
8 ]
9 }
10 }
11 }
12
13 Vue.createApp(ListRendering).mount('#list-rendering')
Composing with Components
The component system is another important concept in Vue, because it's an abstraction that allows us to build large-scale applications
composed of small, self-contained, and often reusable components. If we think about it, almost any type of application interface can be
abstracted into a tree of components:
In Vue, a component is essentially an instance with pre-defined options. Registering a component in Vue is straightforward: we create a
component object as we did with App objects and we define it in parent's components option:
1 // Create Vue application
2 const app = Vue.createApp(...)
3
4 // Define a new component called todo-item
5 app.component('todo-item', {
6 template: `<li>This is a todo</li>`
7 })
8
9 // Mount Vue application
10 app.mount(...)
1 <ol>
2 <!-- Create an instance of the todo-item component -->
3 <todo-item></todo-item>
4 </ol>
But this would render the same text for every todo, which is not super interesting. We should be able to pass data from the parent scope
into child components. Let's modify the component definition to make it accept a prop:
1 app.component('todo-item', {
2 props: ['todo'],
3 template: `<li>{{ todo.text }}</li>`
4 })
Now we can pass the todo into each repeated component using v-bind :
1 <div id="todo-list-app">
2 <ol>
3 <!--
4 Now we provide each todo-item with the todo object
5 it's representing, so that its content can be dynamic.
6 We also need to provide each component with a "key",
7 which will be explained later.
8 -->
9 <todo-item
10 v-for="item in groceryList"
11 v-bind:todo="item"
12 v-bind:key="item.id"
13 ></todo-item>
14 </ol>
15 </div>
1 const TodoList = {
2 data() {
3 return {
4 groceryList: [
5 { id: 0, text: 'Vegetables' },
6 { id: 1, text: 'Cheese' },
7 { id: 2, text: 'Whatever else humans are supposed to eat' }
8 ]
9 }
10 }
11 }
12
13 const app = Vue.createApp(TodoList)
14
15 app.component('todo-item', {
16 props: ['todo'],
17 template: `<li>{{ todo.text }}</li>`
18 })
19
20 app.mount('#todo-list-app')
This is a contrived example, but we have managed to separate our app into two smaller units, and the child is reasonably well-
decoupled from the parent via the props interface. We can now further improve our <todo-item> component with more complex
template and logic without affecting the parent app.
In a large application, it is necessary to divide the whole app into components to make development manageable. We will talk a lot more
about components later in the guide, but here's an (imaginary) example of what an app's template might look like with components:
1 <div id="app">
2 <app-nav></app-nav>
3 <app-view>
4 <app-sidebar></app-sidebar>
5 <app-content></app-content>
6 </app-view>
7 </div>
1. The Web Components Spec has been finalized but is not natively implemented in every browser. Safari 10.1+, Chrome 54+ and
Firefox 63+ natively support web components. In comparison, Vue components work consistently in all supported browsers (IE11 with
compatibility build and above). When needed, Vue components can also be wrapped inside a native custom element.
2. Vue components provide important features that are not available in plain custom elements, most notably cross-component data flow,
custom event communication and build tool integrations.
Although Vue doesn't use custom elements internally, it has great interoperability when it comes to consuming or distributing as
custom elements. Vue CLI also supports building Vue components that register themselves as native custom elements.
Under the hood, Vue compiles the templates into Virtual DOM render functions. Combined with the reactivity system, Vue is able to
intelligently figure out the minimal number of components to re-render and apply the minimal amount of DOM manipulations when the
app state changes.
If you are familiar with Virtual DOM concepts and prefer the raw power of JavaScript, you can also directly write render functions instead
of templates, with optional JSX support.
Interpolations
Text
The most basic form of data binding is text interpolation using the "Mustache" syntax (double curly braces):
The mustache tag will be replaced with the value of the msg property from the corresponding component instance. It will also be
updated whenever the msg property changes.
You can also perform one-time interpolations that do not update on data change by using the v-once directive, but keep in mind this will
also affect any other bindings on the same node:
Raw HTML
The double mustaches interprets the data as plain text, not HTML. In order to output real HTML, you will need to use the v-
html directive:
The contents of the span will be replaced with the value of the rawHtml property, interpreted as plain HTML - data bindings are
ignored. Note that you cannot use v-html to compose template partials, because Vue is not a string-based templating engine. Instead,
components are preferred as the fundamental unit for UI reuse and composition.
TIP
Dynamically rendering arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities
. Only use HTML interpolation on trusted content and never on user-provided content
Attributes
Mustaches cannot be used inside HTML attributes. Instead, use a v-bind directive:
1 <div v-bind:id="dynamicId"></div>
If the bound value is null or undefined then the attribute will not be included on the rendered element.
In the case of boolean attributes, where their mere existence implies true , v-bind works a little differently. For example:
1 <button v-bind:disabled="isButtonDisabled">Button</button>
The disabled attribute will be included if isButtonDisabled has a truthy value. It will also be included if the value is an empty string,
maintaining consistency with <button disabled=""> . For other falsy values the attribute will be omitted.
1 {{ number + 1 }}
2
3 {{ ok ? 'YES' : 'NO' }}
4
5 {{ message.split('').reverse().join('') }}
6
7 <div v-bind:id="'list-' + id"></div>
These expressions will be evaluated as JavaScript in the data scope of the current active instance. One restriction is that each binding
can only contain one single expression, so the following will NOT work:
1 <!-- this is a statement, not an expression: -->
2 {{ var a = 1 }}
3
4 <!-- flow control won't work either, use ternary expressions -->
5 {{ if (ok) { return message } }}
Directives
Directives are special attributes with the v- prefix. Directive attribute values are expected to be a single JavaScript expression (with
the exception of v-for and v-on , which will be discussed later). A directive's job is to reactively apply side effects to the DOM when
the value of its expression changes. Let's review the example we saw in the introduction:
Here, the v-if directive would remove/insert the <p> element based on the truthiness of the value of the expression seen .
Arguments
Some directives can take an "argument", denoted by a colon after the directive name. For example, the v-bind directive is used to
reactively update an HTML attribute:
Here href is the argument, which tells the v-bind directive to bind the element's href attribute to the value of the expression url .
Here the argument is the event name to listen to. We will talk about event handling in more detail too.
Dynamic Arguments
It is also possible to use a JavaScript expression in a directive argument by wrapping it with square brackets:
1 <!--
2 Note that there are some constraints to the argument expression, as explained
3 in the "Dynamic Argument Expression Constraints" section below.
4 -->
5 <a v-bind:[attributeName]="url"> ... </a>
Here attributeName will be dynamically evaluated as a JavaScript expression, and its evaluated value will be used as the final value
for the argument. For example, if your component instance has a data property, attributeName , whose value is "href" , then this
binding will be equivalent to v-bind:href .
Similarly, you can use dynamic arguments to bind a handler to a dynamic event name:
In this example, when eventName 's value is "focus" , v-on:[eventName] will be equivalent to v-on:focus .
Modifiers
Modifiers are special postfixes denoted by a dot, which indicate that a directive should be bound in some special way. For example,
the .prevent modifier tells the v-on directive to call event.preventDefault() on the triggered event:
1 <form v-on:submit.prevent="onSubmit">...</form>
You'll see other examples of modifiers later, for v-on and for v-model , when we explore those features.
Shorthands
The v- prefix serves as a visual cue for identifying Vue-specific attributes in your templates. This is useful when you are using Vue.js
to apply dynamic behavior to some existing markup, but can feel verbose for some frequently used directives. At the same time, the
need for the v- prefix becomes less important when you are building a SPA , where Vue manages every template. Therefore, Vue
provides special shorthands for two of the most often used directives, v-bind and v-on :
v-bind Shorthand
v-on Shorthand
1 <!-- full syntax -->
2 <a v-on:click="doSomething"> ... </a>
3
4 <!-- shorthand -->
5 <a @click="doSomething"> ... </a>
6
7 <!-- shorthand with dynamic argument -->
8 <a @[event]="doSomething"> ... </a>
They may look a bit different from normal HTML, but : and @ are valid characters for attribute names and all Vue-supported
browsers can parse it correctly. In addition, they do not appear in the final rendered markup. The shorthand syntax is totally optional, but
you will likely appreciate it when you learn more about its usage later.
From the next page on, we'll use the shorthand in our examples, as that's the most common usage for Vue developers.
Caveats
Dynamic arguments are expected to evaluate to a string, with the exception of null . The special value null can be used to explicitly
remove the binding. Any other non-string value will trigger a warning.
Dynamic argument expressions have some syntax constraints because certain characters, such as spaces and quotes, are invalid
inside HTML attribute names. For example, the following is invalid:
When using in-DOM templates (templates directly written in an HTML file), you should also avoid naming keys with uppercase
characters, as browsers will coerce attribute names into lowercase:
1 <!--
2 This will be converted to v-bind:[someattr] in in-DOM templates.
3 Unless you have a "someattr" property in your instance, your code won't work.
4 -->
5 <a v-bind:[someAttr]="value"> ... </a>
JavaScript Expressions
Template expressions are sandboxed and only have access to a whitelist of globals such as Math and Date . You should not
attempt to access user defined globals in template expressions.
Data Properties and Methods
Data Properties
The data option for a component is a function. Vue calls this function as part of creating a new component instance. It should return
an object, which Vue will then wrap in its reactivity system and store on the component instance as $data . For convenience, any top-
level properties of that object are also exposed directly via the component instance:
It is possible to add a new property directly to the component instance without including it in data . However, because this property isn't
backed by the reactive $data object, it won't automatically be tracked by Vue's reactivity system.
Vue uses a $ prefix when exposing its own built-in APIs via the component instance. It also reserves the prefix _ for internal
properties. You should avoid using names for top-level data properties that start with either of these characters.
Methods
To add methods to a component instance we use the methods option. This should be an object containing the desired methods:
Vue automatically binds the this value for methods so that it always refers to the component instance. This ensures that a method
retains the correct this value if it's used as an event listener or callback. You should avoid using arrow functions when
defining methods , as that prevents Vue from binding the appropriate this value.
Just like all other properties of the component instance, the methods are accessible from within the component's template. Inside a
template they are most commonly used as event listeners:
In the example above, the method increment will be called when the <button> is clicked.
It is also possible to call a method directly from a template. As we'll see shortly, it's usually better to use a computed property instead.
However, using a method can be useful in scenarios where computed properties aren't a viable option. You can call a method anywhere
that a template supports JavaScript expressions:
1 <span :title="toTitleDate(date)">
2 {{ formatDate(date) }}
3 </span>
If the methods toTitleDate or formatDate access any reactive data then it will be tracked as a rendering dependency, just as if it had
been used in the template directly.
Methods called from a template should not have any side effects, such as changing data or triggering asynchronous processes. If you
find yourself tempted to do that you should probably use a lifecycle hook instead.
Debouncing and Throttling
Vue doesn't include built-in support for debouncing or throttling but it can be implemented using libraries such as Lodash .
In cases where a component is only used once, the debouncing can be applied directly within methods :
1 <script src="https://unpkg.com/lodash@4.17.20/lodash.min.js"></script>
2 <script>
3 Vue.createApp({
4 methods: {
5 // Debouncing with Lodash
6 click: _.debounce(function() {
7 // ... respond to click ...
8 }, 500)
9 }
10 }).mount('#app')
11 </script>
However, this approach is potentially problematic for components that are reused because they'll all share the same debounced
function. To keep the component instances independent from each other, we can add the debounced function in the created lifecycle
hook:
app.component('save-button', {
created() {
// Debouncing with Lodash
this.debouncedClick = _.debounce(this.click, 500)
},
unmounted() {
// Cancel the timer when the component is removed
this.debouncedClick.cancel()
},
methods: {
click() {
// ... respond to click ...
}
},
template: `
<button @click="debouncedClick">
Save
</button>
`
})
Component Registration
This page assumes you've already read the Components Basics. Read that first if you are new to components.
Component Names
When registering a component, it will always be given a name. For example, in the global registration we've seen so far:
The component's name is the first argument of app.component . In the example above, the component's name is "my-component-
name".
The name you give a component may depend on where you intend to use it. When using a component directly in the DOM (as opposed
to in a string template or single-file component), we strongly recommend following the W3C rules for custom tag names:
1. All lowercase
2. Contains a hyphen (i.e., has multiple words connected with the hyphen symbol)
By doing so, this will help you avoid conflicts with current and future HTML elements.
You can see other recommendations for component names in the Style Guide.
Name Casing
When defining components in a string template or a single-file component, you have two options when defining component names:
With kebab-case
1 app.component('my-component-name', {
2 /* ... */
3 })
When defining a component with kebab-case, you must also use kebab-case when referencing its custom element, such as in <my-
component-name> .
With PascalCase
1 app.component('MyComponentName', {
2 /* ... */
3 })
When defining a component with PascalCase, you can use either case when referencing its custom element. That means both <my-
component-name> and <MyComponentName> are acceptable. Note, however, that only kebab-case names are valid directly in the DOM
(i.e. non-string templates).
Global Registration
So far, we've only created components using app.component :
1 Vue.createApp({...}).component('my-component-name', {
2 // ... options ...
3 })
These components are globally registered for the application. That means they can be used in the template of any component instance
within this application:
1 <div id="app">
2 <component-a></component-a>
3 <component-b></component-b>
4 <component-c></component-c>
5 </div>
This even applies to all subcomponents, meaning all three of these components will also be available inside each other.
Local Registration
Global registration often isn't ideal. For example, if you're using a build system like Webpack, globally registering all components means
that even if you stop using a component, it could still be included in your final build. This unnecessarily increases the amount of
JavaScript your users have to download.
In these cases, you can define your components as plain JavaScript objects:
1 const ComponentA = {
2 /* ... */
3 }
4 const ComponentB = {
5 /* ... */
6 }
7 const ComponentC = {
8 /* ... */
9 }
Note that locally registered components are not also available in subcomponents. For example, if you wanted ComponentA to be
available in ComponentB , you'd have to use:
1 const ComponentA = {
2 /* ... */
3 }
4
5 const ComponentB = {
6 components: {
7 'component-a': ComponentA
8 }
9 // ...
10 }
Or if you're using ES2015 modules, such as through Babel and Webpack, that might look more like:
Note that in ES2015+, placing a variable name like ComponentA inside an object is shorthand for ComponentA: ComponentA , meaning
the name of the variable is both:
Module Systems
If you're not using a module system with import / require , you can probably skip this section for now. If you are, we have some
special instructions and tips just for you.
Then you'll need to import each component you'd like to use, before you locally register it. For example, in a
hypothetical ComponentB.js or ComponentB.vue file:
Now both ComponentA and ComponentC can be used inside ComponentB 's template.
Props
This page assumes you've already read the Components Basics. Read that first if you are new to components.
Prop Types
So far, we've only seen props listed as an array of strings:
Usually though, you'll want every prop to be a specific type of value. In these cases, you can list props as an object, where the
properties' names and values contain the prop names and types, respectively:
1 props: {
2 title: String,
3 likes: Number,
4 isPublished: Boolean,
5 commentIds: Array,
6 author: Object,
7 callback: Function,
8 contactsPromise: Promise // or any other constructor
9 }
This not only documents your component, but will also warn users in the browser's JavaScript console if they pass the wrong type. You'll
learn much more about type checks and other prop validations further down this page.
Passing Static or Dynamic Props
So far, you've seen props passed a static value, like in:
You've also seen props assigned dynamically with v-bind or its shortcut, the : character, such as in:
In the two examples above, we happen to pass string values, but any type of value can actually be passed to a prop.
Passing a Number
1 <!-- Even though `42` is static, we need v-bind to tell Vue that -->
2 <!-- this is a JavaScript expression rather than a string. -->
3 <blog-post :likes="42"></blog-post>
4
5 <!-- Dynamically assign to the value of a variable. -->
6 <blog-post :likes="post.likes"></blog-post>
Passing a Boolean
1 <!-- Including the prop with no value will imply `true`. -->
2 <blog-post is-published></blog-post>
3
4 <!-- Even though `false` is static, we need v-bind to tell Vue that -->
5 <!-- this is a JavaScript expression rather than a string. -->
6 <blog-post :is-published="false"></blog-post>
7
8 <!-- Dynamically assign to the value of a variable. -->
9 <blog-post :is-published="post.isPublished"></blog-post>
Passing an Array
1 <!-- Even though the array is static, we need v-bind to tell Vue that -->
2 <!-- this is a JavaScript expression rather than a string. -->
3 <blog-post :comment-ids="[234, 266, 273]"></blog-post>
4
5 <!-- Dynamically assign to the value of a variable. -->
6 <blog-post :comment-ids="post.commentIds"></blog-post>
Passing an Object
1 <!-- Even though the object is static, we need v-bind to tell Vue that -->
2 <!-- this is a JavaScript expression rather than a string. -->
3 <blog-post
4 :author="{
5 name: 'Veronica',
6 company: 'Veridian Dynamics'
7 }"
8 ></blog-post>
9
10 <!-- Dynamically assign to the value of a variable. -->
11 <blog-post :author="post.author"></blog-post>
1 post: {
2 id: 1,
3 title: 'My Journey with Vue'
4 }
1 <blog-post v-bind="post"></blog-post>
In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value. This
means you should not attempt to mutate a prop inside a child component. If you do, Vue will warn you in the console.
There are usually two cases where it's tempting to mutate a prop:
1. The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards. In this case, it's
best to define a local data property that uses the prop as its initial value:
1 props: ['initialCounter'],
2 data() {
3 return {
4 counter: this.initialCounter
5 }
6 }
2. The prop is passed in as a raw value that needs to be transformed. In this case, it's best to define a computed property using the
prop's value:
1 props: ['size'],
2 computed: {
3 normalizedSize: function () {
4 return this.size.trim().toLowerCase()
5 }
6 }
Note
Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or
array itself inside the child component will affect parent state.
Prop Validation
Components can specify requirements for their props, such as the types you've already seen. If a requirement isn't met, Vue will warn
you in the browser's JavaScript console. This is especially useful when developing a component that's intended to be used by others.
To specify prop validations, you can provide an object with validation requirements to the value of props , instead of an array of strings.
For example:
1 app.component('my-component', {
2 props: {
3 // Basic type check (`null` and `undefined` values will pass any type validation)
4 propA: Number,
5 // Multiple possible types
6 propB: [String, Number],
7 // Required string
8 propC: {
9 type: String,
10 required: true
11 },
12 // Number with a default value
13 propD: {
14 type: Number,
15 default: 100
16 },
17 // Object with a default value
18 propE: {
19 type: Object,
20 // Object or array defaults must be returned from
21 // a factory function
22 default: function() {
23 return { message: 'hello' }
24 }
25 },
26 // Custom validator function
27 propF: {
28 validator: function(value) {
29 // The value must match one of these strings
30 return ['success', 'warning', 'danger'].indexOf(value) !== -1
31 }
32 },
33 // Function with a default value
34 propG: {
35 type: Function,
36 // Unlike object or array default, this is not a factory function - this is a function to serve as a default value
37 default: function() {
38 return 'Default function'
39 }
40 }
41 }
42 })
When prop validation fails, Vue will produce a console warning (if using the development build).
Note
Note that props are validated before a component instance is created, so instance properties (e.g. data , computed , etc) will
not be available inside default or validator functions.
Type Checks
The type can be one of the following native constructors:
String
Number
Boolean
Array
Object
Date
Function
Symbol
In addition, type can also be a custom constructor function and the assertion will be made with an instanceof check. For example,
given the following constructor function exists:
1 app.component('blog-post', {
2 props: {
3 author: Person
4 }
5 })
to validate that the value of the author prop was created with new Person .
Again, if you're using string templates, this limitation does not apply.
Non-Prop Attributes
This page assumes you've already read the Components Basics. Read that first if you are new to components.
A component non-prop attribute is an attribute or event listener that is passed to a component, but does not have a corresponding
property defined in props or emits. Common examples of this include class , style , and id attributes. You can access those
attributes via $attrs property.
Attribute Inheritance
When a component returns a single root node, non-prop attributes will automatically be added to the root node's attributes. For example,
in the instance of a date-picker component:
1 app.component('date-picker', {
2 template: `
3 <div class="date-picker">
4 <input type="datetime" />
5 </div>
6 `
7 })
In the event we need to define the status of the date-picker component via a data-status property, it will be applied to the root node
(i.e., div.date-picker ).
1 <!-- Date-picker component with a non-prop attribute -->
2 <date-picker data-status="activated"></date-picker>
3
4 <!-- Rendered date-picker component -->
5 <div class="date-picker" data-status="activated">
6 <input type="datetime" />
7 </div>
1 <date-picker @change="submitChange"></date-picker>
1 app.component('date-picker', {
2 created() {
3 console.log(this.$attrs) // { onChange: () => {} }
4 }
5 })
This might be helpful when we have an HTML element with change event as a root element of date-picker .
1 app.component('date-picker', {
2 template: `
3 <select>
4 <option value="1">Yesterday</option>
5 <option value="2">Today</option>
6 <option value="3">Tomorrow</option>
7 </select>
8 `
9 })
In this case, change event listener is passed from the parent component to the child and it will be triggered on
native <select> change event. We won't need to emit an event from the date-picker explicitly:
The common scenario for disabling an attribute inheritance is when attributes need to be applied to other elements besides the root
node.
By setting the inheritAttrs option to false , you can control to apply to other elements attributes to use the
component's $attrs property, which includes all attributes not included to component props and emits properties
(e.g., class , style , v-on listeners, etc.).
Using our date-picker component example from the previous section, in the event we need to apply all non-prop attributes to
the input element rather than the root div element, this can be accomplished by using the v-bind shortcut.
1 app.component('date-picker', {
2 inheritAttrs: false,
3 template: `
4 <div class="date-picker">
5 <input type="datetime" v-bind="$attrs" />
6 </div>
7 `
8 })
With this new configuration, our data-status attribute will be applied to our input element!
Event Names
Like components and props, event names provide an automatic case transformation. If you emit an
event from the child component in camel case, you will be able to add a kebab-cased listener in the
parent:
1 this.$emit('myEvent')
1 <my-component @my-event="doSomething"></my-component>
As with props casing, we recommend using kebab-cased event listeners when you are using in-DOM
templates. If you're using string templates, this limitation does not apply.
Emitted events can be defined on the component via the emits option.
1 app.component('custom-form', {
2 emits: ['inFocus', 'submit']
3 })
When a native event (e.g., click ) is defined in the emits option, the component event will be
used instead of a native event listener.
TIP
It is recommended to define all emitted events in order to better document how a component
should work.
To add validation, the event is assigned a function that receives the arguments passed to
the $emit call and returns a boolean to indicate whether the event is valid or not.
1 app.component('custom-form', {
2 emits: {
3 // No validation
4 click: null,
5
6 // Validate submit event
7 submit: ({ email, password }) => {
8 if (email && password) {
9 return true
10 } else {
11 console.warn('Invalid submit event payload!')
12 return false
13 }
14 }
15 },
16 methods: {
17 submitForm() {
18 this.$emit('submit', { email, password })
19 }
20 }
21 })
v-model arguments
By default, v-model on a component uses modelValue as the prop and update:modelValue as the
event. We can modify these names passing an argument to v-model :
1 <my-component v-model:title="bookTitle"></my-component>
In this case, child component will expect a title prop and emits update:title event to sync:
1 app.component('my-component', {
2 props: {
3 title: String
4 },
5 emits: ['update:title'],
6 template: `
7 <input
8 type="text"
9 :value="title"
10 @input="$emit('update:title', $event.target.value)">
11 `
12 })
1 <my-component v-model:title="bookTitle"></my-component>
Each v-model will sync to a different prop, without the need for extra options in the component:
1 <user-name
2 v-model:first-name="firstName"
3 v-model:last-name="lastName"
4 ></user-name>
1 app.component('user-name', {
2 props: {
3 firstName: String,
4 lastName: String
5 },
6 emits: ['update:firstName', 'update:lastName'],
7 template: `
8 <input
9 type="text"
10 :value="firstName"
11 @input="$emit('update:firstName', $event.target.value)">
12
13 <input
14 type="text"
15 :value="lastName"
16 @input="$emit('update:lastName', $event.target.value)">
17 `
18 })
Let's create an example custom modifier, capitalize , that capitalizes the first letter of the string
provided by the v-model binding.
Notice that when the component's created lifecycle hook triggers, the modelModifiers prop
contains capitalize and its value is true - due to it being set on the v-model binding v-
model.capitalize="myText" .
1 <my-component v-model.capitalize="myText"></my-component>
1 app.component('my-component', {
2 props: {
3 modelValue: String,
4 modelModifiers: {
5 default: () => ({})
6 }
7 },
8 emits: ['update:modelValue'],
9 template: `
10 <input type="text"
11 :value="modelValue"
12 @input="$emit('update:modelValue', $event.target.value)">
13 `,
14 created() {
15 console.log(this.modelModifiers) // { capitalize: true }
16 }
17 })
Now that we have our prop set up, we can check the modelModifiers object keys and write a handler to
change the emitted value. In the code below we will capitalize the string whenever the <input
/> element fires an input event.
1 <div id="app">
2 <my-component v-model.capitalize="myText"></my-component>
3 {{ myText }}
4 </div>
For v-model bindings with arguments, the generated prop name will be arg + "Modifiers" :
1 <my-component v-model:description.capitalize="myText"></my-component>
1 app.component('my-component', {
2 props: ['description', 'descriptionModifiers'],
3 emits: ['update:description'],
4 template: `
5 <input type="text"
6 :value="description"
7 @input="$emit('update:description', $event.target.value)">
8 `,
9 created() {
10 console.log(this.descriptionModifiers) // { capitalize: true }
11 }
12 })
Slots
This page assumes you've already read the Components Basics. Read that first if you are new to components.
Slot Content
Vue implements a content distribution API inspired by the Web Components spec draft , using the <slot> element to serve as
distribution outlets for content.
1 <todo-button>
2 Add todo
3 </todo-button>
Strings are just the beginning though! Slots can also contain any template code, including HTML:
1 <todo-button>
2 <!-- Add a Font Awesome icon -->
3 <i class="fas fa-plus"></i>
4 Add todo
5 </todo-button>
1 <todo-button>
2 <!-- Use a component to add an icon -->
3 <font-awesome-icon name="plus"></font-awesome-icon>
4 Add todo
5 </todo-button>
If <todo-button> 's template did not contain a <slot> element, any content provided between its opening and closing tag would be
discarded.
Render Scope
When you want to use data inside a slot, such as in:
1 <todo-button>
2 Delete a {{ item.name }}
3 </todo-button>
That slot has access to the same instance properties (i.e. the same "scope") as the rest of the template.
The slot does not have access to <todo-button> 's scope. For example, trying to access action would not work:
1 <todo-button action="delete">
2 Clicking here will {{ action }} an item
3 <!--
4 The `action` will be undefined, because this content is passed
5 _to_ <todo-button>, rather than defined _inside_ the
6 <todo-button> component.
7 -->
8 </todo-button>
As a rule, remember that:
Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope.
Fallback Content
There are cases when it's useful to specify fallback (i.e. default) content for a slot, to be rendered only when no content is provided. For
example, in a <submit-button> component:
1 <button type="submit">
2 <slot></slot>
3 </button>
We might want the text "Submit" to be rendered inside the <button> most of the time. To make "Submit" the fallback content, we can
place it in between the <slot> tags:
1 <button type="submit">
2 <slot>Submit</slot>
3 </button>
Now when we use <submit-button> in a parent component, providing no content for the slot:
1 <submit-button></submit-button>
1 <submit-button>
2 Save
3 </submit-button>
1 <button type="submit">
2 Save
3 </button>
Named Slots
There are times when it's useful to have multiple slots. For example, in a <base-layout> component with the following template:
1 <div class="container">
2 <header>
3 <!-- We want header content here -->
4 </header>
5 <main>
6 <!-- We want main content here -->
7 </main>
8 <footer>
9 <!-- We want footer content here -->
10 </footer>
11 </div>
For these cases, the <slot> element has a special attribute, name , which can be used to assign a unique ID to different slots so you
can determine where content should be rendered:
1 <div class="container">
2 <header>
3 <slot name="header"></slot>
4 </header>
5 <main>
6 <slot></slot>
7 </main>
8 <footer>
9 <slot name="footer"></slot>
10 </footer>
11 </div>
To provide content to named slots, we need to use the v-slot directive on a <template> element, providing the name of the slot
as v-slot 's argument:
1 <base-layout>
2 <template v-slot:header>
3 <h1>Here might be a page title</h1>
4 </template>
5
6 <template v-slot:default>
7 <p>A paragraph for the main content.</p>
8 <p>And another one.</p>
9 </template>
10
11 <template v-slot:footer>
12 <p>Here's some contact info</p>
13 </template>
14 </base-layout>
Now everything inside the <template> elements will be passed to the corresponding slots.
1 <div class="container">
2 <header>
3 <h1>Here might be a page title</h1>
4 </header>
5 <main>
6 <p>A paragraph for the main content.</p>
7 <p>And another one.</p>
8 </main>
9 <footer>
10 <p>Here's some contact info</p>
11 </footer>
12 </div>
Note that v-slot can only be added to a <template> (with one exception)
Scoped Slots
Sometimes, it's useful for slot content to have access to data only available in the child component. It's a common case when a
component is used to render an array of items, and we want to be able to customize the way each item is rendered.
We might want to replace the {{ item }} with a <slot> to customize it on parent component:
1 <todo-list>
2 <i class="fas fa-check"></i>
3 <span class="green">{{ item }}</span>
4 </todo-list>
That won't work, however, because only the <todo-list> component has access to the item and we are providing the slot content
from its parent.
To make item available to the slot content provided by the parent, we can add a <slot> element and bind it as an attribute:
1 <ul>
2 <li v-for="( item, index ) in items">
3 <slot :item="item"></slot>
4 </li>
5 </ul>
You can bind as many attributes to the slot as you need:
1 <ul>
2 <li v-for="( item, index ) in items">
3 <slot :item="item" :index="index" :another-attribute="anotherAttribute"></slot>
4 </li>
5 </ul>
Attributes bound to a <slot> element are called slot props. Now, in the parent scope, we can use v-slot with a value to define a
name for the slot props we've been provided:
1 <todo-list>
2 <template v-slot:default="slotProps">
3 <i class="fas fa-check"></i>
4 <span class="green">{{ slotProps.item }}</span>
5 </template>
6 </todo-list>
In this example, we've chosen to name the object containing all our slot props slotProps , but you can use any name you like.
1 <todo-list v-slot:default="slotProps">
2 <i class="fas fa-check"></i>
3 <span class="green">{{ slotProps.item }}</span>
4 </todo-list>
This can be shortened even further. Just as non-specified content is assumed to be for the default slot, v-slot without an argument is
assumed to refer to the default slot:
1 <todo-list v-slot="slotProps">
2 <i class="fas fa-check"></i>
3 <span class="green">{{ slotProps.item }}</span>
4 </todo-list>
Note that the abbreviated syntax for default slot cannot be mixed with named slots, as it would lead to scope ambiguity:
Whenever there are multiple slots, use the full <template> based syntax for all slots:
1 <todo-list>
2 <template v-slot:default="slotProps">
3 <i class="fas fa-check"></i>
4 <span class="green">{{ slotProps.item }}</span>
5 </template>
6
7 <template v-slot:other="otherSlotProps">
8 ...
9 </template>
10 </todo-list>
1 function (slotProps) {
2 // ... slot content ...
3 }
That means the value of v-slot can actually accept any valid JavaScript expression that can appear in the argument position of a
function definition. So you can also use ES2015 destructuring to pull out specific slot props, like so:
This can make the template much cleaner, especially when the slot provides many props. It also opens other possibilities, such as
renaming props, e.g. item to todo :
1 <todo-list v-slot="{ item: todo }">
2 <i class="fas fa-check"></i>
3 <span class="green">{{ todo }}</span>
4 </todo-list>
You can even define fallbacks, to be used in case a slot prop is undefined:
1 <base-layout>
2 <template v-slot:[dynamicSlotName]>
3 ...
4 </template>
5 </base-layout>
However, just as with other directives, the shorthand is only available when an argument is provided. That means the following syntax is
invalid:
Instead, you must always specify the name of the slot if you wish to use the shorthand:
1 <component :is="currentTabComponent"></component>
When switching between these components though, you'll sometimes want to maintain their state or avoid re-rendering for performance
reasons. For example, when expanding our tabbed interface a little:
You'll notice that if you select a post, switch to the Archive tab, then switch back to Posts, it's no longer showing the post you selected.
That's because each time you switch to a new tab, Vue creates a new instance of the currentTabComponent .
Recreating dynamic components is normally useful behavior, but in this case, we'd really like those tab component instances to be
cached once they're created for the first time. To solve this problem, we can wrap our dynamic component with a <keep-
alive> element:
Now the Posts tab maintains its state (the selected post) even when it's not rendered.
Async Components
In large applications, we may need to divide the app into smaller chunks and only load a component from the server when it's needed.
To make that possible, Vue has a defineAsyncComponent method:
As you can see, this method accepts a factory function returning a Promise . Promise's resolve callback should be called when you
have retrieved your component definition from the server. You can also call reject(reason) to indicate the load has failed.
You can also return a Promise in the factory function, so with Webpack 2 or later and ES2015 syntax you can do:
The async component can opt-out of Suspense control and let the component always control its own loading state by
specifying suspensible: false in its options.
You can check the list of available options in the API Reference
Handling Edge Cases
This page assumes you've already read the Components Basics. Read that first if you are new to components.
Note
All the features on this page document the handling of edge cases, meaning unusual situations that sometimes require bending
Vue's rules a little. Note however, that they all have disadvantages or situations where they could be dangerous. These are
noted in each case, so keep them in mind when deciding to use each feature.
Controlling Updates
Thanks to Vue's Reactivity system, it always knows when to update (if you use it correctly). There are edge cases, however, when you
might want to force an update, despite the fact that no reactive data has changed. Then there are other cases when you might want to
prevent unnecessary updates.
Forcing an Update
If you find yourself needing to force an update in Vue, in 99.99% of cases, you've made a mistake somewhere. For example, you may
be relying on state that isn't tracked by Vue's reactivity system, e.g. with data property added after component creation.
However, if you've ruled out the above and find yourself in this extremely rare situation of having to manually force an update, you can
do so with $forceUpdate .
Cheap Static Components with v-once
Rendering plain HTML elements is very fast in Vue, but sometimes you might have a component that contains a lot of static content. In
these cases, you can ensure that it's only evaluated once and then cached by adding the v-once directive to the root element, like this:
1 app.component('terms-of-service', {
2 template: `
3 <div v-once>
4 <h1>Terms of Service</h1>
5 ... a lot of static content ...
6 </div>
7 `
8 })
TIP
Once again, try not to overuse this pattern. While convenient in those rare cases when you have to render a lot of static content,
it's simply not necessary unless you actually notice slow rendering - plus, it could cause a lot of confusion later. For example,
imagine another developer who's not familiar with v-once or simply misses it in the template. They might spend hours trying to
figure out why the template isn't updating correctly.
Overview
Vue offers some abstractions that can help work with transitions and animations, particularly in response to something changing. Some
of these abstractions include:
Hooks for components entering and leaving the DOM, in both CSS and JS, using the built-in <transition> component.
Transition Modes so that you can orchestrate ordering during a transition.
Hooks for when multiple elements are updating in position, with FLIP techniques applied under the hood to increase performance,
using the <transition-group> component.
Transitioning different states in an application, with watchers .
We will cover all of these and more in the next three sections in this Guide. However, aside from these useful API offerings, it's worth
mentioning that the class and style declarations we covered earlier can be used to apply animations and transitions as well, for more
simple use cases.
In this next section, we'll go over some web animation and transitions basics, and link off to some resources for further exploration. If
you're already familiar with web animation and how those principles might work with some of Vue's directives, feel free to skip this next
section. For anyone else looking to learn a little more about web animation basics before diving in, read on.
1 .shake {
2 animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
3 transform: translate3d(0, 0, 0);
4 backface-visibility: hidden;
5 perspective: 1000px;
6 }
7
8 @keyframes shake {
9 10%,
10 90% {
11 transform: translate3d(-1px, 0, 0);
12 }
13
14 20%,
15 80% {
16 transform: translate3d(2px, 0, 0);
17 }
18
19 30%,
20 50%,
21 70% {
22 transform: translate3d(-4px, 0, 0);
23 }
24
25 40%,
26 60% {
27 transform: translate3d(4px, 0, 0);
28 }
29 }
1 const Demo = {
2 data() {
3 return {
4 noActivated: false
5 }
6 }
7 }
8
9 Vue.createApp(Demo).mount('#demo')
1 <div id="demo">
2 <div
3 @mousemove="xCoordinate"
4 :style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }"
5 class="movearea"
6 >
7 <h3>Move your mouse across the screen...</h3>
8 <p>x: {{x}}</p>
9 </div>
10 </div>
1 .movearea {
2 transition: 0.2s background-color ease;
3 }
1 const Demo = {
2 data() {
3 return {
4 x: 0
5 }
6 },
7 methods: {
8 xCoordinate(e) {
9 this.x = e.clientX
10 }
11 }
12 }
13
14 Vue.createApp(Demo).mount('#demo')
In this example, we are creating animation through the use of interpolation, attached to the mouse movement. The CSS transition is
applied to the element as well, to let the element know what kind of easing to use while it's updating.
Performance
You may notice that the animations shown above are using things like transforms , and applying strange properties like perspective -
why were they built that way instead of just using margin and top etc?
We can create extremely smooth animations on the web by being aware of performance. We want to hardware accelerate elements
when we can, and use properties that don't trigger repaints. Let's go over some of how we can accomplish this.
Transform and Opacity
We can check resources like CSS-Triggers to see which properties will trigger repaints if we animate them. Here, if you look
under transform , you will see:
Changing transform does not trigger any geometry changes or painting, which is very good. This means that the operation can likely
be carried out by the compositor thread with the help of the GPU.
Opacity behaves similarly. Thus, they are ideal candidates for movement on the web.
Hardware Acceleration
Properties such as perspective , backface-visibility , and transform: translateZ(x) will allow the browser to know you need
hardware acceleration.
If you wish to hardware-accelerate an element, you can apply any of these properties (not all are necessary, only one):
1 perspective: 1000px;
2 backface-visibility: hidden;
3 transform: translateZ(0);
Many JS libraries like GreenSock will assume you want hardware acceleration and will apply them by default, so you do not need to set
them manually.
Timing
For simple UI transitions, meaning from just one state to another with no intermediary states, it's common to use timings between 0.1s
and 0.4s, and most folks find that 0.25s tends to be a sweet spot. Can you use that timing for everything? No, not really. If you have
something that needs to move a greater distance or has more steps or state changes, 0.25s is not going to work as well and you will
have to be much more intentional, and the timing will need to be more unique. That doesn't mean you can't have nice defaults that you
repeat within your application, though.
You may also find that entrances look better with slightly more time than an exit. The user typically is being guided during the entrance,
and is a little less patient upon exit because they want to go on their way.
Easing
Easing is an important way to convey depth in an animation. One of the most common mistakes newcomers to animation make is to
use ease-in for entrances, and ease-out for exits. You'll actually need the opposite.
If we were to apply these states to a transition, it would look something like this:
1 .button {
2 background: #1b8f5a;
3 /* applied to the initial state, so this transition will be applied to the return state */
4 transition: background 0.25s ease-in;
5 }
6
7 .button:hover {
8 background: #3eaf7c;
9 /* applied to the hover state, so this transition will be applied when a hover is triggered */
10 transition: background 0.35s ease-out;
11 }
Easing can also convey the quality of material being animated. Take this pen for example, which ball do you think is hard and which is
soft?
You can get a lot of unique effects and make your animation very stylish by adjusting your easing. CSS allows you to modify this by
adjusting a cubic bezier property, this playground by Lea Verou is very helpful for exploring this.
Though you can achieve great effects for simple animation with the two handles the cubic-bezier ease offers, JavaScript allows multiple
handles, and therefore, allows for much more variance.
Value
Value
Progress Progress
Here is the code used for a bounce in CSS (example from animate.css):
1 @keyframes bounceInDown {
2 from,
3 60%,
4 75%,
5 90%,
6 to {
7 animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
8 }
9
10 0% {
11 opacity: 0;
12 transform: translate3d(0, -3000px, 0) scaleY(3);
13 }
14
15 60% {
16 opacity: 1;
17 transform: translate3d(0, 25px, 0) scaleY(0.9);
18 }
19
20 75% {
21 transform: translate3d(0, -10px, 0) scaleY(0.95);
22 }
23
24 90% {
25 transform: translate3d(0, 5px, 0) scaleY(0.985);
26 }
27
28 to {
29 transform: translate3d(0, 0, 0);
30 }
31 }
32
33 .bounceInDown {
34 animation-name: bounceInDown;
35 }
And here is the same bounce in JS using GreenSock:
We'll be using GreenSock in some of the examples in the sections following. They have a great ease visualizer that will help you build
nicely crafted eases.
# Further Reading
Designing Interface Animation: Improving the User Experience Through Animation by Val Head
Overview
Vue offers some abstractions that can help work with transitions and animations, particularly in response to something changing. Some
of these abstractions include:
Hooks for components entering and leaving the DOM, in both CSS and JS, using the built-in <transition> component.
Transition Modes so that you can orchestrate ordering during a transition.
Hooks for when multiple elements are updating in position, with FLIP techniques applied under the hood to increase performance,
using the <transition-group> component.
Transitioning different states in an application, with watchers .
We will cover all of these and more in the next three sections in this Guide. However, aside from these useful API offerings, it's worth
mentioning that the class and style declarations we covered earlier can be used to apply animations and transitions as well, for more
simple use cases.
In this next section, we'll go over some web animation and transitions basics, and link off to some resources for further exploration. If
you're already familiar with web animation and how those principles might work with some of Vue's directives, feel free to skip this next
section. For anyone else looking to learn a little more about web animation basics before diving in, read on.
1 .shake {
2 animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
3 transform: translate3d(0, 0, 0);
4 backface-visibility: hidden;
5 perspective: 1000px;
6 }
7
8 @keyframes shake {
9 10%,
10 90% {
11 transform: translate3d(-1px, 0, 0);
12 }
13
14 20%,
15 80% {
16 transform: translate3d(2px, 0, 0);
17 }
18
19 30%,
20 50%,
21 70% {
22 transform: translate3d(-4px, 0, 0);
23 }
24
25 40%,
26 60% {
27 transform: translate3d(4px, 0, 0);
28 }
29 }
1 const Demo = {
2 data() {
3 return {
4 noActivated: false
5 }
6 }
7 }
8
9 Vue.createApp(Demo).mount('#demo')
1 <div id="demo">
2 <div
3 @mousemove="xCoordinate"
4 :style="{ backgroundColor: `hsl(${x}, 80%, 50%)` }"
5 class="movearea"
6 >
7 <h3>Move your mouse across the screen...</h3>
8 <p>x: {{x}}</p>
9 </div>
10 </div>
1 .movearea {
2 transition: 0.2s background-color ease;
3 }
1 const Demo = {
2 data() {
3 return {
4 x: 0
5 }
6 },
7 methods: {
8 xCoordinate(e) {
9 this.x = e.clientX
10 }
11 }
12 }
13
14 Vue.createApp(Demo).mount('#demo')
In this example, we are creating animation through the use of interpolation, attached to the mouse movement. The CSS transition is
applied to the element as well, to let the element know what kind of easing to use while it's updating.
Performance
You may notice that the animations shown above are using things like transforms , and applying strange properties like perspective -
why were they built that way instead of just using margin and top etc?
We can create extremely smooth animations on the web by being aware of performance. We want to hardware accelerate elements
when we can, and use properties that don't trigger repaints. Let's go over some of how we can accomplish this.
Transform and Opacity
We can check resources like CSS-Triggers to see which properties will trigger repaints if we animate them. Here, if you look
under transform , you will see:
Changing transform does not trigger any geometry changes or painting, which is very good. This means that the operation can likely
be carried out by the compositor thread with the help of the GPU.
Opacity behaves similarly. Thus, they are ideal candidates for movement on the web.
Hardware Acceleration
Properties such as perspective , backface-visibility , and transform: translateZ(x) will allow the browser to know you need
hardware acceleration.
If you wish to hardware-accelerate an element, you can apply any of these properties (not all are necessary, only one):
1 perspective: 1000px;
2 backface-visibility: hidden;
3 transform: translateZ(0);
Many JS libraries like GreenSock will assume you want hardware acceleration and will apply them by default, so you do not need to set
them manually.
Timing
For simple UI transitions, meaning from just one state to another with no intermediary states, it's common to use timings between 0.1s
and 0.4s, and most folks find that 0.25s tends to be a sweet spot. Can you use that timing for everything? No, not really. If you have
something that needs to move a greater distance or has more steps or state changes, 0.25s is not going to work as well and you will
have to be much more intentional, and the timing will need to be more unique. That doesn't mean you can't have nice defaults that you
repeat within your application, though.
You may also find that entrances look better with slightly more time than an exit. The user typically is being guided during the entrance,
and is a little less patient upon exit because they want to go on their way.
Easing
Easing is an important way to convey depth in an animation. One of the most common mistakes newcomers to animation make is to
use ease-in for entrances, and ease-out for exits. You'll actually need the opposite.
If we were to apply these states to a transition, it would look something like this:
1 .button {
2 background: #1b8f5a;
3 /* applied to the initial state, so this transition will be applied to the return state */
4 transition: background 0.25s ease-in;
5 }
6
7 .button:hover {
8 background: #3eaf7c;
9 /* applied to the hover state, so this transition will be applied when a hover is triggered */
10 transition: background 0.35s ease-out;
11 }
Easing can also convey the quality of material being animated. Take this pen for example, which ball do you think is hard and which is
soft?
You can get a lot of unique effects and make your animation very stylish by adjusting your easing. CSS allows you to modify this by
adjusting a cubic bezier property, this playground by Lea Verou is very helpful for exploring this.
Though you can achieve great effects for simple animation with the two handles the cubic-bezier ease offers, JavaScript allows multiple
handles, and therefore, allows for much more variance.
Value
Value
Progress Progress
Here is the code used for a bounce in CSS (example from animate.css):
1 @keyframes bounceInDown {
2 from,
3 60%,
4 75%,
5 90%,
6 to {
7 animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
8 }
9
10 0% {
11 opacity: 0;
12 transform: translate3d(0, -3000px, 0) scaleY(3);
13 }
14
15 60% {
16 opacity: 1;
17 transform: translate3d(0, 25px, 0) scaleY(0.9);
18 }
19
20 75% {
21 transform: translate3d(0, -10px, 0) scaleY(0.95);
22 }
23
24 90% {
25 transform: translate3d(0, 5px, 0) scaleY(0.985);
26 }
27
28 to {
29 transform: translate3d(0, 0, 0);
30 }
31 }
32
33 .bounceInDown {
34 animation-name: bounceInDown;
35 }
And here is the same bounce in JS using GreenSock:
We'll be using GreenSock in some of the examples in the sections following. They have a great ease visualizer that will help you build
nicely crafted eases.
# Further Reading
Designing Interface Animation: Improving the User Experience Through Animation by Val Head
Animation at Work by Rachel Nabors
Introduction
Note
Reaching this far in the documentation, you should already be familiar with both the basics of Vue and creating components.
Creating Vue components allows us to extract repeatable parts of the interface coupled with its functionality into reusable pieces of
code. This alone can get our application pretty far in terms of maintainability and flexibility. However, our collective experience has
proved that this alone might not be enough, especially when your application is getting really big – think several hundred components.
When dealing with such large applications, sharing and reusing code becomes especially important.
Let’s imagine that in our app, we have a view to show a list of repositories of a certain user. On top of that, we want to apply search and
filter capabilities. Our component handling this view could look like this:
1 // src/components/UserRepositories.vue
2
3 export default {
4 components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
5 props: {
6 user: {
7 type: String,
8 required: true
9 }
10 },
11 data () {
12 return {
13 repositories: [], // 1
14 filters: { ... }, // 3
15 searchQuery: '' // 2
16 }
17 },
18 computed: {
19 filteredRepositories () { ... }, // 3
20 repositoriesMatchingSearchQuery () { ... }, // 2
21 },
22 watch: {
23 user: 'getUserRepositories' // 1
24 },
25 methods: {
26 getUserRepositories () {
27 // using `this.user` to fetch user repositories
28 }, // 1
29 updateFilters () { ... }, // 3
30 },
31 mounted () {
32 this.getUserRepositories() // 1
33 }
34 }
1. Getting repositories from a presumedly external API for that user name and refreshing it whenever the user changes
2. Searching for repositories using a searchQuery string
3. Filtering repositories using a filters object
Organizing logics with component's options ( data , computed , methods , watch ) works in most cases. However, when our
components get bigger, the list of logical concerns also grows. This can lead to components that are hard to read and understand,
especially for people who didn't write them in the first place.
Example presenting a large component where its logical concerns are grouped by colors.
Such fragmentation is what makes it difficult to understand and maintain a complex component. The separation of options obscures the
underlying logical concerns. In addition, when working on a single logical concern, we have to constantly "jump" around option blocks for
the relevant code.
It would be much nicer if we could collocate code related to the same logical concern. And this is exactly what the Composition API
enables us to do.
The new setup component option is executed before the component is created, once the props are resolved, and serves as the
entry point for composition API's.
WARNING
Because the component instance is not yet created when setup is executed, there is no this inside a setup option. This
means, with the exception of props , you won't be able to access any properties declared in the component – local
state, computed properties or methods.
The setup option should be a function that accepts props and context which we will talk about later. Additionally, everything that
we return from setup will be exposed to the rest of our component (computed properties, methods, lifecycle hooks and so on) as well
as to the component's template.
1 // src/components/UserRepositories.vue
2
3 export default {
4 components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
5 props: {
6 user: {
7 type: String,
8 required: true
9 }
10 },
11 setup(props) {
12 console.log(props) // { user: '' }
13
14 return {} // anything returned here will be available for the rest of the component
15 }
16 // the "rest" of the component
17 }
Now let’s start with extracting the first logical concern (marked as "1" in the original snippet).
1. Getting repositories from a presumedly external API for that user name and refreshing it whenever the user changes
This is our starting point, except it's not working yet because our repositories variable is not reactive. This means from a user's
perspective, the repository list would remain empty. Let's fix that!
ref takes the argument and returns it wrapped within an object with a value property, which can then be used to access or mutate
the value of the reactive variable:
Having a wrapper object around any value allows us to safely pass it across our whole app without worrying about losing its reactivity
somewhere along the way.
Note
In other words, ref creates a Reactive Reference to our value. The concept of working with References will be used often
throughout the Composition API.
Done! Now whenever we call getUserRepositories , repositories will be mutated and the view will be updated to reflect the change.
Our component should now look like this:
1 // src/components/UserRepositories.vue
2 import { fetchUserRepositories } from '@/api/repositories'
3 import { ref } from 'vue'
4
5 export default {
6 components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
7 props: {
8 user: {
9 type: String,
10 required: true
11 }
12 },
13 setup (props) {
14 const repositories = ref([])
15 const getUserRepositories = async () => {
16 repositories.value = await fetchUserRepositories(props.user)
17 }
18
19 return {
20 repositories,
21 getUserRepositories
22 }
23 },
24 data () {
25 return {
26 filters: { ... }, // 3
27 searchQuery: '' // 2
28 }
29 },
30 computed: {
31 filteredRepositories () { ... }, // 3
32 repositoriesMatchingSearchQuery () { ... }, // 2
33 },
34 watch: {
35 user: 'getUserRepositories' // 1
36 },
37 methods: {
38 updateFilters () { ... }, // 3
39 },
40 mounted () {
41 this.getUserRepositories() // 1
42 }
43 }
We have moved several pieces of our first logical concern into the setup method, nicely put close to each other. What’s left is
calling getUserRepositories in the mounted hook and setting up a watcher to do that whenever the user prop changes.
Now we need to react to the changes made to the user prop. For that we will use the standalone watch function.
Whenever counter is modified, for example counter.value = 5 , the watch will trigger and execute the callback (second argument)
which in this case will log 'The new counter value is: 5' into our console.
1 export default {
2 data() {
3 return {
4 counter: 0
5 }
6 },
7 watch: {
8 counter(newValue, oldValue) {
9 console.log('The new counter value is: ' + this.counter)
10 }
11 }
12 }
You probably have noticed the use of toRefs at the top of our setup . This is to ensure our watcher will react to changes made to
the user prop.
With those changes in place, we've just moved the whole first logical concern into a single place. We can now do the same with the
second concern – filtering based on searchQuery , this time with a computed property.
Here, the computed function returns a read-only Reactive Reference to the output of the getter-like callback passed as the first
argument to computed . In order to access the value of the newly-created computed variable, we need to use the .value property just
like with ref .
We could do the same for other logical concerns but you might be already asking the question – Isn’t this just moving the code to
the setup option and making it extremely big? Well, that’s true. That’s why before moving on with the other responsibilities, we will first
extract the above code into a standalone composition function. Let's start with creating useUserRepositories :
1 // src/composables/useUserRepositories.js
2
3 import { fetchUserRepositories } from '@/api/repositories'
4 import { ref, onMounted, watch } from 'vue'
5
6 export default function useUserRepositories(user) {
7 const repositories = ref([])
8 const getUserRepositories = async () => {
9 repositories.value = await fetchUserRepositories(user.value)
10 }
11
12 onMounted(getUserRepositories)
13 watch(user, getUserRepositories)
14
15 return {
16 repositories,
17 getUserRepositories
18 }
19 }
1 // src/composables/useRepositoryNameSearch.js
2
3 import { ref, computed } from 'vue'
4
5 export default function useRepositoryNameSearch(repositories) {
6 const searchQuery = ref('')
7 const repositoriesMatchingSearchQuery = computed(() => {
8 return repositories.value.filter(repository => {
9 return repository.name.includes(searchQuery.value)
10 })
11 })
12
13 return {
14 searchQuery,
15 repositoriesMatchingSearchQuery
16 }
17 }
Now having those two functionalities in separate files, we can start using them in our component. Here’s how this can be done:
1 // src/components/UserRepositories.vue
2 import useUserRepositories from '@/composables/useUserRepositories'
3 import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
4 import { toRefs } from 'vue'
5
6 export default {
7 components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
8 props: {
9 user: {
10 type: String,
11 required: true
12 }
13 },
14 setup (props) {
15 const { user } = toRefs(props)
16
17 const { repositories, getUserRepositories } = useUserRepositories(user)
18
19 const {
20 searchQuery,
21 repositoriesMatchingSearchQuery
22 } = useRepositoryNameSearch(repositories)
23
24 return {
25 // Since we don’t really care about the unfiltered repositories
26 // we can expose the filtered results under the `repositories` name
27 repositories: repositoriesMatchingSearchQuery,
28 getUserRepositories,
29 searchQuery,
30 }
31 },
32 data () {
33 return {
34 filters: { ... }, // 3
35 }
36 },
37 computed: {
38 filteredRepositories () { ... }, // 3
39 },
40 methods: {
41 updateFilters () { ... }, // 3
42 }
43 }
At this point you probably already know the drill, so let’s skip to the end and migrate the leftover filtering functionality. We don’t really
need to get into the implementation details as it’s not the point of this guide.
1 // src/components/UserRepositories.vue
2 import { toRefs } from 'vue'
3 import useUserRepositories from '@/composables/useUserRepositories'
4 import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
5 import useRepositoryFilters from '@/composables/useRepositoryFilters'
6
7 export default {
8 components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
9 props: {
10 user: {
11 type: String,
12 required: true
13 }
14 },
15 setup(props) {
16 const { user } = toRefs(props)
17
18 const { repositories, getUserRepositories } = useUserRepositories(user)
19
20 const {
21 searchQuery,
22 repositoriesMatchingSearchQuery
23 } = useRepositoryNameSearch(repositories)
24
25 const {
26 filters,
27 updateFilters,
28 filteredRepositories
29 } = useRepositoryFilters(repositoriesMatchingSearchQuery)
30
31 return {
32 // Since we don’t really care about the unfiltered repositories
33 // we can expose the end results under the `repositories` name
34 repositories: filteredRepositories,
35 getUserRepositories,
36 searchQuery,
37 filters,
38 updateFilters
39 }
40 }
41 }
Keep in mind that we've only scratched the surface of Composition API and what it allows us to do. To learn more about it, refer to the
in-depth guide.
Setup
This section uses single-file component syntax for code examples
This guide assumes that you have already read the Composition API Introductionand Reactivity Fundamentals. Read that first if you
are new to Composition API.
Arguments
When using the setup function, it will take two arguments:
1. props
2. context
Props
The first argument in the setup function is the props argument. Just as you would expect in a standard component, props inside of
a setup function are reactive and will be updated when new props are passed in.
1 // MyBook.vue
2
3 export default {
4 props: {
5 title: String
6 },
7 setup(props) {
8 console.log(props.title)
9 }
10 }
WARNING
However, because props are reactive, you cannot use ES6 destructuringbecause it will remove props reactivity.
If you need to destructure your props, you can do this by utilizing the toRefs inside of the setup function:
1 // MyBook.vue
2
3 import { toRefs } from 'vue'
4
5 setup(props) {
6 const { title } = toRefs(props)
7
8 console.log(title.value)
9 }
If title is an optional prop, it could be missing from props . In that case, toRefs won't create a ref for title . Instead you'd need to
use toRef :
1 // MyBook.vue
2
3 import { toRef } from 'vue'
4
5 setup(props) {
6 const title = toRef(props, 'title')
7
8 console.log(title.value)
9 }
Context
The second argument passed to the setup function is the context . The context is a normal JavaScript object that exposes three
component properties:
1 // MyBook.vue
2
3 export default {
4 setup(props, context) {
5 // Attributes (Non-reactive object)
6 console.log(context.attrs)
7
8 // Slots (Non-reactive object)
9 console.log(context.slots)
10
11 // Emit Events (Method)
12 console.log(context.emit)
13 }
14 }
The context object is a normal JavaScript object, i.e., it is not reactive, this means you can safely use ES6 destructuring on context .
1 // MyBook.vue
2 export default {
3 setup(props, { attrs, slots, emit }) {
4 ...
5 }
6 }
attrs and slots are stateful objects that are always updated when the component itself is updated. This means you should avoid
destructuring them and always reference properties as attrs.x or slots.x . Also note that
unlike props , attrs and slots are not reactive. If you intend to apply side effects based on attrs or slots changes, you should
do so inside an onUpdated lifecycle hook.
props
attrs
slots
emit
In other words, you will not have access to the following component options:
data
computed
methods
Note that refs returned from setup are automatically unwrapped when accessed in the template so you shouldn't use .value in
templates.
Usage of this
Inside setup() , this won't be a reference to the current active instance Since setup() is called before other component options
are resolved, this inside setup() will behave quite differently from this in other options. This might cause confusions when
using setup() along other Options API.
Lifecycle Hooks
This guide assumes that you have already read the Composition API Introductionand Reactivity
Fundamentals. Read that first if you are new to Composition API.
You can access a component's lifecycle hook by prefixing the lifecycle hook with "on".
The following table contains how the lifecycle hooks are invoked inside of setup():
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
Options API Hook inside setup
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered
TIP
Because setup is run around the beforeCreate and created lifecycle hooks, you do not
need to explicitly define them. In other words, any code that would be written inside those hooks
should be written directly in the setup function.
These functions accept a callback that will be executed when the hook is called by the component:
1 // MyBook.vue
2
3 export default {
4 setup() {
5 // mounted
6 onMounted(() => {
7 console.log('Component is mounted!')
8 })
9 }
10 }
De
# Mixins
Basics
Mixins distribute reusable functionalities for Vue components. A mixin object can contain any component
options. When a component uses a mixin, all options in the mixin will be "mixed" into the component's
own options.
Example:
Option Merging
When a mixin and the component itself contain overlapping options, they will be "merged" using
appropriate strategies.
For example, data objects undergo a recursive merge, with the component's data taking priority in cases
of conflicts.
1 const myMixin = {
2 data() {
3 return {
4 message: 'hello',
5 foo: 'abc'
6 }
7 }
8 }
9
10 const app = Vue.createApp({
11 mixins: [myMixin],
12 data() {
13 return {
14 message: 'goodbye',
15 bar: 'def'
16 }
17 },
18 created() {
19 console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" }
20 }
21 })
Hook functions with the same name are merged into an array so that all of them will be called. Mixin
hooks will be called before the component's own hooks.
1 const myMixin = {
2 created() {
3 console.log('mixin hook called')
4 }
5 }
6
7 const app = Vue.createApp({
8 mixins: [myMixin],
9 created() {
10 console.log('component hook called')
11 }
12 })
13
14 // => "mixin hook called"
15 // => "component hook called"
Options that expect object values, for example methods , components and directives , will be merged
into the same object. The component's options will take priority when there are conflicting keys in these
objects:
1 const myMixin = {
2 methods: {
3 foo() {
4 console.log('foo')
5 },
6 conflicting() {
7 console.log('from mixin')
8 }
9 }
10 }
11
12 const app = Vue.createApp({
13 mixins: [myMixin],
14 methods: {
15 bar() {
16 console.log('bar')
17 },
18 conflicting() {
19 console.log('from self')
20 }
21 }
22 })
23
24 const vm = app.mount('#mixins-basic')
25
26 vm.foo() // => "foo"
27 vm.bar() // => "bar"
28 vm.conflicting() // => "from self"
Global Mixin
You can also apply a mixin globally for a Vue application:
Use with caution! Once you apply a mixin globally, it will affect every component instance created
afterwards in the given app (for example, child components):
In most cases, you should only use it for custom option handling like demonstrated in the example
above. It's also a good idea to ship them as Plugins to avoid duplicate application.
Custom Option Merge Strategies
When custom options are merged, they use the default strategy which overwrites the existing value. If
you want a custom option to be merged using custom logic, you need to attach a function
to app.config.optionMergeStrategies :
The merge strategy receives the value of that option defined on the parent and child instances as the
first and second arguments, respectively. Let's try to check what do we have in these parameters when
we use a mixin:
As you can see, in the console we have toVal and fromVal printed first from the mixin and then from
the app . We always return fromVal if it exists, that's why this.$options.custom is set to hello! in
the end. Let's try to change a strategy to always return a value from the child instance:
Precautions
In Vue 2, mixins were the primary tool to abstract parts of component logic into reusable chunks.
However, they have a few issues:
Mixins are conflict-prone: Since properties from each feature are merged into the same component,
you still have to know about every other feature to avoid property name conflicts and for debugging.
Reusability is limited: we cannot pass any parameters to the mixin to change its logic which reduces
their flexibility in terms of abstracting logic
To address these issues, we added a new way to organize code by logical concerns: the Composition
API.
Custom Directives
Intro
In addition to the default set of directives shipped in core (like v-model or v-show ), Vue also allows you to register your own custom
directives. Note that in Vue, the primary form of code reuse and abstraction is components - however, there may be cases where you
need some low-level DOM access on plain elements, and this is where custom directives would still be useful. An example would be
focusing on an input element, like this one:
When the page loads, that element gains focus (note: autofocus doesn't work on mobile Safari). In fact, if you haven't clicked on
anything else since visiting this page, the input above should be focused now. Also, you can click on the Rerun button and input will be
focused.
If you want to register a directive locally instead, components also accept a directives option:
1 directives: {
2 focus: {
3 // directive definition
4 mounted(el) {
5 el.focus()
6 }
7 }
8 }
Then in a template, you can use the new v-focus attribute on any element, like this:
Hook Functions
A directive definition object can provide several hook functions (all optional):
created : called before the bound element's attributes or event listeners are applied. This is useful in cases where the directive
needs to attach event listeners that must be called before normal v-on event listeners.
beforeMount : called when the directive is first bound to the element and before parent component is mounted.
Note
We'll cover VNodes in more detail later, when we discuss render functions.
updated : called after the containing component's VNode and the VNodes of its children have updated.
unmounted : called only once, when the directive is unbound from the element and the parent component is unmounted.
You can check the arguments passed into these hooks (i.e. el , binding , vnode , and prevVnode ) in Custom Directive API
Let's say you want to make a custom directive that allows you to pin elements to your page using fixed positioning. We could create a
custom directive where the value updates the vertical positioning in pixels, like this:
This would pin the element 200px from the top of the page. But what happens if we run into a scenario when we need to pin the element
from the left, instead of the top? Here's where a dynamic argument that can be updated per component instance comes in very handy:
1 <div id="dynamicexample">
2 <h3>Scroll down inside this section ↓</h3>
3 <p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
4 </div>
Result:
Our custom directive is now flexible enough to support a few different use cases. To make it even more dynamic, we can also allow to
modify a bound value. Let's create an additional property pinPadding and bind it to the <input type="range">
1 <div id="dynamicexample">
2 <h2>Scroll down the page</h2>
3 <input type="range" min="0" max="500" v-model="pinPadding">
4 <p v-pin:[direction]="pinPadding">Stick me {{ pinPadding + 'px' }} from the {{ direction }} of the page</p>
5 </div>
Now let's extend our directive logic to recalculate the distance to pin on component update:
1 app.directive('pin', {
2 mounted(el, binding) {
3 el.style.position = 'fixed'
4 const s = binding.arg || 'top'
5 el.style[s] = binding.value + 'px'
6 },
7 updated(el, binding) {
8 const s = binding.arg || 'top'
9 el.style[s] = binding.value + 'px'
10 }
11 })
Result:
Function Shorthand
In previous example, you may want the same behavior on mounted and updated , but don't care about the other hooks. You can do it
by passing the callback to directive:
Object Literals
If your directive needs multiple values, you can also pass in a JavaScript object literal. Remember, directives can take any valid
JavaScript expression.
1 <my-component v-demo="test"></my-component>
1 app.component('my-component', {
2 template: `
3 <div> // v-demo directive will be applied here
4 <span>My component content</span>
5 </div>
6 `
7 })
With fragments support, components can potentially have more than one root nodes. When applied to a multi-root component, directive
will be ignored and the warning will be thrown.
Teleport
Learn how to use teleport with a free lesson on Vue School
Vue encourages us to build our UIs by encapsulating UI and related behavior into components. We can nest them inside one another to
build a tree that makes up an application UI.
However, sometimes a part of a component's template belongs to this component logically, while from a technical point of view, it would
be preferable to move this part of the template somewhere else in the DOM, outside of the Vue app.
A common scenario for this is creating a component that includes a full-screen modal. In most cases, you'd want the modal's logic to live
within the component, but the positioning of the modal quickly becomes difficult to solve through CSS, or requires a change in
component composition.
1 <body>
2 <div style="position: relative;">
3 <h3>Tooltips with Vue 3 Teleport</h3>
4 <div>
5 <modal-button></modal-button>
6 </div>
7 </div>
8 </body>
When using this component inside the initial HTML structure, we can see a problem - the modal is being rendered inside the deeply
nested div and the position: absolute of the modal takes the parent relatively positioned div as reference.
Teleport provides a clean way to allow us to control under which parent in our DOM we want a piece of HTML to be rendered, without
having to resort to global state or splitting this into two components.
Let's modify our modal-button to use <teleport> and tell Vue "teleport this HTML tothe "body" tag".
1 app.component('modal-button', {
2 template: `
3 <button @click="modalOpen = true">
4 Open full screen modal! (With teleport!)
5 </button>
6
7 <teleport to="body">
8 <div v-if="modalOpen" class="modal">
9 <div>
10 I'm a teleported modal!
11 (My parent is "body")
12 <button @click="modalOpen = false">
13 Close
14 </button>
15 </div>
16 </div>
17 </teleport>
18 `,
19 data() {
20 return {
21 modalOpen: false
22 }
23 }
24 })
As a result, once we click the button to open the modal, Vue will correctly render the modal's content as a child of the body tag.
In this case, even when child-component is rendered in the different place, it will remain a child of parent-component and will receive
a name prop from it.
This also means that injections from a parent component work as expected, and that the child component will be nested below the
parent component in the Vue Devtools, instead of being placed where the actual content moved to.
Official Router
For most Single Page Applications, it's recommended to use the officially-supported vue-router library . For more details, see vue-
router's documentation .
Combined with the History API , you can build a very basic but fully-functional client-side router. To see that in practice, check out this
example app .
If there's a 3rd-party router you prefer to use, such as Page.js or Director , integration is similarly straightforward . Here's
a complete example using Page.js.
State Management
Now whenever sourceOfTruth is mutated, both appA and appB will update their views automatically. We have a single source of
truth now, but debugging would be a nightmare. Any piece of data could be changed by any part of our app at any time, without leaving
a trace.
Notice all actions that mutate the store's state are put inside the store itself. This type of centralized state management makes it easier
to understand what type of mutations could happen and how they are triggered. Now when something goes wrong, we'll also have a log
of what happened leading up to the bug.
In addition, each instance/component can still own and manage its own private state:
1 <div id="app-a">{{sharedState.message}}</div>
2
3 <div id="app-b">{{sharedState.message}}</div>
1 const appA = Vue.createApp({
2 data() {
3 return {
4 privateState: {},
5 sharedState: store.state
6 }
7 },
8 mounted() {
9 store.setMessageAction('Goodbye!')
10 }
11 }).mount('#app-a')
12
13 const appB = Vue.createApp({
14 data() {
15 return {
16 privateState: {},
17 sharedState: store.state
18 }
19 }
20 }).mount('#app-b')
TIP
You should never replace the original state object in your actions - the components and the store need to share reference to the
same object in order for mutations to be observed.
As we continue developing the convention, where components are never allowed to directly mutate state that belongs to a store but
should instead dispatch events that notify the store to perform actions, we eventually arrive at the Flux architecture. The benefit of this
convention is we can record all state mutations happening to the store and implement advanced debugging helpers such as mutation
logs, snapshots, and history re-rolls / time travel.
This brings us full circle back to Vuex , so if you've read this far it's probably time to try it out!
Server-Side Rendering
Nuxt.js
Properly configuring all the discussed aspects of a production-ready server-rendered app can be a daunting task. Luckily, there is an
excellent community project that aims to make all of this easier: Nuxt.js . Nuxt.js is a higher-level framework built on top of the Vue
ecosystem which provides an extremely streamlined development experience for writing universal Vue applications. Better yet, you can
even use it as a static site generator (with pages authored as single-file Vue components)! We highly recommend giving it a try.
We have already discussed about how we can make HTTP requests using Axios to get data from the server
using axios.get() function and post data to the server using axios.post() function in an earlier guide.
In this guide, we will take a deeper route and learn more about other cool features Axios provides.
jsx
1 class User extends Component {
2 constructor(props) {
3 super(props);
4 this.state = {
5 user: {
6 data: {},
7 permissions: {}
8 }
9 };
10 }
11
12 getUserData = async () => {
13 try {
14 const {data} = await axios.get(`${ROOT_URL}/profile/${this.props.activeUserId}
15 return data;
16 } catch (err) {
17 console.log(err.message);
18 }
19 }
20
21 getPermissions = async () => {
22 try {
23 const {data} = await axios.get(`${ROOT_URL}/permissions/${this.props.activeUse
24 return data;
25 } catch (err) {
26 console.log(err.message);
27 }
28 }
29
30 async componentDidMount() {
31 const userData = await this.getUserData();
32 const userPermissions = await this.getPermissions();
33 this.setState(
34 user: {
35 data: userData,
36 permissions: userPermissions
37 }
38 );
39 }
40
41 render() {
42 // render the data
43 }
44
45 }
Instead of making the requests twice, we can call the endpoints simultaneously using
the axios.all() function.
jsx
1 class User extends Component {
2 // ...
3
4 getUserData = () => axios.get(`${ROOT_URL}/profile/${this.props.activeUserId}`);
5
6 getPermissions = () => axios.get(`${ROOT_URL}/permissions/${this.props.activeUserId}`)
7
8 async componentDidMount() {
9 try {
10 const [userData, userPermissions] = await axios.all([ this.getUserData(), this
11 this.setState(
12 user: {
13 data: userData.data,
14 permissions: userPermissions.data
15 }
16 );
17 }
18 catch (err) {
19 console.log(err.message);
20 }
21
22 }
23
24 // ...
25 }
Note that I have used async-await syntax to make the code more readable, instead of handling the
promises with .then() and .catch() . Having said that, it's really up to you which syntax you prefer, both
do the same job of handling promises.
axios.all() accepts an array of Axios requests, and returns an array that contains the responses from
each Axios request. The problem here is that even if one of the requests fails, by default, all the other requests
fail as well and the error from the first failed request will be logged in the catch() block.
To get around this problem, we can simply return null from the catch() block of each Axios request. So,
even if one of the requests fail, it will still consider the axios.all() promise to be resolved. The return value
from the failed request will be null , hence we need to have additional checks for if the data in the returned
array is null or not.
jsx
1 class User extends Component {
2 // ...
3
4 getUserData = () => axios.get(`${ROOT_URL}/profile/${this.props.activeUserId}`).catch(
5
6 getPermissions = () => axios.get(`${ROOT_URL}/permissions/${this.props.activeUserId}`)
7
8 async componentDidMount() {
9 try {
10 const [userData, userPermissions] = await axios.all([ this.getUserData(), this
11 this.setState(
12 user: {
13 data: userData && userData.data,
14 permissions: userPermissions && userPermissions.data
15 }
16 );
17 }
18 catch (err) {
19 console.log(err.message);
20 }
21 }
22
23 // ...
24 }
jsx
1 class User extends Component {
2 constructor(props) {
3 super(props);
4 this.state = {
5 user: {
6 data: {},
7 permissions: {}
8 }
9 };
10 }
11
12 async componentDidMount() {
13 const URLs = [ `${ROOT_URL}/profile/${this.props.activeUserId}`, `${ROOT_URL}/perm
14
15 const requests = URLs.map(URL => axios.get(URL).catch(err => null));
16
17 try {
18 const [userData, userPermissions] = await axios.all(requests);
19 this.setState(
20 user: {
21 data: userData && userData.data,
22 permissions: userPermissions && userPermissions.data
23 }
24 );
25 }
26 catch (err) {
27 console.log(err.message);
28 }
29 }
30
31 render() {
32 // render the data
33 }
34
35 }
jsx
1 class Posts extends Component {
2 constructor(props) {
3 super(props);
4 this.state = { posts: [] }
5 }
6
7 async componentDidMount() {
8 try {
9 const {data} = await axios.get(`${ROOT_URL}/posts`);
10 this.setState({
11 posts: data
12 })
13 } catch (err) {
14 console.log(err.message)
15 }
16 }
17
18 render() {
19 return (
20 <div className="container">
21 { this.state.posts && this.state.posts.length !== 0 ?
22 this.state.posts.map(post => <Card title={post.title}>{post.content}</Card
23 <Loading/> }
24 </div>
25 );
26 }
27 }
The Array.map() function iterates over each element of the array and returns a new array; in our case, the
array contains JSX elements, the <Card /> component. Note that here, I'm using the shorthand version, a
more complete implementation is as follows:
jsx
1 // ...
2 render() {
3 return (
4 <div className="container">
5 { this.state.posts && this.state.posts.length !== 0 ?
6 this.state.posts.map((post, index) => {
7 const { title, content } = post;
8 return <Card title={title}>{content}</Card>;
9 }) :
10 <Loading/> }
11 </div>
12 );
13 }
14 //..
For each element in the array, the map() function provides us with an anonymous function that accepts two
arguments, the item itself and an optional index argument which contains the position value of the current item.
We can also do some transformation of data before returning it with JSX. For example we can transform the title
to uppercase, as follows:
jsx
1 // ...
2 this.state.posts.map((post, index) => {
3 const { title, content } = post;
4 const transformedTitle = title.toUpperCase();
5 return <Card title={transformedTitle}>{content}</Card>;
6 })
7 //..
For conditional rendering in JSX, you cannot use an if-else block. Hence, we have used the ternary
operators. Also notice that, when we try to run the above code in the browser, we get a warning message in the
console which says: Each child in an array or iterator should have a unique "key" prop.
The key prop is used by React to keep track of items in an array. In the above
example, this.state.posts.map() will result in an array, hence each JSX element must have a key
prop associated with it. Not including a key prop will lead to unexpected results and bugs.
In our case, if we do not specify the key prop in our <Card /> component, React wouldn't know how to
keep track of the posts and, hence, when the state changes it would re-render the whole array again instead of
updating the changes. This is undesirable because it will affect the performance of the application. Therefore,
it's important to have at least one unique key value (in our case, post id) for array item.
jsx
1 //...
2 this.state.posts.map((post, index) => {
3 const { id, title, content } = post;
4 return <Card key={id} title={title}>{content}</Card>;
5 })
6 //...
jsx
1 const NavBar = props => (
2 <Nav>
3 <NavItem onClick={() => props.navigate('/home') }> Home </NavItem>
4 <NavItem onClick={() => props.navigate('/about') }> About </NavItem>
5 <NavItem onClick={() => props.navigate('/contact') }> Contact </NavItem>
6 </Nav>
7 )
8
9 class Posts extends Component {
10 constructor(props) {
11 super(props);
12 this.state = { posts: [] }
13 }
14
15 navigate = url => {
16 // cancel the request
17 this.source.cancel('User navigated to different page');
18
19 // assuming we are using React-Router
20 this.props.history.push(url);
21 }
22
23 async componentDidMount() {
24 const CancelToken = axios.CancelToken;
25 // create the source
26 this.source = CancelToken.source();
27 try {
28 const {data} = await axios.get(`${ROOT_URL}/posts`, {
29 cancelToken: this.source.token
30 });
31 this.setState({
32 posts: data
33 })
34 } catch (err) {
35 // check if the request was cancelled
36 if(axios.isCancel(err)) {
37 console.log(err.message);
38 }
39 console.log(err.message)
40 }
41 }
42
43 render() {
44 return (
45 <div className="container">
46 <NavBar navigate={this.navigate}/>
47 { this.state.posts && this.state.posts.length !== 0 ?
48 this.state.posts.map(post => <Card key={post.id} title={post.title}>{post.
49 <Loading/> }
50 </div>
51 );
52 }
53 }
The CancelToken.source factory provides us with two main requirements for cancelling the Axios request,
the cancel token and the cancel() function. We need to pass the cancel token as a config to
the axios.get() function and call the cancel() function whenever we need to cancel the previous Axios
request. We can use the same cancel token for multiple Axios requests.
We can also create a cancel token by passing an executor() function to the CancelToken constructor.
jsx
1 navigate = url => {
2 this.cancelRequest && this.cancelRequest('User navigated to different page');
3
4 // assuming we are using React-Router
5 this.props.history.push(url);
6 }
7
8 async componentDidMount() {
9 const CancelToken = axios.CancelToken;
10 try {
11 const {data} = await axios.get(`${ROOT_URL}/posts`, {
12 cancelToken: new CancelToken(function executor(c) {
13 this.cancelRequest = c;
14 })
15 });
16 this.setState({
17 posts: data
18 })
19 } catch (err) {
20 if(axios.isCancel(err)) {
21 console.log(err.message);
22 }
23 console.log(err.message);
24 }
25 }
Let’s check out another use case for cancelling the request. Consider the scenario where we have a search
component that retrieves the results from the search API. As the user types in the input field, we need to cancel
the previous axios request.
jsx
1 class Search extends Component {
2 constructor(props) {
3 super(props);
4 this.state = { value: null, results: [] };
5 }
6
7 search = async () => {
8 const CancelToken = axios.CancelToken;
9 try {
10 const {data} = await axios.get(`${ROOT_URL}/search/q=${this.state.value}`, {
11 cancelToken: new CancelToken(function executor(c) {
12 this.cancelRequest = c;
13 });
14 });
15 } catch (err) {
16 if(axios.isCancel(thrown)) {
17 console.log(thrown.message);
18 }
19 console.log(err.message)
20 }
21 }
22
23 handleChange = e => {
24 this.cancelRequest && this.cancelRequest();
25 if(e.target.value !== "") {
26 this.setState({ value: e.target.value }, async () => await this.search());
27 }
28 }
29
30 render() {
31 return <input type="text" onChange={this.handleChange} value={this.state.value}/>
32 }
33 }
Here, we are cancelling the request when the value of the input field changes. An important point to note in the
above code is that we are passing a callback as a second parameter to the setState() function. This is
because the state does not change immediately and, hence, to avoid any unexpected outcomes, we are calling
the search() function in the callback instead of calling it directly inside the handleChange() method. In
a more real-world use case, the search input would be debounced but, for brevity purposes, I'm not including it
here.
Conclusion
In this guide, we looked up various important use cases of Axios and how it's really a great library for making
HTTP requests in React (or any other JavaScript Framework like Vue). I hope you had enough of Axios today,
and do checkout the Axios documentation that will be linked in the references.
Please follow my other React guides to learn more. If you have any queries, feel free to ask at Codealphabet
axios
code e pe s
npm v0.21.1
cdnjs v0.21.1
build failing
coverage 94%
install size 388 kB
downloads 56M/month
chat on gitter
code helpers 141
Table of Contents
Features
Browser Support
Installing
Example
Axios API
Request method aliases
Concurrency (Deprecated)
Creating an instance
Instance methods
Request Config
Response Schema
Config Defaults
Global axios defaults
Custom instance defaults
Config order of precedence
Interceptors
Handling Errors
Cancellation
Using application/x-www-form-urlencoded format
Browser
Node.js
Query string
Form data
Semver
Promises
TypeScript
Resources
Credits
License
Features
Make XMLHttpRequests from the browser
Make http requests from node.js
Supports the Promise API
Intercept request and response
Transform request and response data
Cancel requests
Automatic transforms for JSON data
Client side support for protecting against XSRF
Browser Support
10 10.12
11 10.13
TESTING P O WERED BY
Installing
Using npm:
Using bower:
Using yarn:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
Example
// Want to use async/await? Add the `async` keyword to your outer function/method.
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}
NOTE: async/await is part of ECMAScript 2017 and is not supported in Internet Explorer and older browsers, so use with caution.
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Performing multiple concurrent requests
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
Promise.all([getUserAccount(), getUserPermissions()])
.then(function (results) {
const acct = results[0];
const perm = results[1];
});
axios API
Requests can be made by passing the relevant config to axios .
axios(config)
axios(url[, config])
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
NOTE
When using the alias methods url , method , and data properties don't need to be specified in config.
Concurrency (Deprecated)
Please use Promise.all to replace the below functions.
axios.all(iterable) axios.spread(callback)
Creating an instance
You can create a new instance of axios with a custom config.
axios.create([config])
Instance methods
The available instance methods are listed below. The specified config will be merged with the instance config.
axios#request(config)
axios#get(url[, config])
axios#delete(url[, config])
axios#head(url[, config])
axios#options(url[, config])
axios#getUri([config])
Request Config
These are the available config options for making requests. Only the url is required. Requests will default to GET if method is not
specified.
{
// `url` is the server URL that will be used for the request
url: '/user',
// `transformRequest` allows changes to the request data before it is sent to the server
// This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'
// The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
// FormData or Stream
// You may modify the headers object.
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data
return data;
}],
return data;
}],
// `timeout` specifies the number of milliseconds before the request times out.
// If the request takes longer than `timeout`, the request will be aborted.
timeout: 1000, // default is `0` (no timeout)
// `auth` indicates that HTTP Basic auth should be used, and supplies credentials.
// This will set an `Authorization` header, overwriting any existing
// `Authorization` custom headers you have set using `headers`.
// Please note that only HTTP Basic auth is configurable through this parameter.
// For Bearer tokens and such, use `Authorization` custom headers instead.
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
responseType: 'json', // default
// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `maxContentLength` defines the max size of the http response content in bytes allowed in node.js
maxContentLength: 2000,
// `maxBodyLength` (Node only option) defines the max size of the http request content in bytes allowed
maxBodyLength: 2000,
// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
// and https requests, respectively, in node.js. This allows options to be added like
// `keepAlive` that are not enabled by default.
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// `proxy` defines the hostname, port, and protocol of the proxy server.
// You can also define your proxy using the conventional `http_proxy` and
// `https_proxy` environment variables. If you are using environment variables
// for your proxy configuration, you can also define a `no_proxy` environment
// variable as a comma-separated list of domains that should not be proxied.
// Use `false` to disable proxies, ignoring environment variables.
// `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and
// supplies credentials.
// This will set an `Proxy-Authorization` header, overwriting any existing
// `Proxy-Authorization` custom headers you have set using `headers`.
// If the proxy server uses HTTPS, then you must set the protocol to `https`.
proxy: {
protocol: 'https',
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// `cancelToken` specifies a cancel token that can be used to cancel the request
// (see Cancellation section below for details)
cancelToken: new CancelToken(function (cancel) {
}),
Response Schema
The response for a request contains the following information.
{
// `data` is the response that was provided by the server
data: {},
// `config` is the config that was provided to `axios` for the request
config: {},
When using catch , or passing a rejection callback as second parameter of then , the response will be available through
the error object as explained in the Handling Errors section.
Config Defaults
You can specify config defaults that will be applied to every request.
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// Override timeout for this request as it's known to take a long time
instance.get('/longRequest', {
timeout: 5000
});
Interceptors
You can intercept requests or responses before they are handled by then or catch .
Handling Errors
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
Using the validateStatus config option, you can define HTTP code(s) that should throw an error.
axios.get('/user/12345', {
validateStatus: function (status) {
return status < 500; // Resolve only if the status code is less than 500
}
})
Using toJSON you get an object with more information about the HTTP error.
axios.get('/user/12345')
.catch(function (error) {
console.log(error.toJSON());
});
Cancellation
You can cancel a request using a cancel token.
The axios cancel token API is based on the withdrawn cancelable promises proposal.
You can create a cancel token using the CancelToken.source factory as shown below:
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
You can also create a cancel token by passing an executor function to the CancelToken constructor:
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
});
Note: you can cancel several requests with the same cancel token.
Using application/x-www-form-urlencoded format
By default, axios serializes JavaScript objects to JSON . To send data in the application/x-www-form-urlencoded format instead, you can
use one of the following options.
Browser
In a browser, you can use the URLSearchParams API as follows:
Note that URLSearchParams is not supported by all browsers (see caniuse.com), but there is a polyfill available (make sure to polyfill
the global environment).
const qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));
Query string
NOTE
The qs library is preferable if you need to stringify nested objects, as the querystring method has known issues with that use case
(https://github.com/nodejs/node-v0.x-archive/issues/1665).
Form data
axios.interceptors.request.use(config => {
if (config.data instanceof FormData) {
Object.assign(config.headers, config.data.getHeaders());
}
return config;
});
Semver
Until axios reaches a 1.0 release, breaking changes will be released with a new minor version. For example 0.5.1 , and 0.5.4 will have
the same API, but 0.6.0 will have breaking changes.
Promises
axios depends on a native ES6 Promise implementation to be supported. If your environment doesn't support ES6 Promises, you
can polyfill.
TypeScript
axios includes TypeScript definitions.
Resources
Changelog
Upgrade Guide
Ecosystem
Contributing Guide
Code of Conduct
Credits
axios is heavily inspired by the $http service provided in Angular. Ultimately axios is an effort to provide a standalone $http -like service
for use outside of Angular.
License