šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Components & Props in Vue.js

Learn about Components & Props in this comprehensive Vue.js tutorial. Understand the core of Vue architecture: Components, Props, and Custom Events.

⚔ Total XP: 0|šŸ’» vuejs XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

Select an unlocked node to view details root

šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Break your application into reusable Lego bricks.

1Component Registration

In Vue 3 with <script setup>, using a component is as easy as importing it. You don't need to manually register it in a components object. Vue's compiler automatically makes it available in the template.

2Props (Data Down)

Props are custom attributes you can register on a component. When a value is passed to a prop attribute, it becomes a property on that component instance. You declare them using defineProps(). Remember: Props are strictly read-only inside the child component.

3Emits (Events Up)

Because children cannot mutate props, they must communicate back to the parent by emitting custom events using defineEmits(). The parent listens to these events using the standard v-on (or @) directive.

4Step-by-Step Breakdown

A Vue application is a tree of components. Instead of writing one massive HTML file, you break your UI into small, reusable Lego bricks.

To use another component in Vue 3 <script setup>, you just import it. Vue automatically registers it for use in the template!

In Vue 3 <script setup>, how do you register an imported component so you can use it in the template?

  • →Add it to the components object
  • →Call app.component()
  • →Nothing, it's automatically available

Data flows DOWN the component tree via "Props". A parent passes data to a child like custom HTML attributes.

Inside the child component, you declare which props it accepts using the defineProps() compiler macro.

Which function is used inside <script setup> to declare the props that a component accepts?

  • →getProps
  • →defineProps
  • →useProps

Events flow UP the component tree. A child cannot mutate a prop directly (props are read-only). Instead, it "emits" an event to the parent.

The parent listens to the emitted event using the @ syntax, just like a native DOM event!

Which function is used inside <script setup> to declare the custom events that a component can send to its parent?

  • →defineEvents
  • →useEmits
  • →defineEmits

Props down, Events up. This is the golden rule of Vue components. It keeps your data flow predictable and easy to debug.

Level Up šŸš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Attribute Fallthrough for ARIA

When a component has a single root element, attributes the parent passes that aren't declared as props — including aria-label, aria-describedby, or role — automatically fall through to that root element, so accessibility metadata set on <CustomButton aria-label="Close"> reaches the real <button> without any pass-through code.

<!-- CustomButton.vue root --> <button><slot /></button> <!-- Parent: <CustomButton aria-label="Close">Ɨ</CustomButton> --> <!-- Renders: <button aria-label="Close">Ɨ</button> -->

SEO Implications

  • 1

    Multi-Root Components Disable Automatic Fallthrough

    If a component's template has multiple root nodes (allowed in Vue 3), attribute fallthrough is disabled and Vue warns unless you bind v-bind="$attrs" explicitly — meaning an id targeted by a JSON-LD script or an aria attribute can silently disappear from the rendered HTML crawlers see.

Best Practices

Declare Explicit Prop Types and Defaults

Declare props with defineProps({ age: { type: Number, required: true } }) instead of a bare array of strings. Vue validates the type at runtime and logs a console warning in development if a parent passes the wrong type, catching bugs before production.

Never Mutate a Prop

Treat every prop as read-only. If a child needs to change a value derived from a prop, copy it into local state with ref() or computed(), and report changes back up via defineEmits() instead of assigning to the prop directly.

Frequent Bugs

THE BUG

Vue warns "Extraneous non-props attributes were passed to component but could not be automatically inherited".

THE FIX

This fires when a component has multiple root elements and receives an attribute like class or an event listener. Bind v-bind="$attrs" explicitly on the intended root element to route the attribute correctly.

Real-World Examples

A Reusable Confirmation Modal

A <ConfirmDialog :open="showConfirm" title="Delete item?" @confirm="deleteItem" @cancel="showConfirm=false" /> receives its visibility and copy via props and reports the user's decision back through two named events, keeping the deletion logic entirely in the parent.

<script setup>
defineProps<{ open: boolean; title: string }>();
const emit = defineEmits(['confirm', 'cancel']);
</script>
<template>
  <div v-if="open" class="dialog">
    <p>{{ title }}</p>
    <button @click="emit('confirm')">Yes</button>
    <button @click="emit('cancel')">No</button>
  </div>
</template>

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Mutating a Prop Directly

// Wrong const props = defineProps(['count']); function increment() { props.count++; } // Correct const emit = defineEmits(['update:count']); function increment() { emit('update:count', props.count + 1); }

The Solution //

A child component should never assign to a prop it received (e.g. props.title = 'New'). Vue logs "Unexpected mutation of prop" because props are a one-way binding. Copy the prop into local state with ref()/computed() first, or emit an event asking the parent to change its own data.

The Error //

Losing Reactivity by Destructuring reactive()

// Wrong const { name } = reactive({ name: 'Alice' }); // name is now a static string // Correct const state = reactive({ name: 'Alice' }); const { name } = toRefs(state); // name is a live ref

The Solution //

Destructuring a reactive() object copies its properties out as disconnected plain values, so future mutations of the original object won't update the destructured variables. Keep referencing the object's properties directly, or convert it with toRefs() before destructuring.

Continue Learning