Vue Fallthrough Attributes
Understanding attribute inheritance in components
🎯 What are Fallthrough Attributes?
Fallthrough attributes are HTML attributes or event listeners passed to a component that aren't declared as props or emits. Vue automatically applies them to the component's root element, making components more flexible and reusable without extra configuration.
<!-- MyButton.vue -->
<template>
<button>
<slot />
</button>
</template>
<!-- Parent Usage -->
<template>
<MyButton class="primary" id="submit-btn" disabled>
Submit
</MyButton>
</template>
<!-- Rendered Output -->
<button class="primary" id="submit-btn" disabled>
Submit
</button>
Output:
Fallthrough Features
Automatic
Attributes pass through automatically
<MyButton class="btn" />
<!-- class applied automatically -->
Class Merging
Classes combine intelligently
<!-- Both classes apply -->
class="btn primary"
Event Listeners
Events pass through too
<MyButton @click="handler" />
Disable Inherit
Control inheritance behavior
defineOptions({
inheritAttrs: false
})
🔹 Basic Fallthrough Example
Attributes automatically apply to root element:
<!-- CustomInput.vue -->
<template>
<input type="text" />
</template>
<!-- Parent Usage -->
<template>
<CustomInput
placeholder="Enter name"
class="form-input"
maxlength="50"
/>
</template>
<!-- Rendered as -->
<input
type="text"
placeholder="Enter name"
class="form-input"
maxlength="50"
/>
Output:
🔹 Class and Style Merging
Classes and styles combine from both sources:
<!-- StyledButton.vue -->
<template>
<button class="base-btn">
<slot />
</button>
</template>
<style scoped>
.base-btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
}
</style>
<!-- Parent Usage -->
<template>
<StyledButton class="primary-btn">
Click Me
</StyledButton>
</template>
<!-- Rendered with both classes -->
<button class="base-btn primary-btn">
Click Me
</button>
Output:
Both base-btn and primary-btn classes applied
🔹 Event Listener Fallthrough
Event handlers pass through automatically:
<!-- ClickableDiv.vue -->
<template>
<div class="clickable">
<slot />
</div>
</template>
<!-- Parent Usage -->
<template>
<ClickableDiv @click="handleClick" @mouseover="handleHover">
Hover or Click Me
</ClickableDiv>
</template>
<script setup>
function handleClick() {
console.log('Clicked!')
}
function handleHover() {
console.log('Hovered!')
}
</script>
Output:
Events automatically work on the div
🔹 Disabling Attribute Inheritance
Control where attributes are applied:
<!-- CustomButton.vue -->
<template>
<div class="button-wrapper">
<button v-bind="$attrs">
<slot />
</button>
</div>
</template>
<script setup>
defineOptions({
inheritAttrs: false
})
</script>
<!-- Parent Usage -->
<template>
<CustomButton class="primary" @click="handleClick">
Submit
</CustomButton>
</template>
<!-- Attributes go to button, not wrapper -->
<div class="button-wrapper">
<button class="primary" @click="handleClick">
Submit
</button>
</div>
Output:
Attributes applied to inner button, not wrapper
🔹 Accessing Fallthrough Attributes
Use $attrs to access fallthrough attributes:
<template>
<div>
<p>Received attributes:</p>
<pre>{{ $attrs }}</pre>
<button v-bind="$attrs">
Button with all attributes
</button>
</div>
</template>
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
// Access in script
console.log(attrs.class)
console.log(attrs.onClick)
</script>
🔹 Multiple Root Elements
With multiple roots, specify where attributes go:
<template>
<header>Header</header>
<main v-bind="$attrs">
<!-- Attributes applied here -->
Main Content
</main>
<footer>Footer</footer>
</template>
<script setup>
defineOptions({
inheritAttrs: false
})
</script>
Output:
Attributes only on main element