Vue vs React: Which is the Best JavaScript Framework in 2021?

#
By Peter Tasker

React or Vue.js, which JavaScript framework should you choose? React powers WordPress’ new editor while Vue.js is a popular framework in the Laravel community.

In this post we’ll go over what these two popular frameworks are all about, some use cases and see how they’re similar and how they differ.

The Contenders

I would guess that many front-end web developers have heard of the open-source JavaScript UI framework developed at Facebook called React. It powers most of the Facebook website as well as Instagram. It’s also the JS framework used by Gutenberg – WordPress’ new editor.

React was a bit of change in the JavaScript world when it came out as it’s quite different from other at-the-time frameworks like jQuery, Backbone.js and Angular 1. The biggest difference is that React popularized a Virtual DOM (we’ll get into this later) and created a new syntax called JSX that allows developers to write HTML in JavaScript. WAT?

Vue.js is a similar web development tool – it’s a JavaScript framework that aims to tackle the same problems as React but in a different way. Vue.js uses a template system instead of JSX, arguably making it easier to integrate into existing web applications. Because the templates use regular HTML Vue.js can be integrated into existing code quite easily, without the need for a build step. Vue.js is also said to have a flatter learning curve as well – something that I’ve recently verified as I’m new to Vue.js. The other main thing to mention about Vue.js is that it’s development is not backed by a large corporation like Facebook.

Similarities

React and Vue.js have a lot in common as they’re both UI JavaScript frameworks focused solely on creating rich front-end experiences. Unlike earlier JavaScript frameworks that had ‘batteries included,’ both React and Vue.js are fairly barebones with functionality like routing and state management handled by separate frameworks.

Usage of a Virtual DOM

One of the biggest similarities between Vue.js and React is the usage of what’s called a ‘Virtual DOM’. A Virtual DOM is basically what it sounds like, a virtual representation of the DOM tree. It hinges on the concept that updating the real DOM frequently is computationally heavy. Updating JavaScript objects is relatively lightweight by comparison.

With a Virtual DOM, a JavaScript object representing the real DOM tree is created. Updates to any element are made in the Virtual DOM rather than the real DOM. When something is changed, a new Virtual DOM object is created and the changes between the old and new are determined. These changes are then applied to the real DOM.

For example let’s take this list in HTML:

<ul class="list">
  <li>item 1</li>
  <li>item 2</li>
</ul>

In JavaScript this could be represented simply as:

{
    type: 'ul', 
    props: {'class': 'list'}, 
    children: [
        { type: 'li', props: {}, children: ['item 1'] },
        { type: 'li', props: {}, children: ['item 2'] }
    ]
}

Actual Virtual DOM implementations are more complex than this, but they’re essentially a nested JavaScript object with nested arrays.

When a new item is added to this JavaScript object a function will ‘diff’ the changes and apply the new markup to the real DOM. This ‘diffing’ algorithm is the secret sauce, and both React and Vue.js do this a bit differently.

Vue.js is reported to be able to diff changes to the Virtual DOM more quickly as it keeps track of each component’s dependencies during render, not needing to re-render a whole component’s sub-tree.

With React, child components will all be refreshed each time application state is changed. This can be overridden by using the shouldComponentUpdate lifecycle method, but Vue.js handles this kind of optimization by default.

Component Based Architecture

Both React and Vue.js encourage a component based architecture. This essentially means separating your application into distinct chunks of related functionality with a defined way for each chunk to ‘talk’ to each other. A good explanation of what a component is can be found in this Medium article:

You can think of a component as a small feature that makes up a piece of the user interface. If I were to describe a component within the scope of Facebook’s UI, A chat window would be a component, a comment feed would be another component, and a constantly updating friend list would represent yet another component.

In Vue.js, you can use single file components that follow this principle.

//PastaItem.vue

<template>
<li class="pasta-dish list-unstyled">
    <div class="row">
        <div class="col-md-3">
            <img :src="this.item.image" :alt="this.item.name" />
        </div>
        <div class="col-md-9 text-left">
            <h3>{{this.item.name}}</h3>
            <p>
                {{this.item.desc}}
            </p>
            <button v-on:click="addToOrderNew" class="btn btn-primary">Add to order</button> <mark>{{this.orders}}</mark>
        </div>
    </div>
</li>
</template>

<script>

export default {
    name: 'pasta-item',
    props: ['item'],
    data:  function(){
        return{
            orders: 0
        }
    },
    methods: {
        addToOrderNew: function(y){
            this.orders += 1;
            this.$emit('order');
        }
    }
}

</script>

<style src="./Pasta.css"></style>

As you can see in the above example HTML, JavaScript and CSS are combined in one file. You don’t have to include CSS in your component .vue files, but it’s an option.

In React it’s very similar, with JavaScript and that fun JSX markup in a component file.

import React from "react";

class PastaItem extends React.Component {

    render() {
        const { details, index } = this.props;

        return (
            <li className="pasta-dish list-unstyled">
                <div className="row">
                    <div className="col-md-3">
                        <img src={details.image} alt={details.name} />
                    </div>
                    <div className="col-md-9 text-left">
                        <h3>{details.name}</h3>
                        <p>
                            {details.desc}
                        </p>
                        <button onClick={() => this.props.addToOrder(index)} className="btn btn-primary">Add to order</button> <mark>{this.props.orders || 0}</mark>
                    </div>
                </div>
            </li>
        );
    }
}

export default PastaItem;

Props

As in the above example, both React and Vue.js have a concept of ‘props’, which is short for properties. These are special attributes on an element that allow for the passing of data from parent to child.

Object.keys(this.state.pastadishes).map(key =>
    <PastaItem index={key} key={key} details={this.state.pastadishes[key]} addToOrder={this.addToOrder} orders={this.state.orders[key]} />
)

In the above JSX example the index, key, details, orders and addToOrder attributes are props passing data to the child PastaItem component.

In React this is necessary as it relies on a local ‘state’ (more on that later) that acts as a ‘single source of truth’.

In Vue.js, props are slightly different. They’re defined on a component in the same way, but since Vue.js relies on a template syntax – you have to use the built-in template loop functions.

<pasta-item v-for="(item, key) in samplePasta" :item="item" :key="key" @order="handleOrder(key)"></pasta-item>

It’s a little more on the ‘templatey’ side of things, but it gets the job done and I find it about as complex as the React version.

Build Tools

Both React and Vue.js have bootstrap applications that get you up and running quickly with your development environment. In React this is Create React App (CRA) and in Vue.js it’s vue-cli. In both cases you get a project template set up according to the latest best practices.

With CRA you’re a little more handcuffed as far as options. It’s an opinionated tool, forcing you to use Webpack and Babel out of the box. With vue-cli, you have options in the form of templates which makes it a little more flexible.

Chrome Devtools

Both React and Vue.js also have awesome Chrome extensions to help in debugging. They let you inspect your application as you would any other website, but they allow you to see the Vue.js or React version of the markup. You’re also able to see application state data and see updates happen in real time.

React devtools in action:

Vue.js devtools in action:

Companion Frameworks

One last similarity (and difference) between these two frameworks is how they handle companion frameworks. Both React and Vue.js are focused solely on the UI layer, and leave functionality such as routing and state handling to companion frameworks.

The difference between Vue.js and React is how they relate to their respective companion frameworks. The Vue.js core team maintains the vue-router and vuex frameworks and keeps them under the main Vue umbrella. React’s react-router and react-redux are maintained by community members and aren’t ‘officially’ under the Facebook/React umbrella.

Main Differences

While Vue.js and React have a lot in common, there are some major differences.

Templating vs JSX

The biggest difference between React and Vue.js is how templating is done. In Vue.js, you’re encouraged to use regular-old-HTML for templating. This approach leverages custom attributes on standard HTML elements.

<ul>
    <template v-for="item in items">
        <li>{{ item.msg }}</li>
        <li class="divider"></li>
    </template>
</ul>

The attributes can also be used on single file components, although it requires a build step to convert the component syntax to valid JavaScript and HTML.

<ul>
  <pasta-item v-for="(item, key) in samplePasta" :item="item" :key="key" @order="handleOrder(key)"></pasta-item>
</ul>

Vue.js encourages the use of HTML to render things, while it uses a familiar Angular-style method for outputting dynamic content with Mustache-style syntax. For this reason, Vue.js is arguably easier to integrate into existing applications as you can very simply incorporate Vue.js templates into existing templates without too much overhead. This also reportedly makes it easier for newcomers to adapt to this syntax.

React on the other hand recommends you write all your template code in JavaScript by using a ‘syntax extension to JavaScript’ called JSX. For example, the same code in JSX:

<ul className="pasta-list">
    {
        Object.keys(this.state.pastadishes).map(key =>
            <PastaItem index={key} key={key} details={this.state.pastadishes[key]} addToOrder={this.addToOrder} orders={this.state.orders[key]} />
        )
    }
</ul>

React/JSX definitely appears more verbose at first glance, but by adding the ability to use JavaScript within templates a lot more power is given to the developer.

But remember:

With great power comes great responsibility. Ben Parker

JSX is really just JavaScript with some funny XML syntax. However, once you get used to it, it feels a lot more flexible and robust. This might be my own biases coming through, but I feel this is a better approach as I was never a fan of the Angular 1 style attribute mess.

The counter argument is that Vue.js’ template syntax removes the temptation to pile additional logic into your views/components, maintaining a separation of concerns.

It should also be mentioned that Vue.js does technically support render functions and JSX, like React, but they’re not the default approach.

State Management vs Object Properties

If you’re familiar with React you’ll know that application state is a key concept. There are even frameworks dedicated to managing large scale state objects like Redux. Additionally, state data in React applications is immutable, meaning that it can’t be changed directly (though this isn’t exactly true). In React you need to use the setState() method (or the useState() hook) to modify anything in the local state.

 addToOrder(key) {
        //Make a copy of this.state
        const orders = { ...this.state.orders };

        //update or add
        orders[ key ] = orders[ key ] + 1 || 1;
        this.setState( { orders } );
 }

With Vue.js a local state object isn’t required, and data is managed via the data property on the Vue object.

export default {
  name: 'app',
  data() {
    return {
      samplePasta: samplePasta,
      orders: {}
    }
  },
...
  methods: {
    handleOrder: function (key) {

      if (!this.orders.hasOwnProperty(key)) {
        this.$set(this.orders, key, { count: 0 });
      }

      this.orders[key].count += 1;
    }
  }
}

In Vue there’s no need to call a state management function like setState(), as the data parameter on the Vue object acts as the holder for application data.

On the topic of state management for large scale applications, Evan You, creator of Vue.js, has said that these kind of solutions are suitable for small scale applications, but are not scalable for larger applications.

In most cases, the built-in state management patterns provided by the frameworks themselves are insufficient for large scale apps, and a dedicated solution like Redux or Vuex must be used.

With that in mind, arguing about how state is managed in your application is most likely a premature optimization, and, as with most things, it’s a matter of personal preference. Besides, you might not even need to worry about it.

React Hooks

One of the bigger updates to React in 2019 was the addition of Hooks in React 16.8. Hooks are a big deal because they introduced stateful functional components. This means that you no longer have to use the ES2015+ class syntax to create components and you can use the much less verbose functional syntax.

For example, here’s the minimum amount of code to create a component using the class syntax:

class MyComponent extends React.Component {
    constructor() {
        super();
        this.state = {
            someStateValue: true
        };
    }

    handleClick = event => {
        this.setState({
            someStateValue: !this.state.someStateValue
        });
    };

    render(){
        return (
        <>
            <p>React Class test</p>
            {`state: ${this.state.someStateValue}`}
            <p>
            <button onClick={()=> this.handleClick()}>Toggle state</button>
            </p>
        </>
        );
    }
}

ReactDOM.render(
    <MyComponent />,
    document.getElementById('root')
);

And with hooks:

const MyComponent = props => {
    const [someStateValue, setSomeState] = React.useState(true);

    return (
        <>
            <p>React hooks test</p>
            {`state: ${someStateValue}`}
            <p>
                <button onClick={() => setSomeState(!someStateValue)}>
                Toggle state
                </button>
            </p>
        </>
    );
};

ReactDOM.render(<MyComponent />, document.getElementById("root"));

As you can see, with the functional syntax, not only is there less overall code, but it’s much easier to see what’s going on. The biggest addition in hooks is the addition of new built-in hooks like useState() and useEffect().

useState() is the Hooks method to manage state in a component. Rather than use this.setState(), you destructure two variables from the useState() method. The first variable is the state property and the second variable is the ‘setter’ function.

For example, in the above code we have const [someStateValue, setSomeState] = React.useState(true);. someStateValue is the state value we’ll use in our component and setSomeState is actually a function we’ll call when we want to update our state.

See the Pen React Hooks example by Peter (@tasker82) on CodePen.

The other big enhancement with hooks is that class ‘lifecycle’ methods are replaced with one useEffect() hook. It handles all the ‘componentDidMount(), componentWillUnmount() etc.’ jank.

As per the React docs, previously if you want to do something when a component ‘mounted’ (was added to the DOM), and when a component was updated, you would have to use the two different methods:

componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;   
}

componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
}   

With the useEffect() hook, you have one function that handles both cases:

useEffect(() => {
    document.title = `You clicked ${count} times`;
});

The useEffect hook is a lot more powerful than this simple example, with support for running actions when components unmount (replacing componentWillUnmount) as well as limiting effects to only run when certain values don’t change between renders.

The other neat part of hooks is that you can create your own custom hooks. This allows you to ‘extract component logic into reusable functions’. What this means is that you can reuse state and lifecycle management code throughout your app, just like reusing components.

Vue 3 – Composition API

So does Vue.js have anything like React’s hooks? Vue 2.x doesn’t but Vue 3 is right around the corner. One of the new features in version 3 is something called the Composition API. Before we go any further, I should mention that Vue 3 is in the ‘pre-alpha’ phase, so some of this may change once version 3 is officially released.

Some of the reasons Hooks were added to React are valid for Vue.js as well. Vue.js typically has a simpler API, separating out data and computed properties. What can happen in large applications is that this simpler interface can make it difficult to create reusable code. Developers can end up creating massive components that are hard to reason about – just like React classes!

Let’s take a look at some Vue 2 code:

<template>
    <div>
        <h3>Vue 2 test</h3>
        <h3>Hungry? {{ hungry }}</h3>
        <h3>Eating? {{ setEating }}</h3>
        <button v-on:click="updateEating">Toggle</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            hungry: true
        };
    },
    computed: {
        setEating: function() {
            if (!this.hungry) {
                return true;
            }
            return false;
        }
    },
    methods: {
        updateEating: function() {
            return (this.hungry = !this.hungry);
        }
    }
};
</script>

In this super simple example, you can see how we’ve got three distinct methods to keep track of: the data(), computed() and methods(). We’re also sort-of relying on the pseudo-class based interface syntax by referencing instance variables using the this keyword. In version 3 of Vue.js, all this data can be colocated in the same method.

Note: To use the Vue 3 Composition API you need to add the @vue/composition-api package and add it to your Vue.js project.

<template>
<div>
    <h3>Vue 3 test</h3>
    <h3>Hungry? {{ hungryState.hungry }}</h3>
    <h3>Eating? {{ setEating }}</h3>
    <button v-on:click="updateEating">Toggle</button>
</div>
</template>

<script>
import { reactive, computed } from '@vue/composition-api'

export default {
    setup() {
        // Pet name
        const hungryState = reactive(
            {
                hungry: false
            }
        );

        const setEating = computed(() => {
            if (!hungryState.hungry) {
                return true;
            }
            return false;
        });

        const updateEating = () =>{
            return (hungryState.hungry = !hungryState.hungry);
        }

        // All properties we can bind to in our template
        return {
            hungryState,
            setEating,
            updateEating
        };
    }
};
</script>

In the above contrived example we see how things can be combined into one setup() method. This allows for a more tightly coupled set of code and simplifies reusability a bit more. In the Vue 3 RFC for the Composition API, there are two main reasons this organization method:

  1. The code of complex components becomes harder to reason about as features grow over time. This happens particularly when developers are reading code they did not write themselves. The root cause is that Vue.js’ existing API forces code organization by options, but in some cases it makes more sense to organize code by logical concerns.

  2. Lack of a clean and cost-free mechanism for extracting and reusing logic between multiple components.

Like React Hooks, the Vue 3 Composition API is opt-in, with the legacy Options API still supported.

Static Site Generators

The next biggest thing to come into the React world in the last couple of years is static site generators, in particular Gatsby. It seems that out of nowhere JS-based static site generators have become a big deal – this is especially true of Gatsby.

If you’re not familiar with Gatsby, it’s essentially a build system that relies on React and GraphQL to create static HTML files. I wrote about Gatsby previously, and I wondered if Vue had a similar tool. Turns out, it does – Gridsome. I haven’t had the chance to try out Gridsome, but it looks like a Vue flavored Gatsby. Just like Gatsby, you can specify multiple content sources (APIs, headless CMS’), use GraphQL to define content queries, write content in Markdown, and build new sites from a premade starter template.

Judging by the feature list of Gridsome, it appears that it has just about reached feature parity with Gatsby. The biggest issue I see with something like Gridsome is overcoming the popularity of Gatsby. Judging entirely by the superficial metric of Github stars, Gridsome has 5.6k while Gatsby has ~9x that with 42.6k. This gets into the next point – market share.

Market Share

In 2020, few would argue that React is the top JavaScript framework in terms of popularity. Good or bad, it seems React is here to stay. Because of its popularity, there is plenty of documentation and tutorials for React available online.

A side-effect of a large market share is development speed. React shipped React Hooks in Feb 2019, while Vue 3 is in the early alpha phase as of writing this post. Now, I hear you say “But React is backed by Facebook, a huge corporation!?!” This is true, so there may indeed be more people working on React, speeding up development.

All that to say, React will continue to dominate the front-end framework landscape for the time being. This is especially true for the WordPress editor as it’s built entirely with React and related frameworks.

With this popularity, it’s also worth talking about jobs and career prospects. React jobs seem to be more in demand, pay the most, and seems to hold a large chunk of developer mind-share. But as with everything, it changes over time 🙃. All that said, career growth is something worth thinking about if you’re considering picking up either React or Vue.js.

Bottom Line

So there you have it, a ‘sort-of-thorough’ overview of React and Vue.js. There are plenty of things not discussed here, but this should give you a pretty good starting point to get up to speed with either framework.

I do think however, now more than ever React is the most popular front-end framework out there. The growing fanfare around Gatsby as well as React ruling mobile (React Native and desktop (Electron) only highlights this popularity.

Vue.js seems to be taking a lot of the concepts developed in React and refining them and making them better. Some would argue Vue.js has a less steep learning curve. It seems to be very popular in the Laravel community, and I think the release of Vue 3 will be a big deal for Vue.js fans. I would argue that Vue.js has a strong community, it’s just not at the same level as React.

We are also starting to see new frameworks that take what works in React and Vue.js and make them better. One example is Svelte. Svelte doesn’t use virtual DOM diffing to make updates to the UI, but ‘…writes code that surgically updates the DOM when the state of your app changes.” I think Svelte is something to keep an eye on as devs like myself are becoming allergic to complexity!

Calling all React developers and Vue developers, what do you think are the best features of React and Vue.js? Which one do you prefer? Let us know in the comments.

About the Author

Peter Tasker

Peter is a PHP and JavaScript developer from Ottawa, Ontario, Canada. In a previous life he worked for marketing and public relations agencies. Love's WordPress, dislikes FTP.