Why I chose Vue over React

Serpentarius13
11 min readOct 9, 2023

Introduction

State of the current JavaScript ecosystem is no joke — there is a ton of stuff: different libraries, frameworks which implement the same stuff their own way. In my opinion, this is neither good nor bad — plenty of choice, but for a novice engineer it could lead to a plenty of harm, because a newbie can’t really know good and bad. Of course, popularity helps — but they are all pretty popular.

And I think in this battle there is two battling competitors which stand out: React and Vue. A lot of people argue whether one is better, and this article tries to shed some light on this topic being guided with my arguments and personal experience.

My start

I started with React and knew not so much at that time, only having completed few courses on HTML, CSS and JavaScript. As it was the most popular, I thought it was the way to get into the field. Ive rolled right into the Next.js which had just released its app router, started reading documentation and made my first project. It was a message board that was dedicated to dream sharing — I’m a bit of a Carl Jung’s fan, if you know what I mean. I felt good to complete it — there was authorization, profile page, forms and some good practice with animations. If you want to check it, here’s the link (just the landing and form opening works now).

Although I’ve reached what I want with React, I was still looking for more opportunities — and that’s where I’ve found out about Vue.js. It was already in its mature years, having conquered transition to Composition API and looked almost the same as React. At first, the documentation had intimidated me, but after a few peeks I fell in love with it and right now I certainly could say that it is my favorite modern web framework.

Learning curve

At that time, I haven’t really touched React’s documentation, and Vue docs was my start in reading any kind of tutorial written by the creator itself and not some content makers. Even now, if you look at the upgraded version of React docs which is very mature, I think Vue is still better in regard of learning it. Advantages are listed below:

  1. Documentation that explains everything — some key points and hidden behaviors of React are not well documented, as the documentation covers only the basic functionality of library — therefore there is not much tricks that every React developer must know to be on par with modern trends.
  2. Official learning courses — Vueschool.io and Vuemastery.com are a good start for learning Vue, while React has some materials and most of them are probably outdated.
  3. Developer-friendly API — to me, Vue has a much better and differentiated pool of tools you can use to develop your apps. Coming from p. 1, they are well described and grant more control over your app than React does. Plus to that, there are not much hidden things that are mentioned in the docs, but are not obvious to newbie (rendering cycles, useEffect)

Of course, learning React helped me greatly in learning Vue afterwards — after you have solid ground and at least some understanding of one framework, you get over the others much faster. But even with that, I would recommend Vue to every newbie, because it is much more forgiving in terms of developing than React and it is pretty hard to shoot yourself in the foot.

Better gear

As I’ve mentioned previously, Vue has much better opinionated API than React does. Few keypoints to that statement:

There is no useEffect

Which means you won’t need to deal with the shortcomings of a frankenstein meant to be used by people blind to Single-Responsibility Principle of SOLID. In React, useEffect is two very different things in one tool — component lifecycle’s keeper and side effects’ provider.

useEffect(() => {
// do something
}, [])

What does this code does? Effects!

useEffect(() => {
return () => // do something after
}, [])

And this? Effect returns?..

useEffect(() => {

}, [a, b, c, d, eslint-error-that-suggests-to-gtfo, e, j])

Arrays?!

Why keep all these onMounted, onUnmounted, onBeforeMount, onRouteChange and watch , when you can just have one stupid abstraction with infinite amount of use cases? Want to only trigger something when component mounts? Provide an empty array! Why? Because abstraction!

Yeah, there are conventions that most react developers probably know, but why not introduce them into the library itself and make it better experience? Hook yourself.

Two-way data binding

React uses Flux architecture, which in short means that data could flow only one way. Using this, it is easy to track where the data changes, because there is only one source of truth. While Vue is almost identical — it has its benefits.

The first one is input binding. With React, you need to write something like this to bind your input value to a component’s state:


export const Input() => {

const [inputValue, setInputValue] = useState<string>("")

const handleChange = (ev: ChangeEvent<HTMLInputElement>) => {
const {value} = ev.target as HTMLInputElement;
setInputValue(value)
}

return <input onChange={handleChange} value={inputValue}/>
}

And this is for every input component. And in vue its just:

<template>
<input v-model="inputValue"/>
</template>

<script lang="ts" setup>

const inputValue = ref<string>("")

</script>

Yes, you can write a custom hook in React that would handle all this functionality instead of rewriting it in all input components… But why?

The second one is two-way binding between child and parent components, which means you can communicate the data from parent via props, and from child back to parent via emits. In React to handle some data from child component you would write:


const Parent = () => {
const handleData = (data: IData) => // do something
}

const Child = ({handleData}: {handleData: (data: IData) => any}) => {
return <button onClick={handleData}> HandleData! </button>
}

And in Vue that would be:

//Child
<template>
<button @click="$emit('ev')"> Emit! </button>
</template>

<script lang="ts" setup>

defineEmits<{ev: [something: string]}>();

</script>

//Parent
<template>
<Child @ev="handleEv"/>
</template>

<script lang="ts" setup>
const handleEv = (something: string) => console.log("Handled", something)
</script>

Which syntax is better? Let’s assume we have a component (which would be a corner-case situation because it’s rare to occur, but is still possible) with about 20 props in react. The interface for props of this component would be something like:

interface ComponentProps {
...valueProperties...
onChange: Function,
onEmit: Function,
onPause: Function,
onPuke: Function,
onRerender: Function,
onCry: Function,
onShit: Function,
onDeath: Function,
onLeave: Function,
onMeme: Function
}

Yes, you can understand that function bindings start from on, but why? What is the function just wasn’t passed to the component? It would crash your app. In Vue, those things would be differentiated with @ sign, and every other developer would have a better understanding of what the hell is happening.

A compiler, not a library

One more benefit that Vue has is that it provides a compiler for .vue files instead of using pure JavaScript expression. It may sound horrendous and overly complicated at first — but after you dwell a bit in the horrors of React’s jsx and other things that I will mention below, you’d get my point.

When jsx came to React, some people were disgusted a bit and they were right to a certain degree. The classic web designed with 3 core pieces in mind — HTML, CSS and JavaScript. They are meant to be differentiated by file extensions and the separation of concerns should work. But in the real world, there is a lot of reused logic, and with the origin of component-based approach to creating interfaces, I think it is was a good decision to combine them, because switching between three files (hello, Angular! (edit: Sorry, Angular)) is not a good experience for sure. But what React did — Vue does better and there’s why:

  1. No need for CSS files — Vue has a scoped style block where you could just simply put all your styles. They would not be shared among other components if you don’t want. With that, every CSS-in-JS lib for React becomes obsolete, and you don’t need to overengineer and create tons of css modulesjust to be able to write ten lines of code in it. Also, you are not forced to use Tailwind, if you want to avoid that (˵ ͡° ͜ʖ ͡°˵)
  2. Better conventions — there is no jsx, therefore you wouldn’t get lost. Want to have an html tag emit some event (click, select..)? Just add @ and handle it:
<input @change="handleChange">

Want to set innerHtml? Dont worry, you wouldn’t need to remember this:

<div dangerouslySetInnerHTML={{__html: data}} />

There is just:

<div v-html="data"/>

It is also dangerous to write too much, you know?

Need to have many classNames? Concatenate strings!

className={`${aboba} ${boba} ${voba} ${roba ? 'roba' : ''}` }

Want it the other way around? Use a lib! (of course, it is below 1kb in size, but it is still an external dependency)

3. Handling of attributes inheritance is way better. For example, in React, to pass a className to UI component that is probably going to be dynamic in the future, you need to write it in props and later pass it in the component’s root HTML tag:

export const Button = ({className}: {className: string}) => {
return <button className={className}> Classified </button>
}

And in Vue it is just:

<template>
<Child class="abc"/>
</template>

without the need to specify that the component need to take it up. Yes, it is ambiguous, but if you think that is the reason React could be better — you seem to not have written prop for every component that would have a corner-edge case where it needs a border that should not become part of it’s Theme.

You can also think that there could be times where you would not need to pass attribute directly to a root tag — and Vue has a solution for you:

<template>
<div>
<div v-bind="$attrs"/>
</div>
</template>

4. Slots are better children

To pass some template to a child component in React you should define children property in component’s props and then later use curly jsx brackets to render it:

const Component = ({children}: {children: ReactNode}) => {
return <div> {children} </div>
}

And in Vue it is not only much easier to do this, but provides much better freedom to make dynamic components:

a. Instead of children you could just leave a <slot/> tag to handle children:

<template>
<div>
<slot/>
</div>
</template>

b. You could have multiple named slots:

<template>
<div>
<slot/>
</div>
<div>
<slot name="chicken"/>
</div>
</template>

// Parent

<template>

<Child>

<template #default>
Goes in the default slot
</template>

<template #chicken>
Chicken!
</template>

</Child>

</template>

c. Slots could expose data to parent:

<template>
<div>
<slot :data={data}/>
</div>
</template>

// Parent

<template>

<Child>

<template #default={data}>
Using data!
</template>


</Child>

</template>

which is great for making reusable components that would be used with something else in a good combo alongside them (For example, encapsulating library’s components (swiper, etc..))

Rendering is on us

When React came out, it was a revelation. With the technologies that were dominating at that time, you had to write all re-render logic by yourself — and there came an absolute solution that swiped everything else off — just rerender everything! While it has fixed some problems and made developer’s life easier — it certainly has its flaws. Instead of trying to catch up with re-rendering, you need to make sure you are not re-rendering.

memo my ass! It worked great in the past, but we, especially those who are still starting to write code, don’t really need to stand one foot there and just eat it. I’m not saying you should not master rendering cycles and understand how it works under the hood, but why does everybody need to keep track of it, when some newer and better technologies do it for you?

Although Vue’s rendering mechanism is still dependent on Virtual DOM like React’s (Evan said new thingy is coming 2024) unlike super-cool Svelte and Solid, it is still much better than React because instead of re-rendering everything dumbly, it does something else. When compiling, Vue keeps track of every DOM node that needs to be updated when its dependencies change and then surgically updates it. No more useMemo, useCallback and performance dumbster that comes with the outdated technique of too much React-ivity.

In Vue, you’d rather need to find a method to re-render your component, that to prevent that behavior, which I think is much better than handling some constant edge-case that was created by the authors.

In-builts

As for some utilities that are a good fit to be included in an UI rendering solution, which React and Vue are, Vue has the upper hand too. When I wanted to give React a second try, at some point I encountered the need to use something like Transition component that Vue provides out of the box. With it, you can handle animations of component that is getting re-rendered. As for every problem, React did have a solution library for my case. And with that, blood spilled from my tears and I began to cry so deeply and was so sorry, that I just handled the animations with pure css. Look at that:

<template> 
<Transition>
<some-div/>
</Transition>
</template>

<style scoped>
.v-enter-from,
.v-leave-to {
opacity: 0;
}

.v-enter-active,
.v-leave-active {
transition: 0.25s ease all;
}
</style>

You probably understand what it does.

Now at this:

import { Transition } from 'react-transition-group';
import { useRef } from 'react';

const duration = 300;

const defaultStyle = {
transition: `opacity ${duration}ms ease-in-out`,
opacity: 0,
}

const transitionStyles = {
entering: { opacity: 1 },
entered: { opacity: 1 },
exiting: { opacity: 0 },
exited: { opacity: 0 },
};

function Fade({ in: inProp }) {
const nodeRef = useRef(null);
return (
<Transition nodeRef={nodeRef} in={inProp} timeout={duration}>
{state => (
<div ref={nodeRef} style={{
...defaultStyle,
...transitionStyles[state]
}}>
I'm a fade Transition!
</div>
)}
</Transition>
);
}

Want to keep component cached so it remembers the previous data and keeps up with better performance? Install a lib!

While in Vue:

<KeepAlive>
<div/>
</KeepAlive>

React has came up with Suspense — I’m glad! package.json is not big enough for three of those.

Conclusion

Thinking of this article brought me to the conclusion I think is the best in this war between frameworks — you can do everything everywhere, there are just different ways to do so. If you know the basics of something, you would keep cool at every library and framework you encounter. If you just want to stare at abstraction layers and compare them — you would lose. But, when choosing what tool to use I guess it is actually better to try them all before coming to conclusion.

For me, React feels like an old fighter that has its tricks, but is getting older and older, while there are some new young folks in good shape that are not bound by their dark past of harsh victories — they are looking for more and more. Plus to that, this old fighter is commissioned by an organization, while young folks are people’s heroes trying to win their spot under the sun and connecting tons of people with their combat (I mean, Vue is truly open-source and is not backed by anything like Meta).

Each one of them fights and wins, the old man has a big fanbase which he accumulated over the years, while youngun’s fans are screamingly committed to support him till the end.

Without React, its quite possible there wouldn’t be this kind of Vue, but without Vue and other frameworks like Svelte and Solid, there is just stagnation to come.

Thanks for spending time on this article. Hope you’d have a great or evening, whatever it is! Have fun!

--

--

Serpentarius13

Hey! My name is Andrey, Im a 20 year old Junior front-end developer! Nice to meet you!