Components and Single-File Components
By Syncfusion®, Inc. · Last updated
What components are, component trees, parent and child, the blocks of a Single-File Component, independent instances, local and global registration, naming rules, scoped styles, :deep(), CSS modules, v-bind() in CSS and defineOptions(), and Tailwind CSS and UI libraries.
Components
- A component = a reusable piece of the page with its own HTML, code and style.
- A big page is built from many small components.
- A component used inside another is its child. The outer one is the parent.
- All the components together form a component tree.
Simple lesson: a component is a reusable piece of the page.
A Real-World Example
- Meera's page has a header, a menu and a footer.
- Each part lives in its own file, so it is easy to find and fix.
App.vueputs them together.
- What happened:
App.vueimported three components and used them like HTML tags. - Open the
componentsfolder in the Explorer to see each file.
What You Will Learn
- How components form a tree.
- The three blocks of a
.vuefile. - Why each use of a component has its own state.
- How to register components locally and globally.
- How to name components and files.
- How scoped styles and
:deep()work, and where Tailwind and UI libraries fit. - How to use CSS modules.
- How to use state inside CSS with
v-bind(). - How to set component options with
defineOptions().
The Component Tree
Animation: App sits at the top of the tree. SiteHeader, MenuList and SiteFooter are its children. MenuList has DishCard children. Each level lights up in turn
App SiteHeader MenuList SiteFooter DishCard DishCard
App is the root. Each component can have children of its own.
Appis the root component. You met it in Introduction to Vue.- A parent passes data down to its children. You will learn how in Props and Events.
Simple lesson: components nest inside each other like a family tree.
Single-File Components
- A Single-File Component (SFC) = one
.vuefile that holds one component. It has up to three blocks:
<template>: what it shows<script setup>: its state and functions<style>: how it looks
- Vite compiles each
.vuefile into plain JavaScript and CSS. - In Vue 3 a template may have several root elements. Vue 2 allowed only one.
<script setup lang="ts">
import { ref } from "vue";
const likes = ref(0);
</script>
<template>
<button class="like" @click="likes++">❤ {{ likes }}</button>
</template>
<style scoped>
.like { color: crimson; }
</style>
Simple lesson: one .vue file = one component = template + script + style.
Independent Instances
- Each time you use a component, Vue makes a new instance of it.
- Instance = one live copy, with its own state.
- Three
<LikeButton />tags = three separate counts.
State created at the top of a separate .ts file is shared by every instance that imports it. Create per-instance state inside the component's own <script setup>.
Simple lesson: every use of a component gets its own copy of the state.
In Vue DevTools, the Components tab shows this tree. Click a component to see its own state. In this lab, each box's Explorer shows the files instead.
Registering Components
Local Registration
- With
<script setup>, importing a component is enough. It is ready in the template. - This is local registration: only this file can use it.
- Local is the default choice. Unused components are left out of the build.
Global Registration
app.component("Name", Component)inmain.tsmakes a component usable everywhere, with no import.- Use it only for small parts used on almost every page, such as an icon or a price tag.
- Too many global components make it hard to see where a tag comes from.
Simple lesson: import to use a component in one file. Register globally only for tiny, everywhere parts.
Naming Components
- Use PascalCase for names:
DishCard,SiteHeader. - Use two or more words, so names never clash with HTML tags like
<header>. - Name the file the same:
DishCard.vue. - In templates you can write
<DishCard />or<dish-card />. PascalCase is clearer. - Self-close a component with no content:
<DishCard />.
| Good | Avoid | Why |
|---|---|---|
DishCard.vue | Card.vue | One word may clash with HTML |
SiteHeader | Header | <header> is already an HTML tag |
<DishCard /> | <DishCard></DishCard> | Shorter when it has no content |
Styling Components
Scoped Styles
<style scoped>applies only to this component's own elements.- Vue adds a unique attribute like
data-v-1a2b3cto the elements and the CSS. - So
.titlein one component cannot change.titlein another. - A child's root element gets the parent's scoped style too.
- What happened: App's
.titlerule did not reach the card's title. :deep(.price)reached inside the child on purpose.
CSS Modules
<style module>turns each class into a unique name.- Use the classes in the template through
$style::class="$style.badge". - Good for big apps where class names might clash.
v-bind() in CSS
v-bind(name)inside<style>uses a value from the script.- When the value changes, the style changes.
Pick one style approach per project. Most Vue projects use <style scoped> for almost everything.
Tailwind CSS and UI Libraries
- Tailwind CSS = small utility classes such as
p-4andtext-green-700, written in the template. No separate CSS file per component. - Tailwind v4 needs one Vite plugin and one CSS line:
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({ plugins: [vue(), tailwindcss()] });
/* src/style.css, imported in main.ts */
@import "tailwindcss";
<template>
<button class="rounded-lg bg-green-700 px-4 py-2 text-white hover:bg-green-800">Order</button>
</template>
- UI libraries give ready-made, accessible components: buttons, tables, dialogs, date pickers.
| Library | Style | Good for |
|---|---|---|
| Vuetify | Material Design | Admin apps, dashboards |
| PrimeVue | Many themes, or unstyled | Business apps with big tables and forms |
| Element Plus | Clean desktop look | Internal tools |
| Nuxt UI / shadcn-vue | Tailwind-based | Custom-looking sites |
- Install one with npm, then
app.use(Library)or import components one by one (smaller bundles).
The preview boxes have no internet, so Tailwind and UI libraries cannot load here. Scoped CSS, CSS modules and v-bind() above work the same way in every project.
defineOptions()
defineOptions()(Vue 3.3) sets options that have no other place in<script setup>.- The common ones are
name(shown in Vue DevTools) andinheritAttrs. - You will use
inheritAttrsin Slots, Attributes and Provide/Inject.
Simple lesson: scoped CSS by default, :deep() to reach a child, $style for modules, v-bind() for live values.
TL;DR
- Component = a reusable piece of the page.
- Component tree = the root
Appwith children nested inside. - SFC = a
.vuefile with<template>,<script setup>and<style>. - Instance = each use of a component, with its own state.
- Local registration = import it in
<script setup>. The default. - Global registration =
app.component("Name", C)inmain.ts. - Naming = PascalCase, two or more words, same file name.
<style scoped>= styles only this component.:deep()reaches children.<style module>/v-bind()= unique class names via$style/ script values in CSS.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| One-word names | Header.vue is fine | SiteHeader.vue | Clashes with the HTML <header> tag |
| Forgetting the import | Vue finds components by itself | Import it, or register globally | "Failed to resolve component" warning |
| Shared state in a module | Each instance gets its own copy | Create state inside the component | All instances change together |
| Styling a child's inner element from the parent | Scoped CSS reaches children | Use :deep(.class) | The style is ignored |
| Everything global | Global is easier | Import locally by default | Bigger builds, unclear origins |
class="$style.badge" | Plain class reads $style | Write :class="$style.badge" | The class is the literal text |
Check Yourself
Exercises
What to Remember
- A page is a tree of components, with
Appat the root. - A
.vuefile holds one component: template, script setup and style. - Each use of a component has its own state.
- Import components locally. Register globally only tiny, everywhere parts.
- Use
<style scoped>. Reach into children with:deep().
Next: Props and Events