Components in Vue web applications are very important because they can be used in very simple ways to make very complex applications. In this article, we’ll cover the basics of Vue 3 components and how to use them.
Components are reusable pieces of UI code that build functional web applications. They can either be used as building blocks of complex applications, or as reusable pieces to prevent rewriting similar pieces of code repeatedly.
Vue applications usually consist of a root component and any number of other components. All components are actually component objects that hold the state variables, component logic, and a template that specifies the component rendering.
Before we start creating components, we must first set up our project. There are two types of Vue 3 projects that we can create:
A CDN project is the simplest type of Vue 3 project.
To set up an empty CDN project, we first import the CDN into our HTML page:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.37/vue.global.min.js"></script>
Next, we create a div
element, to render the Vue application:
<div id="app"></div>
Then, we create an empty script
tag at the bottom of the body
tag:
<script> </script>
Finally, we create and mount an empty app, inside the new script
tag:
Vue.createApp({}).mount("#app");
After completing the above steps, the HTML code should look like this:
<script src="https://unpkg.com/vue@3"></script> <div id="app"></div> <script> Vue.createApp({}).mount("#app"); </script>
Setting up a CLI project is more complex than creating a CDN project. Start by installing the following:
Next, install Vue CLI with any of the below commands:
# for npm npm install -g @vue/cli # for yarn 1.x yarn global add @vue/cli
Now, create a project vue-tutorial
, like so:
vue create vue-tutorial
Then, select Vue 3
:
N.B., if you’re using Yarn v2 or later, you’ll have to use
*yarn dlx @vue/cli create vue-tutorial*
to create the project; Yarn v2+ does not support global installation
After Vue CLI creates the project, its tree structure should look like this:
. ├── babel.config.js ├── jsconfig.json ├── package.json ├── package-lock.json ├── public │ ├── favicon.ico │ └── index.html ├── README.md ├── src │ ├── App.vueThe v │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ └── main.js └── vue.config.js
The root of a Vue 3 project is src/main.js
:
import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app')
In the above code, the createApp
function, from vue
, creates an app instance and mounts it to an HTML element with ID app
. createApp
takes a component as the argument, in this case from the ./App.vue
file.
createApp
Vue 3 provides a createApp
function for creating application instances. The function takes a component as an argument to become the root of the instance.
The application instance provides a mount
method that mounts the instance to an HTML element:
import { createApp } from "vue"; import App from "./App.vue"; createApp(App).mount('#app'); // the element has an id "app"
The createApp
function only exists in Vue 3. Vue 2 provides a Vue
constructor function to create application instances.
import Vue from 'vue' import App from './App.vue' new Vue({ render: h => h(App), }).$mount('#app')
The Vue
constructor takes an object that should include a render
method to render the root component. The constructed instance has a $mount
method that allows us to mount the instance to an HTML element. The $mount
method also uses a CSS selector to reference the element.
Vue allows us to create directives in both v2 and v3. Directives enable us to add functionality to a Vue application instance. To create directives in Vue 2, we need to use the Vue
object.
Any directives created using the Vue
object will be usable by all application instances. This becomes a problem when our web app uses more than one Vue application instance but we want to limit certain functionality to specific instances.
// The only way to create a directive in Vue 2 Vue.directive('directive', { // ... }); // Both of the application instances can access the directive const appOne = new Vue(App1).mount('#app1'); const appTwo = new Vue(App2).mount('#app2');
The Vue 3 createApp
function solves this problem by providing instance APIs for customizing the instance. One of the API methods, directive
, creates custom directives for the instance:
// Both of the application instances can access the directive const appOne = Vue.createApp(App1); appOne.directive('directive', { // only availalble "appOne" instance */ }); appOne.mount('#app1'); const appTwo = Vue.createApp(App2); appTwo.directive('directive', { // only availalble to "appTwo" }); appTwo.mount('#app2');
All Vue web applications start with a root component. The createApp
function binds this component to an HTML element.
To create a root component, we start with an empty object and call it RootComponent
by convention. To register the object as the root, we pass it to the Vue.createApp()
function:
<script> const RootComponent = {}; const app = Vue.createApp(RootComponent); app.mount("#app"); </script>
In Vue CLI, the root component is just like any other component and is at src/App.vue
.
To create a component in Vue, we start with an empty object, just like the root component. But, instead of passing it to the Vue.createApp()
function, we register it to the component we want to use it in.
To register a new component in the root component, we create a components
property in the object. Here’s an example of registering a CustomButton
component in the root:
<script> const CustomButton = {}; const RootComponent = { components: { CustomButton, }, }; const app = Vue.createApp(RootComponent); app.mount("#app"); </script>
A component needs a template that it renders to the view. A template is usually in a script
tag with type
of vue-template
to prevent the browser from executing it as JavaScript code.
The template should have an ID, which allows the component object to reference its script. Here’s a simple template script for the CustomButton
component. In this example, the template script ID is "custom-button"
:
<script type="vue-template" id="custom-button"> <button>Click Me!</button> </script>
To bind the template to the component, we add a template
property to the component object, and pass the reference to the script:
<script> const CustomButton = { template: "#custom-button" }; ... </script>
Now, we can use the CustomButton
component in the root component:
<div id="app"> <CustomButton /> </div>
In the CLI version, components have a different structure. Each component is in a .vue
file. All components must include a template
tag and a script
. A style
tag may also be added to style the template.
Here’s an example of a simple Vue component created with Vue CLI:
<template> Hello, World! </template> <script> export default {} </script>
The object after export default
is just like the component object in the CDN version. The only differences between simple components created by the CLI and CDN are as follows:
To use a component in another component, follow the below steps:
.vue
file into the component script tagcomponent
propertyimport HelloWorld from './components/HelloWorld.vue'export default { components: { HelloWorld } }
To add state variables to Vue components, create a data
method in the component object. The data
method should return an object, which contains the state variables.
In the below example, the CustomButton
component contains a buttonText
state, which has a string value: "Click me!"
:
<script> const CustomButton = { template: "#custom-button", data: function () { return { 'buttonText': "Click me!" } } } // some lines are hidden </script>
We use the state variable inside the template script’s button
tag by surrounding it with double curly braces, {{ }}
, like so:
<script type="vue-template" id="custom-button"> <button>{{ buttonText }}</button> </script>
Props in Vue are attributes that are passed to components, just like HTML elements. They promote the flexibility and reusability of components, which makes it possible to use the same piece of code for slightly different scenarios, making the UI more efficient.
We pass props to components in the same way that we pass attributes to HTML elements:
<component prop="value"></component>
We can then update the custom-button
component by adding a text
prop that contains the button text:
<div id="app"> {{ greeting }}, {{ name }} <CustomButton text="Click me!"></CustomButton> </div>
To make the text
prop work, we register it to the CustomButton
component:
<script> const CustomButton = { template: "#custom-button", data: function () { return { 'buttonText': "Click me!" } }, > props: ['text'] } // some lines are hidden </script>
Then, we replace {{ buttonText }}
with {{ text }}
in the template:
<script type="vue-template" id="custom-button"> <button>{{ text }}</button> </script>
Next, we remove buttonText
state variable, since it is no longer needed:
<script> const CustomButton = { template: "#custom-button", props: ['text'], } // some lines are hidden </script>
It’s possible to use multiple props in a Vue component. The more props a component has the more flexible and usable it becomes. To add more props to a component, append the name of the prop to the component’s prop array:
<script> const CustomButton = { template: "#custom-button", > props: ['text', 'label'], } // some lines are hidden </script>
But, take care when adding more props to a Vue component. Too many props can make a Vue component unnecessarily complex and hard to maintain.
Now, the CustomButton
component has two props: text
and label
.
After adding the label
prop, we modify the template to use this prop as well:
<script type="vue-template" id="custom-button"> {{ label }}: <button>{{ text }}</button> </script>
Using multiple props with components created with Vue CLI works the same way.
Vue 3 allows us to nest components in other components. This nesting leads to composition, where one large or complex component is composed of smaller components. Composition helps make the user interface more efficient and easier to maintain.
Here’s a simple example of nested components. The StartStopButton
contains two CustomButton
components:
<script type='vue-template' id="start-stop-button"> <!-- template script for "StartStopButton" component --> <CustomButton text='start'/> <CustomButton text='stop'/> </script> <script> const CustomButton = { // ... }; const StartStopButton = { template: '#start-stop-button', // reference template script components: { CustomButton, // import "CustomButton" component } } const RootComponent = { // ... </script>
Now, we can add the StartStopButton
component to the root components:
<div id="app"> {{ greeting }}, {{ name }} <start-stop-button></start-stop-button> </div> <!-- some tags are hidden --> <script> // ... const RootComponent = { data: function () { return { greeting: "Hello", name: "John", } }, components: { StartStopButton }, }; // ... </script>
defineComponent
and TypeScriptBoth Vue 2 and Vue 3 allow us to build Vue applications using TypeScript. TypeScript is a powerful superset of JavaScript, offering optional static typing and supporting more IDEs.
TypeScript support in Vue 2 is really poor compared to Vue 3. To create components in Vue 2 with TypeScript, we need to either use the standard syntax or the class component. Each method has tradeoffs; let’s take a look.
In this example, we create a component in Vue 2 with TypeScript using the standard syntax:
<template> <div> {{ name }}: {{ msg }} </div> </template> <script lang="ts"> import Vue from "vue"; export default Vue.extend({ name: "HelloVue", props: { msg: String }, data () { return { name: "LogRocket" } } }) </script>
In the above code, we can see that the component created with standard syntax is similar to a component object, but lacks support for features like Vuex.
In this example, we create a component in Vue 2 with TypeScript using a class component:
<template> <div> {{ name}}: {{ msg }} </div> </template> <script lang="ts"> import { Component, Prop, Vue } from "vue-property-decorator"; @Component export default class HelloVue extends Vue { @Prop() public msg!: string; public name: string = "LogRocket"; } </script>
In the above code, we can see that the component created with the class component is very different from a component object, although it does support additional features.
Vue 3 resolves these drawbacks by providing a defineComponent
function. This function allows us to create a TypeScript component that is similar to a component object and also supports extra features.
<template> <div> {{ name }}: {{ msg }} </div> </template> <script lang="ts"> import { defineComponent } from "vue"; export default defineComponent({ props: { msg: { type: String, required: true } }, data() { return { name: "LogRocket", } } }); </script>
I hope this article helped you understand the process of creating components in Vue 3 and using them to build complex applications. But, if you still do not find any of the sections clear or could not follow along, let me know in the comments.
Thank you for reading and have a nice day!
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowExplore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
Build a real-time image background remover in Vue using Transformers.js and WebGPU for client-side processing with privacy and efficiency.
Optimize search parameter handling in React and Next.js with nuqs for SEO-friendly, shareable URLs and a better user experience.
5 Replies to "A definitive guide to Vue 3 components"
🙌🏾
🤮 vue3? I see vue2
Thanks you for the post guys, it helped me a lot, you made a litle mistake tho, when you use a component in a CDN project you actually have to use he kebab syntax and with an ending tag to render it, like , instead of , which is exclusive for the CLI version.
Thanks for pointing it out! I’m aware of that, and I did it this way to show the similarity between the two versions.
Found a typo in your text. After “The only differences between simple components created by the CLI and CDN are as follows:” you need to replace “CDN” to “CLI” in both following sentences. Because in the CLI you do not need to create a script tag with a template, and it is also in the CLI that you can split components into separate files, but not in the CDN version.