Why I Prefer React

A journey from Vue to React and back again.

If you Google “React vs Vue” there is no shortage of articles on the subject. Everyone and their cousin has an opinion on which JavaScript framework is better.

So what unique perspective do I have to offer?

Well, I’m going to review these frameworks based on how they make me feel – as I learn, grow, and mature alongside each of them.

Over the past five years, I’ve built and maintained production apps using both React and Vue. I’ve gone through the honeymoon phase where I’ve truly believed a new framework would solve all my problems. I’ve persevered through trial and error and hundreds of documentation tabs open all at once. I’ve refactored large chunks of codebases. I’ve installed Vuex, wrestled with Redux, incrementally adopted TypeScript, embraced hooks, migrated code from React to Vue – and back again. I really feel like I’ve done it all.

These were my impressions along the way.

First Impressions

Vue

Vue.js home page
Vue.js home page

Wow! This site is beautiful. If the quality of this framework matches the quality of the marketing site I am prepared to be blown away. They even put the word “Approachable” on here? Sign me up!

React

React.js home page
React.js home page

This definitely looks like a tool exclusively for programmers and I am a designer so maybe this isn’t for me? Very minimal approach to design. What does declarative mean? Everyone talks about it and Facebook made it so ¯_(ツ)_/¯

The Honeymoon Phase

Vue

Vue.js docs
Vue.js docs

These docs are so easy to follow. I installed the entire framework by copy / pasting a script tag. I’ve now scrolled just partway down the intro page and am already learning how to render content using an English-like template syntax. I have a solid grasp of what’s going on. Feeling empowered.

React

React.js docs
React.js docs

INTRODUCING JSX! The thing you need to learn before you learn the thing you came to learn. It’s neither a string nor HTML. You will learn to love typing curly braces.

Googles “alternatives to React” and ends up trying Vue instead.

The First Component

This app design calls for a bunch of boxes with padding. Seems like a great use-case for one of those newfangled components I’ve heard about.

Vue

Vue.js single file component
Vue.js single file component

Single file components are incredible! This brings me right back to the bad old days of writing HTML, JavaScript, and CSS in separate files. Having them clearly separated in a single file makes me feel at-home in this ecosystem. It just works.

React

React.js component
React.js component

Components can be expressed as either functions or classes. Wait… JavaScript has classes? Since when? Since Babel? Are ES6 and ES2015 the same thing? Hmmm 🤔

Well… just tell me which one is better?

Oh, they’re “equivalent from React’s point of view”?

Ok. I guess I will buy a book about JavaScript classes and design patterns first.

Hates reading books so this does not happen.

Conditional Rendering

Alrighty! I somehow got both local dev environments up and running. Testing out conditional statements seems like an easy way to take these frameworks for a spin. Let’s see how it goes.

Vue

<div>
  <h1 v-if="awesome">Vue is awesome!</h1>
</div>

Mmhmm. I instantly get it. I know exactly what’s going on here. Don’t even need to read the docs. Onward.

React

render() {
  const awesome = true
  return (
    <div>
      {awesome && <h1>React is hard!?</h1>}
    </div>
  )
}

Ampersands and curly brackets, eh? Feels kinda yucky compared to how clean this very same thing was to do in Vue. Also my code just broke.

Oh! According to the docs, those curly braces are actually JSX, so I need to change my file extension from .js to .jsx.

Working — but not totally sure what I did to deserve this.

Looping Over Data

Next up I have some data stored in a JSON file and I need to display it neatly in an unordered list. How does this work in each framework?

Vue

<ul>
  <li v-for="item in items">{{ item.message }}</li>
</ul>

Ok ok ok! I think I know what’s happening here. Coming from Ruby, item in items feels familiar. I like how this feels like HTML with some fancy Vue directives sprinkled in.

React

render() {
  return (
    <ul>
      {this.state.items.map((item, index) => (
        <li>{item.message}</li>
      ))}
    </ul>
  )
}

Time for a refresher on higher-order functions. Since React does not have directives, looks like I’ll be using a plain ‘ol map with a whole bunch of parentheses and curly braces.

JSX is also mixed up in this somehow? Ugggg. Why would anyone use this framework!?

The First Component With Local State

Let’s move on to some more interesting things: a component with local state.

Vue

<template>
  <div id="app">
    <p>{% raw %}{{ message }}{% endraw %}</p>
  </div>
</template>

<script>
  var app = new Vue({
    el: "#app",
    data: {
      message: "Hello Vue!",
    },
  })
</script>

Things are definitely getting more complex here. Looks like Vue has a strict way of structuring things in the <script> section. A JSON-like syntax? A bit magical, but easy to follow. Overall, I don’t mind it.

React

class HelloWorld extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      message: "Hello React!",
    }
  }
  render() {
    return (
      <div>
        <p>{this.state.message}</p>
      </div>
    )
  }
}

I guess most of the examples in the docs actually show class-based implementations? That must mean class-based components are the best approach? Or maybe just the best when you have state to deal with? Weird because I’ve heard functional components are the future.

The use of a constructor here is a bit obtuse, but aside from that I have a solid understanding of what’s happening. Typing out this.state.message is verbose, but I appreciate that it’s plain old JavaScript. No wizardry. I’m a fan.

This render method is a bit annoying compared to Vue’s templates, but I suppose that’s a fair tradeoff React made to do things in a Javascript-y way.

The First Component With Events

Time to make a button that trades Bitcoins…

Vue

<template>
  <div id="counter">
    <button v-on:click="increment">Add 1</button>
    <p>You have {% raw %}{{ counter }}{% endraw %} Bitcoins.</p>
  </div>
</template>
<script>
  var counter = new Vue({
    el: "#counter",
    data: {
      counter: 0,
    },
    methods: {
      increment: function () {
        this.counter += 1
      },
    },
  })
</script>

Simple enough. A lot of syntactical sugar. I already know I’m going to forget where the dash and colon go in v-on:click. Good thing the docs are good… I will definitely be back.

And now I’m writing a click handler inside of this funky JSON structure “methods” thingy? Ok, seems to work well for now.

React

class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = { counter: 0 }
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick() {
    this.setState((state) => ({
      counter: state.counter + 1,
    }))
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Add 1</button>
        <p>You have {this.state.counter} Bitcoins.</p>
      </div>
    )
  }
}

Well, this is familiar. I can use onClick. The same onClick that’s been part of JavaScript since the days of Netscape. And the event handler is just a function?

Pretty cool. Starting to feel very in control of my code.

There’s a bit more code here, but I am starting to feel powerful. This must be what they meant when they were talking about React being “declarative”!?

Passing Props To Components

Cool so this is where the power of components really starts to shine, right?

Vue

Vue.component('box', {
  // camelCase in JavaScript
  props: ['boxLabel'],
  template: '<div>{{ boxLabel }}</div>'
})

<box box-label="hello!"/>

Simple enough. I can modify the box component by passing a boxLabel prop. A bit weird how the prop name is camelCase in JS and kebab-case in HTML, but I’ll get used to it.

React

function Box(props) {
  return <div>{props.boxLabel}</div>
}

const element = <Box boxLabel="hello!" />

Looks like this example is for a functional component. Are they better? Who knows…

Let’s roll with it. Components-as-functions actually make a lot of sense now that I think about it. The data flow from the boxLabel prop to the functional component is clear and easy to follow. Everything is camelCase, too.

+1 React.

Styling a Compoent

Ooops I made a box but I didn’t give it padding. Time for some CSS…

Vue

<template>
  <div id="box" class="box">{% raw %}{{ content }}{% endraw %}</div>
</template>

<script>
var box = new Vue({
  el: "#box",
  data: {
    content: "Hello Vue!",
  },
})
</script>

<style>
.box {
  padding: 20px;
}
</style>

Single-file components to the rescue! I can add a <style> tag, write some CSS, and some Webpack magic takes care of the rest behind the scenes. I really appreciate how Vue has a default way of handling CSS. No inline styles here. Very nice.

React

const boxStyle = {
  padding: 20,
}

function Box() {
  return (
    <div style={boxStyle} className="box">
      Hello World!
    </div>
  )
}

Oooof. It seems like the default approach is… inline styles? The lack of separation between between markup and styles is definitely making me uncomfortable as someone who has been building the internet for 15+ years. Maybe I’m old. No hover state support or pseudo-selectors either. Oh, by the way, class has been renamed to className.

Styling just got a lot more complex I guess.

But wait! I can choose a CSS-in-JS library to help! 😀 And read through / compare 20 sets of docs 🤮

I’ve heard a lot about Styled Components, but it seems like Emotion is the framework-du-jour? Or maybe Styled JSX? Wait… bitcoinBro69 on Hacker News says “every CSS-in-JS library sucks except Aphrodite”. Ummm… what in the holy hell is Aphrodite? It does… SERVER SIDE RENDERING? I thought this was CSS! Helppppppp

The First Complex Component

Passing props was rather simple, but now I need to make a parent component aware of an event that occurred in one of its children. Not sure how that will work since props can only flow from parent to child.

Vue

<!-- Child -->
<button v-on:click="$emit('enlarge-text', 20)">Enlarge Text</button>

<!-- Parent -->
<blog-post v-on:enlarge-text="fontSize = $event" />

So I guess there’s this thing called $emit that allows a child to emit a value to a parent.

Nope! Do not want. Way too much syntactical sugar. What does the dollar sign do? And what wizardry is hiding behind emit? This feels very brittle. Not a fan.

React

class BlogPost extends Component {
  constructor(props) {
    super(props)
    this.state = { fontSize: 16 }
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick() {
    this.setState((state) => ({
      fontSize: 20,
    }))
  }
  render() {
    return <button onClick={this.handleClick}>Enlage Text</button>
  }
}

Ummm this is kind of amazing. React will let me pass a function as a prop. This eliminates the need for emitting a value from the child component altogether.

React’s Just JavaScript™ approach is really starting to pay dividends here. I can feel myself starting to pull away from Vue and moving towards React. That CSS situation though…

The First Component With Global State

Passing a function as a prop was nice, but now things are getting really complicated. The app design calls for keeping track of a user’s session and making edits to their account details from several different components. I need a way to store global state.

Vue

Vuex
Vuex

Looks like I need to install another library, Vuex, but I appreciate that it comes with a strong recommendation and is compatible out-of-the-box.

This graphic is helpful but there are a lot of new concepts here. Dispatch, action, and mutations. Sounds complex. Looks like I have some learning to do. At least I feel confident that it will work.

React

React
React

Redux! Flux! Roll-your-own-state-machine. There are a lot of options and none of them are simple. Looks like I’m about to take a deep dive into immutable state, stores, and reducers.

It seems like global state can get quite complex in any framework (even in Vue), but the lack of a recommended first-party state management solution for React does make me feel a bit uneasy.

Time to call it a night.

When You Inevitably Add TypeScript

Vue

const Component = defineComponent({
  data() {
    return {
      count: 0,
    }
  },
  mounted() {
    const result = this.count.split("") // => Property 'split' does not exist on type 'number'
  },
})

Vue 3.0 has built-in TypeScript support. Unfortunately, there’s still a lot of magic happening here. Instead of writing out types and interfaces by hand, Vue often infers types. For example, the count defined in data is not explicitly typed. Instead, Vue infers a number type and throws an error later on when I try to call a string-specific method on it. Since things are inferred so often, the TypeScript version of Vue often doesn’t look too different from the regular variety.

It’s also possible, however, to write more explicit types like this:

interface Book {
  title: string
  author: string
  year: number
}

const Component = defineComponent({
  data() {
    return {
      book: {
        title: "Vue 3 Guide",
        author: "Vue Team",
        year: 2020,
      } as Book,
    }
  },
})

But… I’d prefer not to have assertions like return {...} as Book all over the place. I’d prefer a more declarative approach.

React

npm install --save-dev typescript

Hmmm… not much in the docs about TypeScript other than a reminder that I can install TypeScript via NPM. Also looks like React requires a .tsx extension for files that use both TypeScript and JSX.

Guess I’m going to have rely on everything I’ve learned so far and the hundreds of helpful resources that pop up when I Google “React TypeScript”.

I found this example in my search results:

type CardProps = {
  title: string
  paragraph: string
}

export const Card: FunctionComponent<CardProps> = ({ title, paragraph }) => {
  return (
    <div>
      <h2>{title}</h2>
      <p>{paragraph}</p>
    </div>
  )
}

Since React is Just JavaScript™, TypeScript integration works really well. Everything is generally declarative and types are rarely inferred like they are in Vue. TypeScript works even better with function components where I can use the generic type provided by the official React typings. I can even use object destructuring to make the props easily accessible in my component. While it may be slightly more work than Vue to set up, I appreciate how clear the React approach is.

In Retrospect

What a journey! Thanks for riding along with me.

While I could certainly continue comparing and contrasting these two frameworks, that is not the main point of this piece.

What I’m trying to illustrate is that working with new technologies is always a journey. We often come to frameworks with preconceived notions of what they are and whether we’ll like working with them.

First impressions are important, sure, but what ultimately matters is how expressive a framework empowers us to be over time.

Based on my first impressions, I would have initially said Vue was the clear winner. It was the framework that clicked fastest and made sense for my brain.

But these days when I write Vue code, I feel like I am limited to a small subset of the English language. The syntactical sugar that made Vue easy to adopt in the beginning ultimately made it more challenging to extend and evolve over time. At some point, the layers of abstraction started to feel a little too abstract.

These days when I write React, I feel like Hemingway writing a new novel. There are always 10 ways to do something, but at this point I’ve memorized my favorite approaches. And while I often don’t have all the words to express what I’m thinking, a quick Google search will typically help me find them.

One of my favorite aspects of React has been the way it has helped me improve as a JavaScript developer. React has forced me to dive deep into the fundamentals of the language in a way that Vue never did. If React were to suddenly disappear or be replaced, I’m confident the JavaScript paradigms it has taught me would be transferable. I feel all-in on JavaScript, not all-in on React.

If I were starting a new project tomorrow, I would definitely choose TypeScript and React over Vue. That said, if I needed a way to quickly prototype an interaction in code, Vue would be my first choice. I’ll always be grateful for Vue’s beginner-friendly onboarding and minimal syntax, but these days I simply feel most productive and empowered in React.