DEV Community

Frederik Dietz
Frederik Dietz

Posted on

8 1

Vue.js Functional Components

In the previous article we looked into headless components and how to achieve maximum flexiblity by separating the logic from the rendering. In this article we focus on functional components.

In most Vue.js apps there are a lot of components which do not really do anything besides rendering a template. They do not contain any business logic or make use of the component lifecycle.

In this case using a functional component might remove some unnecessary boilerplate and the component renders faster too!

You can think of a functional component to be the equivalent of a function which takes a render context as input and returns rendered HTML.

In this chapter we explore how and when to use functional components and the pros and cons of them.

Functional components using the vue-cli and SFC

Let's start with a new default project created via the vue-cli, following the official guide
using the default setup.

It should generate an App.vue and a HelloWorld.vue file for you which we start modifying for our example.

The App.vue file imports the HelloWorld component which has a msg prop and a @click event. We use this event to increment a clickCount.

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App" @click="clickCount+=1">
      <p>I was clicked: {{clickCount}}</p>
    </HelloWorld>
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  name: "app",
  data() {
    return {
      clickCount: 0
    };
  },
  components: {
    HelloWorld
  }
};
</script>

The HelloWorld component consists of a template only:

<template functional>
  <div class="hello">
    <h1>{{ props.msg }}</h1>
    <button @click="listeners.click">Click me</button>
    <slot></slot>
  </div>
</template>

You can find the complete example on GitHub

Note, that the template has an additional functional attribute. This flag turns the component into a functional component. Additionally, Vue.js changes how you access the context of the component. Where you previously accessed props like msg directly, you now need to use prop.msg instead and events via listeners.click.

All these changes in usage are necessary since a functional component has no instance or state and therefore no this or data.

If you need to create lots of small mainly visual components, as for example a Heading, functional components make a lot of sense.

Functional components using Vue.component and render function

There is another way of using functional components using the Vue.component function:

Vue.component("hello-world", {
  // leanpub-start-insert
  functional: true,
  // leanpub-end-insert
  render(createElement, {data, listeners, slots}) {
    return createElement("div", { class: "hello" }, [
      createElement('h2', data.attrs.msg),
      createElement('button', {
        on: {
          click: listeners.click
        }
      }, 'Click me'),
      slots().default
    ]);
  }
});

You can find the complete example on GitHub

The functional attribute is used together with a render function. We've looked into these render functions before in the previous chapter about headless components.

Every argument passed along to the render function is what we call the render context. It includes data, listeners, props, slots, parent, injections, etc. In our example we used JavaScript destructuring to only pick what we need in our function. You can read more background about render functions in the official Vue.js guide.

Compared to the first example using SFC it looks like a lot of boilerplate code. But, this can be much cleaner when using JSX instead.

Functional components using Vue.component and JSX

In order to use JSX we recommend to use the vue-cli with the default setup from the first example. It supports JSX out of the box - no configuration required!

Let's have a look how our component looks like now:

<script>
export default {
  name: "HelloWorld",
  functional: true,
  render(h, { data, listeners, children }) {
    return (
      <div class="hello">
        <h1>{data.attrs.msg}</h1>
        <button onClick={listeners.click}>Click me</button>
        {children}
      </div>
    );
  }
};
</script>

You can find the complete example on GitHub

Since we use a SFC component again we use a script tag for the JavaScript code. The functional attribute together with the render function is used again, but this time the render implementation is using JSX syntax.

When compared to normal Vue.js templates we use single curly braces instead of the mustache syntax and for events we use onClick instead of v-on:click. But, this is only scratching the surface here. The interesting thing about JSX is that everything in these curly braces is all JavaScript and converted to JavaScript functions via the h argument.

Here's an example of rendering a list in JSX:

const listItems = props.numbers.map(number =>
  <li>{number}</li>
);
return (
  <ul>{listItems}</ul>
);

More on JSX syntax in the Vue.js guide.

Summary

I'm not recommending to always use JSX now, but it certainly has it strengths for some use cases and it is therefore beneficial to know the limitations of the Vue.js template language and the pros and cons of JSX compared to that.

I personally favor using Vue.js templates for almost all components. The only reason for me to use JSX is when dealing with very dynamic component creation where the number of v-if and v-else makes the code less readable.

Image of Datadog

Master Mobile Monitoring for iOS Apps

Monitor your app’s health with real-time insights into crash-free rates, start times, and more. Optimize performance and prevent user churn by addressing critical issues like app hangs, and ANRs. Learn how to keep your iOS app running smoothly across all devices by downloading this eBook.

Get The eBook

Top comments (0)

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay