Skip to content

Functional Components
breaking

Overview

In terms of what has changed, at a high level:

  • Performance gains from 2.x for functional components are now negligible in 3.x, so we recommend just using stateful components
  • Functional components can only be created using a plain function that receives props and context (i.e., slots, attrs, emit)
  • BREAKING: functional attribute on single-file component (SFC) <template> is removed
  • BREAKING: { functional: true } option in components created by functions is removed

For more information, read on!

Introduction

In Vue 2, functional components had two primary use cases:

  • as a performance optimization, because they initialized much faster than stateful components
  • to return multiple root nodes

However, in Vue 3, the performance of stateful components has improved to the point that the difference is negligible. In addition, stateful components now also include the ability to return multiple root nodes.

As a result, the only remaining use case for functional components is simple components, such as a component to create a dynamic heading. Otherwise, it is recommended to use stateful components as you normally would.

2.x Syntax

Using the <dynamic-heading> component, which is responsible for rendering out the appropriate heading (i.e., h1, h2, h3, etc.), this could have been written as a single-file component in 2.x as:

js
// Vue 2 Functional Component Example
export default {
  functional: true,
  props: ['level'],
  render(h, { props, data, children }) {
    return h(`h${props.level}`, data, children)
  }
}

Or, for those who preferred the <template> in a single-file component:

vue
<!-- Vue 2 Functional Component Example with <template> -->
<template functional>
  <component
    :is="`h${props.level}`"
    v-bind="attrs"
    v-on="listeners"
  />
</template>

<script>
export default {
  props: ['level']
}
</script>

3.x Syntax

Components Created by Functions

Now in Vue 3, all functional components are created with a plain function. In other words, there is no need to define the { functional: true } component option.

They will receive two arguments: props and context. The context argument is an object that contains a component's attrs, slots, and emit properties.

In addition, rather than implicitly provide h in a render function, h is now imported globally.

Using the previously mentioned example of a <dynamic-heading> component, here is how it looks now.

js
import { h } from 'vue'

const DynamicHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']

export default DynamicHeading

Single File Components (SFCs)

In 3.x, the performance difference between stateful and functional components has been drastically reduced and will be insignificant in most use cases. As a result, the migration path for developers using functional on SFCs is to remove the attribute and rename all references of props to $props and attrs to $attrs.

Using our <dynamic-heading> example from before, here is how it would look now.

vue
<template>
  <component
    v-bind:is="`h${$props.level}`"
    v-bind="$attrs"
  />
</template>

<script>
export default {
  props: ['level']
}
</script>

The main differences are that:

  1. functional attribute removed on <template>
  2. listeners are now passed as part of $attrs and can be removed

Next Steps

For more information on the usage of the new functional components and the changes to render functions in general, see: