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.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
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.
createAppVue 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 users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Vue mutations and actions for all of your users in production, try LogRocket.

LogRocket lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks.
With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.
Modernize how you debug your Vue apps — start monitoring for free.

Test out Meta’s AI model, Llama, on a real CRUD frontend projects, compare it with competing models, and walk through the setup process.

Rosario De Chiara discusses why small language models (SLMs) may outperform giants in specific real-world AI systems.

Vibe coding isn’t just AI-assisted chaos. Here’s how to avoid insecure, unreadable code and turn your “vibes” into real developer productivity.

GitHub SpecKit brings structure to AI-assisted coding with a spec-driven workflow. Learn how to build a consistent, React-based project guided by clear specs and plans.
Would you be interested in joining LogRocket's developer community?
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 now
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.