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.vue puts them together.
  • What happened: App.vue imported three components and used them like HTML tags.
  • Open the components folder in the Explorer to see each file.

What You Will Learn

  1. How components form a tree.
  2. The three blocks of a .vue file.
  3. Why each use of a component has its own state.
  4. How to register components locally and globally.
  5. How to name components and files.
  6. How scoped styles and :deep() work, and where Tailwind and UI libraries fit.
  7. How to use CSS modules.
  8. How to use state inside CSS with v-bind().
  9. 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.

  • App is 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 .vue file 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 .vue file 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) in main.ts makes 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 />.
GoodAvoidWhy
DishCard.vueCard.vueOne word may clash with HTML
SiteHeaderHeader<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-1a2b3c to the elements and the CSS.
  • So .title in one component cannot change .title in another.
  • A child's root element gets the parent's scoped style too.
  • What happened: App's .title rule 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-4 and text-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.
LibraryStyleGood for
VuetifyMaterial DesignAdmin apps, dashboards
PrimeVueMany themes, or unstyledBusiness apps with big tables and forms
Element PlusClean desktop lookInternal tools
Nuxt UI / shadcn-vueTailwind-basedCustom-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) and inheritAttrs.
  • You will use inheritAttrs in 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 App with children nested inside.
  • SFC = a .vue file 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) in main.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

MistakeWrong ideaCorrect ideaWhat goes wrong
One-word namesHeader.vue is fineSiteHeader.vueClashes with the HTML <header> tag
Forgetting the importVue finds components by itselfImport it, or register globally"Failed to resolve component" warning
Shared state in a moduleEach instance gets its own copyCreate state inside the componentAll instances change together
Styling a child's inner element from the parentScoped CSS reaches childrenUse :deep(.class)The style is ignored
Everything globalGlobal is easierImport locally by defaultBigger builds, unclear origins
class="$style.badge"Plain class reads $styleWrite :class="$style.badge"The class is the literal text

Check Yourself

Exercises

What to Remember

  • A page is a tree of components, with App at the root.
  • A .vue file 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