Vue.js for React Developers: A Hands-On Guide
You're a React developer. You've mastered hooks, understood the virtual DOM, and can debug re-renders in your sleep. But you keep hearing about Vue: how it's easier to learn, has automatic reactivity, and requires less boilerplate. Is the grass really greener?
This guide is for you. We'll explore Vue 3's Composition API, reactivity system, and template syntax through interactive comparisons with React. I'm not here to sell you on Vue. I'm here to show you how it works and where it differs from React, so you can decide for yourself.
Why Learn Vue as a React Developer?
Understanding multiple frameworks makes you a better developer. You'll recognize patterns, make better architectural decisions, and understand the trade-offs behind React's design choices. Plus, you might discover you prefer Vue for certain projects.
Vue and React solve the same problems (reactive UIs, component composition, state management) but with different philosophies. React gives you JavaScript for everything (JSX). Vue separates templates from logic. React requires manual dependency tracking (useMemo, useEffect). Vue uses Proxies for automatic tracking.
Part 1: Reactivity - Proxy Magic vs Manual Dependencies
In vanilla JavaScript, changing a variable doesn't update the DOM. Both React and Vue solve this with reactive state, but they detect changes differently.
How React Handles Reactivity
React uses explicit state setters. When you call setState, React knows something changed and schedules a re-render:
const [count, setCount] = useState(0);
// React doesn't "watch" count
// You must call setCount to trigger updates
setCount(count + 1);For derived state, React requires manual dependency arrays:
const fullName = useMemo(() => {
return `${firstName} ${lastName}`;
}, [firstName, lastName]); // You list dependencies manuallyHow Vue Handles Reactivity
Vue wraps your state in a JavaScript Proxy that intercepts all reads and writes:
const count = ref(0);
// Vue's Proxy detects this write automatically
count.value++;For derived state, Vue automatically tracks what you access:
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
}); // No dependency array needed!Toggle between React and Vue modes above. Change the first or last name, then toggle the theme. Notice how React recomputes fullName even when firstName/lastName didn't change, while Vue only recomputes when dependencies actually change.
Key differences:
- React requires manual dependency arrays in useMemo. Any state change can trigger re-renders. You must remember to list all dependencies.
- Vue automatically tracks which properties are accessed. Only dependent computations re-run when data changes. No manual dependency management needed.
React's approach is explicit: you tell it what changed and what depends on what. Vue's approach is automatic: it tracks dependencies at runtime using Proxies. Both work, but Vue reduces boilerplate and eliminates dependency array bugs.
Understanding Proxy-Based Reactive Systems
What exactly is a Proxy, and how does Vue use it for reactivity? A JavaScript Proxy is an object that wraps another object and intercepts operations performed on it. Think of it as a middleman that can watch and control access to your data.
Read the explanation above to understand what Proxies are, then try running the code examples. See how the Proxy's get and set traps intercept property access? Vue uses this mechanism to track which components read which properties, then re-renders only affected components when those properties change.
How it works:
- Proxies intercept property access (get) and modification (set)
- Vue wraps your state in a Proxy to track all reads and writes
- When you read
state.count, Vue knows you depend on it - When you write
state.count = 5, Vue re-renders components that read it
Side-by-Side: useState vs ref()
Both create reactive state, but with different APIs:
Key differences:
- React: Returns [value, setter]. Use value directly. Call setter to update. Setter can take updater function.
- Vue: Returns ref object. Access via
.valuein script. Directly mutate.value. Auto-unwrapped in templates.
Vue's ref() requires .value in JavaScript but NOT in templates. This trips up newcomers:
// In script setup:
const count = ref(0);
count.value++; // .value required
// In template:
<div>{{ count }}</div> <!-- NO .value needed -->Why? Vue auto-unwraps refs in templates for convenience.
Side-by-Side: useMemo vs computed()
Derived state works similarly, but Vue eliminates the dependency array:
Key differences:
- React: You must manually specify [firstName, lastName]. Forget one? Stale data or infinite loops.
- Vue: Automatically tracks that you accessed firstName.value and lastName.value. No manual work.
- Result: Vue's computed is less error-prone and more maintainable.
Vue's computed requires zero maintenance. With React's useMemo, you can forget a dependency (stale values), include too many dependencies (unnecessary recomputes), or trigger infinite loops with wrong dependencies. Vue's computed avoids these issues by tracking dependencies automatically.
Part 2: Composition API vs React Hooks
Both Vue 3's Composition API and React Hooks move away from class components toward function-based composition. They're surprisingly similar!
The Evolution Story
React (2013): Classes with lifecycle methods → Hooks (2019) for functional components
Vue (2014): Options API (data, methods, computed) → Composition API (2020) for better composition
Both frameworks recognized that grouping code by feature (not lifecycle) is more maintainable.
script setup: Vue's Concise Syntax
React function components look like this:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;Vue's <script setup> is more concise:
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="count++">Increment</button>
</div>
</template>React patterns:
- Function components return JSX
- Hooks must follow Rules of Hooks
- State with useState
- Effects with useEffect
- Everything in JavaScript
Vue patterns:
<script setup>auto-exposes to template- No special hook rules
- State with ref/reactive
- Effects with watch/watchEffect
- Separate script and template
Key differences:
- React: Everything is JavaScript (JSX). State, logic, and template mixed.
- Vue: Separate
<script>and<template>. Clean separation of concerns. - React: Must return JSX from component function.
- Vue: Everything in
<script setup>auto-exposes to template.
Lifecycle Hooks: More Granular in Vue
React combines mount, update, and unmount into useEffect:
useEffect(() => {
console.log('Mounted or updated');
return () => console.log('Cleanup');
}, [dependency]); // Runs when dependency changesVue separates each phase:
onBeforeMount(() => console.log('About to mount'));
onMounted(() => console.log('Mounted'));
onBeforeUpdate(() => console.log('About to update'));
onUpdated(() => console.log('Updated'));
onBeforeUnmount(() => console.log('About to unmount'));
onUnmounted(() => console.log('Unmounted'));Click through the lifecycle phases above. Vue gives you hooks before and after each phase, while React combines everything into useEffect.
Key difference:
- React: useEffect combines mount, update, and unmount. Deps array controls when it runs.
- Vue: Separate hooks for each phase (onMounted, onUpdated, onUnmounted) for clarity.
- Vue Advantage: More granular control - you can run code specifically before/after mount or update.
Side Effects: useEffect vs watch()
React's useEffect is general-purpose for all side effects:
useEffect(() => {
console.log('searchQuery changed:', searchQuery);
const timeout = setTimeout(() => {
saveToLocalStorage(searchQuery);
}, 300);
return () => clearTimeout(timeout); // Cleanup
}, [searchQuery]); // Manual dependencyVue's watch is specifically for watching reactive data:
watch(searchQuery, (newValue, oldValue) => {
console.log('searchQuery changed:', newValue);
const timeout = setTimeout(() => {
saveToLocalStorage(newValue);
}, 300);
// Cleanup via onInvalidate
return () => clearTimeout(timeout);
}); // Automatic trackingComparison:
React useEffect:
- General-purpose effect hook
- Requires dependency array
- Runs after every render (if deps change)
- Return cleanup function
Vue watch:
- Specifically for watching data
- Automatically tracks source
- Gets old and new values
- Built-in cleanup via onInvalidate
Vue's watch is more explicit about what it's watching. React's useEffect is more flexible but requires careful dependency management. Both can do the same things, but Vue's API is more specific to the use case.
Part 3: Template Syntax vs JSX
This is where React and Vue diverge most philosophically.
Should you use JavaScript for everything (React's JSX), or separate templates from logic (Vue)? There's no right answer. It's about what feels more natural to you and your team.
Conditional Rendering: && and ?: vs v-if
React uses JavaScript expressions:
{show && <div>Message is visible!</div>}
{mode === 'A' ? <ComponentA /> : <ComponentB />}Vue uses template directives:
<div v-if="show">Message is visible!</div>
<ComponentA v-if="mode === 'A'" />
<ComponentB v-else />Vue's additional directives:
v-if/v-else-if/v-else- Full conditional renderingv-show- Toggle CSS display (element stays in DOM)- React: Only
{expression && <JSX />}or ternary operator
Vue's additional directives:
v-if/v-else-if/v-else- Full conditional chainsv-show- Toggle CSS display (element stays in DOM)
React only has {condition && <JSX />} and ternaries. It's more flexible but more verbose for complex conditions.
List Rendering: .map() vs v-for
React uses JavaScript's .map():
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>Vue uses the v-for directive:
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>Add, remove, and shuffle items above. The key prop/directive is critical in both frameworks for efficient updates. Without it, both frameworks struggle with reordering.
Keys are critical in both frameworks:
- React:
keyprop helps identify which items changed - Vue:
:keydirective does the same thing - Without keys, both frameworks struggle with reordering
- Try shuffling above - keys make it smooth!
Two-Way Binding: Controlled Inputs vs v-model
Vue is particularly concise here.
React requires explicit value + onChange:
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>Vue's v-model is syntactic sugar:
<input type="text" v-model="text" />React (Controlled):
- Explicit value prop
- Explicit onChange handler
- More verbose but clear
- Full control over updates
Vue (v-model):
- Syntactic sugar
- Auto-binds value + @input
- Concise and clean
- Less boilerplate
What v-model actually does:
<input v-model="text" />
<!-- Expands to: -->
<input
:value="text"
@input="text = $event.target.value"
/>It's just sugar, but it eliminates a lot of boilerplate for forms.
DOM Refs: useRef vs Template Refs
Both frameworks let you access DOM elements directly:
React useRef:
- Create with useRef(null)
- Attach with
ref={inputRef} - Access via inputRef.current
- Works for DOM and values
Vue template ref:
- Create with ref(null)
- Attach with ref="inputRef"
- Access via inputRef.value
- Template refs are reactive
Confusing: Vue uses ref() for BOTH state and DOM refs!
const count = ref(0); // Reactive state
const inputRef = ref(null); // DOM refBoth use .value. React separates these: useState for state, useRef for DOM.
Part 4: Component Lifecycle & Deeper Concepts
Let's dig into how components mount, update, and unmount in both frameworks.
Mounting Phase: Step by Step
Use the step controls to walk through mounting in React vs Vue. Notice Vue's more granular hooks (onBeforeMount, onMounted) versus React's single useEffect.
Vue has more granular hooks (onBeforeMount, onMounted) while React combines everything into useEffect with an empty dependency array.
Update Phase: How Changes Propagate
When state changes, both frameworks:
- Detect the change
- Re-render the component
- Diff the virtual DOM
- Update only changed DOM nodes
React re-renders the entire component tree on state change. Uses useMemo/useCallback to prevent unnecessary work.
Vue only re-renders components that use changed reactive data (thanks to Proxy tracking). Less manual optimization needed.
Parent-Child Communication: Props & Events
Both use "props down, events up" pattern:
React pattern:
- Props down via attributes
- Events up via callback props
- onEventName convention
- Direct function calls
Vue pattern:
- Props down via :propName
- Events up via @eventName
- defineEmits() in child
- emit() to trigger events
React passes callbacks as props (onEventName convention). Vue uses defineEmits() and emit() for explicit event declarations.
Cleanup and Unmounting
Both frameworks let you clean up side effects (timers, subscriptions, etc.):
React uses useEffect cleanup functions. Vue has a dedicated onUnmounted hook. Same functionality, different API.
When to Choose What?
Select the factors that apply to your project. The matrix will score React vs Vue based on your needs.
Key Takeaways
-
Reactivity: Vue's Proxy-based automatic tracking vs React's manual dependency arrays. Vue has less boilerplate and fewer bugs. React is more explicit with full JavaScript control.
-
Component Structure: Composition API ≈ Hooks. Vue's
<script setup>is more concise and separates template from logic. React's JSX mixes everything in JavaScript. -
Templates: v-if, v-for, v-model vs JSX expressions. Vue is more HTML-like and familiar to web developers. React gives you full JavaScript power and more flexibility.
-
Lifecycle: Vue's granular hooks vs React's unified useEffect. Vue has separate hooks for each phase (onMounted, onUpdated, etc.). React's useEffect handles everything (simpler but less specific).
-
Ecosystem & Jobs: React is larger, but Vue is growing fast. React has a bigger ecosystem, more jobs, and is backed by Meta. Vue has an easier learning curve, great docs, and is strong in Asia/Europe.
Your Next Steps
If you're coming from React and want to try Vue:
- Official Docs: vuejs.org - Excellent tutorial
- Try It: Build the same component in React and Vue
- Play with Proxies: Understanding reactivity is key
- Explore Pinia: Vue's state management (similar to Redux/Zustand)
If you decide Vue isn't for you, that's fine! You've learned about alternative approaches to reactivity, templates, and lifecycle management. This knowledge makes you a better React developer.
Final Thoughts
Vue and React are both excellent frameworks. React's philosophy is "it's just JavaScript": full control, explicit everything. Vue's philosophy is "make it easy": automatic reactivity, less boilerplate, clearer separation.
Neither is objectively better. It depends on your team's experience, project requirements, personal preference, and ecosystem needs.
The best developers understand why frameworks make different choices. Now you do.