Meituan-Dianping/mpvue · warning

<transition> can only be used on a single element. Use <tran

Error message

<transition> can only be used on a single element. Use <transition-group> for lists.

What it means

<transition> animates exactly one element at a time by toggling classes on `children[0]`. When its slot renders more than one child, Vue cannot decide which element to animate, so in development it warns that transition can only be used on a single element and suggests <transition-group> for lists. The render still proceeds using the first child, but additional children are not animated and behavior is undefined.

Source

Thrown at src/platforms/web/runtime/components/transition.js:99

  props: transitionProps,
  abstract: true,

  render (h: Function) {
    let children: ?Array<VNode> = this.$options._renderChildren
    if (!children) {
      return
    }

    // filter out text nodes (possible whitespaces)
    children = children.filter((c: VNode) => c.tag || isAsyncPlaceholder(c))
    /* istanbul ignore if */
    if (!children.length) {
      return
    }

    // warn multiple elements
    if (process.env.NODE_ENV !== 'production' && children.length > 1) {
      warn(
        '<transition> can only be used on a single element. Use ' +
        '<transition-group> for lists.',
        this.$parent
      )
    }

    const mode: string = this.mode

    // warn invalid mode
    if (process.env.NODE_ENV !== 'production' &&
      mode && mode !== 'in-out' && mode !== 'out-in'
    ) {
      warn(
        'invalid <transition> mode: ' + mode,
        this.$parent
      )
    }

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Ensure the transition contains exactly one element: use v-if/v-else so only one renders at a time.
  2. If multiple items should animate independently, switch to <transition-group> with keyed children.
  3. Move any extra sibling elements outside the <transition> wrapper.
  4. Wrap multiple always-present elements into one container element inside the transition.

Example fix

// before
<transition name="fade">
  <div v-if="ok">A</div>
  <div v-else>B</div>
  <span>note</span>
</transition>
// after
<div>
  <transition name="fade">
    <div v-if="ok">A</div>
    <div v-else>B</div>
  </transition>
  <span>note</span>
</div>
Defensive patterns

Strategy: validation

Validate before calling

// check slot renders a single element before enabling transition
const childCount = document.querySelectorAll('#transition-wrapper > *').length
if (childCount > 1) console.warn('<transition> slot renders ' + childCount + ' elements; must be exactly 1')

Type guard

const hasSingleChild = children => Array.isArray(children) && children.length === 1

Prevention

When it happens

Trigger: Placing two or more sibling elements directly inside <transition>, e.g. `<transition><div v-if="a"/><div v-else/><div>extra</div></transition>`; v-for inside <transition>; a v-if/v-else-if/v-else chain with an extra unconditioned sibling.

Common situations: Adding a wrapper element or icon next to the toggled element inside transition; forgetting that comments/whitespace text nodes are filtered but extra elements are not; converting a v-show toggle into two v-if elements.

Related errors


AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02). Data as JSON: /api/errors/afa8b20081aee22f. Report an issue: GitHub.