Your First Component
A single-file component holds template, script and style in one .vue file; read the generated App.vue, then make it react.
const name = ref('') // name.value in script, name in the templateA component is one piece of a page, with its own markup, data and styles. In a Vue project each one is a single-file component: a file ending in .vue that contains a <template>, a <script setup> and, optionally, a <style>. The three blocks describe one thing, so they live in one file. Filenames are PascalCase, such as UserCard.vue.
Reading the generated App.vue
src/App.vue is the root component. Its script imports two other components from src/components. Its template uses them as tags and passes msg to one of them as a prop, an input from the parent; Stage 3 covers writing components that take props. Its style block carries scoped, which limits those rules to elements this file renders. Replacing the whole file with your own is a normal first step.
Making it react
A plain value in the script is rendered once. For a value that changes, wrap it in a ref: a reactive box around a single value, created with ref(initial). In script you read and write it through .value. In the template you write it without .value, because Vue unwraps top-level refs there. {{ }} is interpolation, printing a value as text. :value binds an attribute to a value and @input runs a function when that event fires. Those four pieces are enough for a component that updates as you type.
Example
<script setup lang="ts">
import { ref } from 'vue'
const name = ref('')
function onInput(event: Event) {
name.value = (event.target as HTMLInputElement).value
}
</script>
<template>
<label for="name">Your name</label>
<input id="name" :value="name" @input="onInput" />
<p>Hello, {{ name || 'stranger' }}.</p>
</template>
<style scoped>
label {
display: block;
font-weight: 600;
}
</style>When to use it
- Replacing the generated App.vue with your own first screen
- Splitting a page you already have into named .vue files
- Wiring a text input to a value that the page shows back as you type
More examples
The generated App.vue, trimmed
Close to what create-vue writes. Two things to notice: an imported component is used as a tag with no registration step, and a relative image src inside a template is compiled into an import, so the build hashes the file and rewrites the URL. Read this file before you delete it, then delete it.
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
</style>A component with no script at all
Only the template block is required. A component that has no data and no behaviour needs no script block, and adding an empty one adds nothing. This is the shape of most layout and presentational components.
<template>
<footer>
<p>Built with Vue 3.5</p>
</footer>
</template>
<style scoped>
footer {
padding: 1rem 0;
font-size: 0.875rem;
color: #555;
}
</style>Where the root component is mounted
A compiled .vue file is an ordinary ES module whose default export is the component, which is why App.vue can be imported like anything else. createApp(App) builds the application instance and mount('#app') renders it into the matching element in index.html.
// src/main.ts
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
Discussion