Your first component
Write a single-file component with script setup, a template and scoped styles, and print values in the template.
What you'll learn
When you finish, you can do these.- 1Write a component as one .vue file with three blocks
- 2Use names from script setup in the template
- 3Print values and expressions with double braces
- 4Bind an attribute such as a link's href to a value
A web page is usually written in three languages: HTML for the structure, JavaScript for what changes, and CSS for the look. On a site built without Vue, they often sit in three separate files, so one price tag on a shop page is spread across all three, and changing it means opening each one. Vue lets you write that price tag as a component: one .vue file that holds its HTML, its JavaScript and its CSS together.
The Code tab holds one such component, a file called App.vue: a small page that greets Asha by name, works out the price of two coffees and links to the Vue docs. By the end you can read every line of it and change it to greet you. If you have written a little HTML, you know enough to start.
One file, three blocks
A Vue component file is called a single-file component, or SFC. It is made of blocks:
<script setup lang="ts">holds the logic: variables, functions and imports.lang="ts"means TypeScript, which is JavaScript with optional type labels, so plain JavaScript works there too.<template>holds the markup: HTML plus a few Vue additions.<style scoped>holds the CSS for this component only.
Most components have one of each. A file may also carry more than one <style> block, or a plain <script> next to <script setup>, but you will not need those for a while.
Keeping the three together is the point. Picture a shop's price tag as a Vue component: one file, named after what it shows, PriceTag.vue. Open that file and everything about the price tag is in front of you: its markup, the code that works out the price, and its style. Delete the file, and no stray rule is left behind in a stylesheet somewhere else.
App.vue in the Code tab works the same way, with the whole page in one file. Its style block makes h1 headings green, and the scoped attribute makes sure that rule styles this component's headings and no others.
The browser cannot read a .vue file. A build tool (Vite, in a Vue project) hands it to the Vue compiler, which turns the template into JavaScript that creates the elements you see.
- App.vuescript, template, style
- Vue compiler
- Component
- Elements on the page
In a real project, a file called main.ts starts everything with one line, createApp(App).mount('#app'). It reads: make an app whose top component is App, and put it inside the element with the id app in index.html. The Code tab does this for you, so App.vue is the only file you edit here.
Top-level names reach the template
Anything you declare at the top level of <script setup>, outside any function, is available in the template. You do not export it or return it. The script block of App.vue declares two such names:
<script setup lang="ts">
const name = 'Asha'
const price = 4
</script>
In the template, double curly braces print a value as text. This is called interpolation, and the braces are often called mustaches.
<template>
<h1>Hello, {{ name }}.</h1>
</template>
The page shows one heading: Hello, Asha.
Vue always prints the value as plain text. If name held <b>Asha</b>, the page would show the angle brackets as characters instead of bold text. That keeps markup someone typed into a form from turning into real HTML on your page.
Expressions inside the braces
The braces accept more than a name: one JavaScript expression, a piece of code that produces a value. The template of App.vue adds two lines, one with arithmetic and one with a method call:
<template>
<h1>Hello, {{ name }}.</h1>
<p>Two coffees cost {{ price * 2 }} euros.</p>
<p>{{ name.toUpperCase() }} is learning Vue.</p>
</template>
Under the heading, the page reads "Two coffees cost 8 euros." and "ASHA is learning Vue." toUpperCase() is a method: a function that comes with every piece of text and gives it back in capitals.
The ternary operator, a choice between two values, works too: price > 3 ? 'pricey' : 'cheap' gives "pricey" while the price is 4.
A statement is not an expression: it does something instead of producing a value, such as let, which creates a variable, or an if block. {{ let total = price * 2 }} fails to compile with Error parsing JavaScript expression: Unexpected token, and the preview shows the error and stops updating until you fix it.
When the logic grows past one short line, work the value out in <script setup>, as in const total = price * 2, and print its name.
The braces only work in text between tags. To put a value in an attribute, such as a link's href (the address it opens), write a colon before the attribute name. The colon tells Vue that the quoted part is JavaScript, not text. A later lesson, Directives, covers this colon under its full name, v-bind.
In App.vue, a third name, url, holds the address, and the link's href is bound to it:
<script setup lang="ts">
const url = 'https://vuejs.org'
</script>
<template>
<a :href="url">Read the Vue docs</a>
</template>
The page shows a link, "Read the Vue docs", that opens https://vuejs.org.
What goes wrong
The first mistake is a misspelled name. Vue looks up every name in the template among the top-level names of <script setup>. When it cannot find one, it renders nothing in that spot, and the preview shows a warning that quotes the missing word: Property "nmae" was accessed during render but is not defined on instance.
<h1>Hello, {{ nmae }}.</h1>
<h1>Hello, {{ name }}.</h1>
The heading reads "Hello, ." with a gap where the name should be. Read the warning before you read your code: the word in quotes is the one to fix.
The second mistake is braces inside an attribute. It compiles, no warning appears, and the link is broken.
<a href="{{ url }}">Read the Vue docs</a>
<a :href="url">Read the Vue docs</a>
Without the colon, href is an ordinary HTML attribute and Vue leaves its text alone. The link points to a page literally called {{ url }}. Inspect the link in the browser's developer tools and you will see the braces in its href.
What to remember
- A single-file component keeps the markup, logic and style for one piece of the page in one
.vuefile. - Everything declared at the top level of
<script setup>can be used in the template, with no export or return. {{ }}prints the result of one JavaScript expression as text. Statements such asletor anifblock do not compile there.- Braces do nothing inside an attribute. A colon in front of the attribute, as in
:href="url", binds it to a value. - A misspelled name renders as an empty gap and a warning that quotes the name Vue could not find.
Try it
Open the Code tab and change the starter code. Each task says what you should see.- Put your own name in
name. The heading greets you, and the third line shows your name in capitals. - Under the capitals line, add
<p>{{ name }} has {{ name.length }} letters.</p>. With the starter name it reads "Asha has 4 letters." - Misspell
namein the heading asnmae. The heading shows a gap and the preview shows a warning that quotes"nmae". Fix the spelling: the name returns and the warning goes away.