Bambi Bunny
Bambi Bunny

Reputation: 1408

Vue 3 passing array warning: Extraneous non-props attributes were passed to component but could not be automatically inherited

please, I'm learning a VueJS 3 and I have probably begineer problem. I have warn in browser developer console like this one:

enter image description here

The Message is:

[Vue warn]: Extraneous non-props attributes (class) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.

I'm passing array of objects to the child Component. In my parent views/Home.vue compoment I have this implemenation:

<template>
  <div class="wrapper">
    <section v-for="(item, index) in items" :key="index" class="box">
      <ItemProperties class="infobox-item-properties" :info="item.properties" />
    </section>
  </div>
</template>
<script>
import { ref } from 'vue'
import { data } from '@/data.js'
import ItemProperties from '@/components/ItemProperties.vue'

export default {
  components: {
    ItemDescription,
  },
  setup() {
    const items = ref(data)

    return {
      items,
    }
  },
</script>

In child compoment components/ItemProperties.vue I have this code:

<template>
  <div class="infobox-item-property" v-for="(object, index) in info" :key="index">
    <span class="infobox-item-title">{{ object.name }}:</span>
    <span v-if="object.type === 'rating'">
      <span v-for="(v, k) in object.value" :key="k">{{ object.icon }}</span>
    </span>
    <span v-else>
      <span>{{ object.value }}</span>
    </span>
  </div>
</template>

<script>
export default {
  props: {
    info: {
      type: Array,
      required: false,
      default: () => [
        {
          name: '',
          value: '',
          type: 'string',
          icon: '',
        },
      ],
    },
  },
}
</script>

It doesn't matter if I have default() function or not. Also doesn't matter if I have v-if condition or not. If I have cycle in the Array, I got this warning

Data are in data.js file. The part of file is here:

export const data = [
  {
    title: 'White shirt',
    properties: [
      { name: 'Material', value: 'Cotton', type: 'string', icon: '' },
      { name: 'Size', value: 'M', type: 'string', icon: '' },
      { name: 'Count', value: 4, type: 'number', icon: '' },
      { name: 'Absorption', value: 4, type: 'rating', icon: '💧' },
      { name: 'Rating', value: 2, type: 'rating', icon: '⭐️' },
      { name: 'Confort', value: 2, type: 'rating', icon: '🛏' },
      { name: 'Sleeves', value: 'Short', type: 'string', icon: '' },
      { name: 'Color', value: 'White', type: 'string', icon: '' },
    ],
  },
]

PS: Application works but I'm afraid about that warning. What can I do please like right way?

I will be glad for any advice. Thank you very much.

Upvotes: 104

Views: 136858

Answers (10)

Kalnode
Kalnode

Reputation: 11296

For Vue slots, passed components' template needs an explicit wrapping

Sharing my experience with the same error as OP, but in the specific context of passing a "component-with-class" into a slot.

TLDR: Ultimately, the fix simply came from ensuring the passed component only have a single child within <template>.

Fails:

<template>
    ... (multiple elements)
</template>

Works:

<template>
    <div>
        ... (multiple elements)
    </div>
</template>

I'm still trying to find where this is covered in the Vue docs (it's a templates-and-slots issue) and whether there's a better and/or proscribed way.


DETAILS

ERROR (in browser)

[Vue warn]: Extraneous non-props attributes (class) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.

MyPage.vue

Note: The existence of class="icon" triggers the error in browser console. removing it clears the warning, but styles are lost.

<AComponentWithSlots>
    <template #SlotA>
        <YetSomeOtherComponent name="Foo111" class="icon" />
    </template>
    <template #SlotB>
        <YetSomeOtherComponent name="Foo222" class="icon" />
    </template>
</AComponentWithSlots>

AComponentWithSlots.vue

<template>
    <div>
        <div><slot name="SlotA">default content slot A</slot></div>
        <div><slot name="SlotB">default content slot B</slot></div>
    </div>
</template>

MyComponent.vue

<template>
    <YetSomeOtherComponent />
</template>

TRIALS

Removing class="icon" cleared the warning, but I no longer had the styling I wanted.

Just for fun, from sneaking suspicion of past experience, added a proper <div></div> wrapping of the template in MyComponent.vue and voila everything is fixed.

SOLUTION

Wrap template with a <div></div>. I suppose this provides explicit content ready for rendering via the slot regardless of what comes out of <YetSomeOtherComponent />.

While it's convenient to have "one-less-div" wrapping, here this is a case of better to be conservative and have the extra <div></div> especially with the "really-dynamic" context of slots.

MyComponent.vue

<template>
    <div> <!-- <<< Added this div wrapping -->
        <YetSomeOtherComponent />
    </div>
</template>

Upvotes: 3

quippe
quippe

Reputation: 373

In new version of vue 3.3+ you can set it like that

<script setup>
  defineOptions({ inheritAttrs: false })
</script>

<template>
  <h1>Title</h1>
  <main v-bind="$attrs">Content</main>
  <footer>Footer</footer>
</template>

Upvotes: 9

V. Sambor
V. Sambor

Reputation: 13389

Another way to avoid this warning is by adding the class dynamically, like this:

<my-awesome-component 
    ref="myComponentEl"
/>

...

myComponentEl.classList.add('awesome-class');

Upvotes: 0

shasi kanth
shasi kanth

Reputation: 7094

You can add defineProps to the script setup code, to get rid of this warning:

const props = defineProps(['class'])

Upvotes: 2

Zelig880
Zelig880

Reputation: 638

Michael answer is correct, but many people still fall for this error as it is triggered by some extra scenarios (edge cases) that are not covered by the documentation or not very intuitive.

The problem Just to recap, the error is triggered because we are passing an attribute (eg. disabled) and the vue component we are planning to render has 2 or more elements in its root. (this is well explained by Michael).

The edge cases:

  • This error is also triggered if you have an if/else statement
  • This error is also triggered if you have a comment alongside your root element
  • If you use teleport or transition, the root element is the actual HTML element used (so if you have 2 elements in a teleport or transition block this error will trigger).

I have covered this error in details here: https://zelig880.com/how-to-solve-extraneous-non-props-attributes-in-vue-js

Upvotes: 6

Michal Lev&#253;
Michal Lev&#253;

Reputation: 37793

Well I think the error message is pretty clear.

Your ItemProperties.vue component is rendering fragments - because it is rendering multiple <div> elements using v-for. Which means there is no single root element.

At the same time, you are passing a class to the component with <ItemProperties class="infobox-item-properties" - class can be placed on HTML elements only. If you place it on Vue component, Vue tries to place it on the root element of the content the component is rendering. But because the content your component is rendering has no root element, Vue does not know where to put it...

To remove the warning either remove the class="infobox-item-properties" or wrap the content of ItemProperties to a single <div>.

The mechanism described above is called Fallthrough Attributes ("Non-prop attributes" Vue 2 docs). It is good to know that this automatic inheritance can be switched off which allows you to apply those attributes by yourself on the element (or component) you choose besides the root element (doing so also eliminates the error message).

This can be very useful. Most notably when designing specialized wrappers around standard HTML elements (like input or button) or some library component - you can tell "This is a button - you can put anything that standard <button> accepts and it will work" without redefining all standard <button> attributes as props of your wrapper

Upvotes: 141

pegiadise
pegiadise

Reputation: 689

This could also be triggered from parent components that have props: true in their route definition. Make sure that you add props: true only in the components that you actually need it and have some route params as props.

Upvotes: 2

fsarter
fsarter

Reputation: 942

You are passing a class attribute to ItemProperties without declaring it.

Declare class in props options api should solve this issue.

ItemProperties.vue

...
export default {
  props:["class"],
  ...
}

Upvotes: 1

Per
Per

Reputation: 381

You could also prevent passing down attributes in child components by doing this:

export default defineComponent({
  name: "ChildComponentName",
  inheritAttrs: false // This..
})

Source: https://vuejs.org/guide/components/attrs.html

Upvotes: 14

tony19
tony19

Reputation: 138246

The ItemProperties component has multiple root nodes because it renders a list in the root with v-for.

Based on the class name (infobox-item-properties), I think you want the class to be applied to a container element, so a simple solution is to just add that element (e.g., a div) in your component at the root:

// ItemProperties.vue
<template>
  <div>
    <section v-for="(item, index) in items" :key="index" class="box">
    ...
    </section>
  </div>
</template>

demo

Upvotes: 20

Related Questions