Tutorial
Functional Components in Vue.js
While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.
Functional components are very popular in the React community. They allow to create simple components in a neat way just by passing a context, making them very simple to reason about.
What’s a Functional Component
You can think of a functional component to be the equivalent of a function, brought to the component world. That said, it’s a component that takes a render context and returns the rendered HTML.
A functional component is:
- Stateless: it doesn’t keep any state by itself
- Instanceless: it has no instance, thus no
this
By its nature, two of its use cases are presentational and higher order components.
Creating a Functional Component
To define a functional component, you must create an object with a functional: true
property and a render function. Let’s create an example of a FunctionalButton component:
export default {
functional: true,
render(createElement, context) {
return createElement('button', 'Click me');
}
};
Render Context
The context
argument you see on the render
function is known as the render context. It’s an object containing the following properties:
-
props
-
children
-
slots
: function returning a slots object -
parent
-
listeners
-
injections
-
data
: an object with contains all the previous properties
Let’s try some out. Say you have an App.vue component where you import FunctionalButton.js to use it in its template:
<template>
<FunctionalButton>
Click me
</FunctionalButton>
</template>
That way it’s possible now to use children
property instead of the hardcoded ‘Click me’:
export default {
functional: true,
render(createElement, { children }) {
return createElement("button", children);
}
};
Passing Props
Let’s have more fun with this component; what’s a button without a disabled
property?
<template>
<FunctionalButton>
Click me
</FunctionalButton>
</template>
export default {
functional: true,
render(createElement, { props, children }) {
return createElement('button', { attrs: props }, children);
}
};
Keep in mind that button is an html tag, not a component, so props
would have no effect. Thus why the use of the attrs
property from the createElement
arguments, which gives a slightly different structure to the render context.
Trigger Events
Given the fact that functional components have no instance, the event listeners come from the parent on the context.listeners
property.
With that in mind, you can implement a click
event like so:
<template>
<FunctionalButton @click="log">
Click me
</FunctionalButton>
</template>
export default {
functional: true,
render(createElement, { props, listeners, children }) {
return createElement(
'button',
{
attrs: props,
on: {
click: listeners.click
}
},
children
);
}
};
All Together
Remember that data
includes all the createElement
context properties, so we can rewrite the FunctionalButton this way:
export default {
functional: true,
render(createElement, { data, children }) {
return createElement( 'button', data, children );
}
};
Think how easy it would be to create a wrapper component, also known as Higher Order Component (HOC), that adds something to a component. For example, we could prepend “Hello ” to the button text:
createElement('button', data, ['Hello ', ...children]);
Wrapping Up
You’ve seen what functional components are and what role they play. Here I’ve been using the render function syntax in order to show the lower-level details, but we’ll explore other ways to render functional components in future articles.
Find all the code from this Button example in this Sandbox