Props and Slots
In the component-driven architecture of Vue 3, props and slots serve as the two foundational mechanisms for passing information from a parent component down to its child. Because both are declared on the child component's tag and provided by the parent, beginners often confuse them. However, they are designed for entirely distinct purposes. Grasping their division of labor and how they collaborate is the first step toward crafting highly reusable components.Props act as the public data interface exposed by a component. A parent component passes data down through custom attributes, and the child component consumes these values just like ordinary local variables.
<!-- ChildComponent: UserCard.vue -->
<script setup>
defineProps({
name: String,
age: Number
})
</script>
<template>
<p>{{ name }}, age {{ age }}</p>
</template>
<!-- Parent usage -->
<UserCard name="Alice" :age="28" />
Props strictly adhere to a unidirectional data flow. Data flows solely from the parent to the child; the child should never mutate props directly. Instead, if a change is needed, the child must emit an event to notify the parent. This guarantees a single source of truth, making component behavior predictable and straightforward to debug.
Slots: Passing Content to Components
Slots address a fundamentally different problem. When a parent wants to inject a block of markup or a template fragment into a child, relying on props becomes awkward and insecure. Treating UI structures as mere data lacks flexibility. Slots allow the parent to author template fragments directly, while the child determines where those fragments are rendered using the <slot> outlet.
<!-- ChildComponent: BaseButton.vue -->
<button class="btn">
<slot>Default Text</slot>
</button>
<!-- Parent usage -->
<BaseButton>Submit Now</BaseButton>
If the parent fails to provide any content, the fallback text nested within the <slot> tags will be rendered as a safety net.
Comparative Overview: Data Versus Content
The core difference between the two can be summarized simply: props pass values, while slots pass structure.
| Dimension | Props | Slots |
|---|---|---|
| Payload Type | Strings, numbers, objects, functions, etc. | Template fragments (markup + logic) |
| Rendering Control | Managed internally by the child using the data | Render location dictated by the <slot> outlet |
| Compilation Scope | Compiled within the child component scope | Compiled within the parent component scope |
| Typical Use Case | Configuring component behavior and appearance | Customizing localized UI content within a component |
As component design evolves, slots branch into default, named, and scoped variants, while props support type validation, default values, and other refinements. For everyday decision-making, remember the golden rule: use props when you need to pass a value, and use slots when you need to pass a piece of the user interface.
Collaborative Harmony: Staying in Your Lane
In real-world development, props and slots frequently work in tandem within the same component. Consider a standard card component:
<!-- Card.vue -->
<script setup>
defineProps({ title: String })
</script>
<template>
<div class="card">
<h3>{{ title }}</h3>
<slot></slot>
</div>
</template>
<!-- Parent usage -->
<Card title="User Profile">
<p>Arbitrary structural content can go right here.</p>
</Card>
Pure data, such as the title string, flows in via props, while open-ended structural content like the body text is distributed through slots. This keeps the component's external API clean and intuitive.
Application Scenarios and Selection Guide
Across production applications, the division between props and slots establishes a universal pattern for component design:
- Primitive Components (buttons, input fields): Rely primarily on props for configuration options like sizing and disabled states, while utilizing slots as custom entry points for icons or text.
- Layout Components (cards, modals, grids): Rely heavily on slots, leaving structural areas like headers, bodies, and footers entirely up to the parent to populate.
- Complex Business Components (tables, lists): Use props for column configurations and data sources, while leveraging slots to customize individual cell rendering. Both mechanisms are indispensable here.
When deciding which to use, follow this decision path:
- Are you passing pure data? Use props.
- Are you passing a slice of UI structure? Use slots.
- Do you need to render parent content using data internal to the child? Look into scoped slots.
- Do you need the child to report information back to the parent? Use events, not props or slots.
Mastering the distinct nature and collaborative synergy of props and slots unlocks the backbone of Vue 3 component communication, empowering you to handle sophisticated application requirements with ease.