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
Fully supported.
Fully supported.
Fully supported.
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
Vue warns "Extraneous non-props attributes were passed to component but could not be automatically inherited".
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>