intermediate Step 7 of 15

Components

Vue.js Development

Components

Components are the fundamental building blocks of Vue applications. A component encapsulates its own template, logic, and styles into a reusable unit that can be composed with other components to build complex user interfaces. Vue's Single-File Component (SFC) format uses .vue files that combine the template, script, and style sections into a single file, providing a clean and organized development experience. Components communicate with their parents through props (data flowing down) and events (data flowing up), creating a clear and predictable data flow architecture.

Single-File Components

A Vue Single-File Component has three optional sections: <template> for HTML, <script> for JavaScript logic, and <style> for CSS. The scoped attribute on the style tag limits CSS to the current component.

<!-- AlertBox.vue -->
<template>
  <div class="alert" :class="`alert-${type}`">
    <strong>{{ title }}</strong>
    <p>{{ message }}</p>
    <button v-if="dismissible" @click="$emit('dismiss')">Close</button>
  </div>
</template>

<script>
export default {
  name: "AlertBox",
  props: {
    title: { type: String, required: true },
    message: { type: String, required: true },
    type: {
      type: String,
      default: "info",
      validator(value) {
        return ["info", "success", "warning", "error"].includes(value);
      },
    },
    dismissible: { type: Boolean, default: false },
  },
  emits: ["dismiss"],
};
</script>

<style scoped>
.alert { padding: 16px; border-radius: 8px; margin: 8px 0; }
.alert-info { background: #e3f2fd; color: #1565c0; }
.alert-success { background: #e8f5e9; color: #2e7d32; }
.alert-warning { background: #fff3e0; color: #e65100; }
.alert-error { background: #ffebee; color: #c62828; }
</style>

Props

Props are custom attributes that pass data from parent to child components. Vue supports type checking, default values, required flags, and custom validators for props.

<!-- UserCard.vue -->
<script>
export default {
  props: {
    // Basic type check
    name: String,

    // Multiple possible types
    id: [String, Number],

    // Required with type
    email: { type: String, required: true },

    // Default value
    role: { type: String, default: "viewer" },

    // Default value from factory function (for objects/arrays)
    tags: {
      type: Array,
      default: () => [],
    },

    // Custom validator
    status: {
      type: String,
      validator(value) {
        return ["active", "inactive", "pending"].includes(value);
      },
    },
  },
};
</script>

<!-- Parent usage -->
<template>
  <UserCard
    :id="1"
    name="Alice"
    email="alice@test.com"
    role="admin"
    status="active"
    :tags="['developer', 'lead']"
  />
</template>

Slots

Slots allow parent components to inject content into child component templates. They enable flexible, composable component designs where the parent controls what is rendered inside the child's layout.

<!-- Card.vue -->
<template>
  <div class="card">
    <div class="card-header">
      <slot name="header">Default Header</slot>
    </div>
    <div class="card-body">
      <slot>Default content goes here.</slot>
    </div>
    <div class="card-footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<!-- Parent usage with named slots -->
<template>
  <Card>
    <template #header>
      <h2>Product Details</h2>
    </template>

    <!-- Default slot content -->
    <p>This is the main content of the card.</p>
    <p>It can contain any HTML or components.</p>

    <template #footer>
      <button @click="save">Save</button>
      <button @click="cancel">Cancel</button>
    </template>
  </Card>
</template>

Component Registration

Components can be registered locally within a parent component or globally on the app instance. Local registration is preferred because it results in better tree-shaking and clearer dependency graphs.

<script>
// Local registration (preferred)
import AlertBox from "./AlertBox.vue";
import UserCard from "./UserCard.vue";
import Card from "./Card.vue";

export default {
  components: {
    AlertBox,
    UserCard,
    Card,
  },
};
</script>

// Global registration (main.ts)
// import AlertBox from "./components/AlertBox.vue";
// app.component("AlertBox", AlertBox);
Tip: Use scoped styles by default to prevent CSS leaking between components. When you need to style child components from a parent, use the :deep() combinator: :deep(.child-class) { color: red; }.

Key Takeaways

  • Single-File Components (.vue) combine template, script, and style in one file.
  • Props pass data from parent to child with type checking, defaults, and validators.
  • Slots let parents inject content into child component templates for flexible composition.
  • Named slots use #slotName syntax for targeting specific slot locations.
  • Prefer local component registration for better tree-shaking and clearer dependencies.