bhylu.com

Advertisements

Vue Fallthrough Attributes

Decide which element receives a component caller attributes.

How it works

Attributes that are not declared as props or emits can fall through to a single root element. A wrapper component may need to disable automatic inheritance and explicitly apply those attributes to its actual control. This is particularly important for id, class, aria attributes and listeners. The example sends the caller test hook and accessible label to a real button, not to its wrapper paragraph.

Run the code

Use a separate empty learning folder with Node 22.12+ in the 22 series. The reviewed Node version is 22.15.1. Save all files below with their exact relative paths; downloads may keep only the basename, so create src and any tests folders as listed. This project pins Vue 3.5.44 and Vite 7.3.1. Run npm install, then npm run dev and open the local address Vite prints.

Bhylu has no Vue playground. SFCs, npm imports and routing need this local project; do not paste them into the plain HTML or JavaScript editor. Apache, MySQL and changes to Bhylu server settings are not required. The application is a fictional seed-swap learning fixture.

Example

src/App.vue

<script setup>
import DeskAction from "./DeskAction.vue";
</script>

<template>
  <main>
    <h1>Riverside seed swap</h1>
    <DeskAction data-result aria-label="Check packet desk"
      >Check desk</DeskAction
    >
  </main>
</template>

Expected result

The button receives the accessible label and displays Check desk.

defineOptions disables automatic inheritance; useAttrs returns the undeclared attributes, and v-bind forwards them to the button. Forwarding all attrs is a deliberate contract here, not a universal rule for complex components. Multiple-root components need explicit placement because there is no single obvious destination.

src/DeskAction.vue

src/DeskAction.vue

<script setup>
import { useAttrs } from "vue";
defineOptions({ inheritAttrs: false });
const attrs = useAttrs();
</script>
<template>
  <p>
    <button v-bind="attrs"><slot /></button>
  </p>
</template>

Check APIs and version differences in the official Vue guide.