React vs Vue: A Practical Guide to Common Patterns for Frontend Developers
After six years of working with React, I transitioned to Vue about a year ago. Since then, I’ve built everything from UI libraries to full-scale applications using Vue.

This article is meant to help React developers understand key patterns and how they translate into Vue. If you’re looking to expand your frontend skills or explore another popular JavaScript framework, this React-to-Vue comparison should make the transition smoother.
If you are coming from a React background and already have basic knowledge of Vue, you might benefit from more detailed articles on advanced techniques:


Autor: Alexandra Petrova, senior frontend developer
Overview: React vs Vue
React and Vue.js are two of the most popular frontend frameworks for building user interfaces. While they share similar goals, they differ in syntax and architectural philosophy. This guide compares common React patterns to their Vue counterparts so that React developers can quickly understand Vue's approach.

Here is a comparison table of React features and their equivalents in Vue:
JSX vs Templates in Vue

React uses JSX to define markup directly in JavaScript:

// Component.jsx
export default function Component({ greeting }) {
  return <p>{greeting}</p>;
}
Vue uses HTML-based templates in Single-File Components (SFCs):
<!-- Component.vue -->
<script setup>
const props = defineProps(['greeting']);
</script>
<template>
  <p>{{ greeting }}</p>
</template>
Although Vue supports JSX, templates are preferred in the Vue ecosystem. Learn more about JSX in Vue.
React Hooks vs Vue Composables
Hooks and composables are both ways to reuse logic. React example:
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(json => {
        setData(json);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}
Vue composable:
import { ref, onMounted } from 'vue';

function useFetch(url) {
  const data = ref(null);
  const loading = ref(true);

  onMounted(async () => {
    const response = await fetch(url);
    data.value = await response.json();
    loading.value = false;
  });

  return { data, loading };
}
Vue Reactivity 101

Unlike React, which requires explicit hooks like useState or useEffect, Vue's reactivity system is built-in. You use ref and reactive to declare reactive state, and Vue automatically tracks dependencies and updates.
Callback Props vs Events

React pattern:

<ChildComponent onDataChange={handleDataChange} />
Vue pattern:
<ChildComponent @data-change="handleDataChange" />
React:
function ChildComponent({ onDataChange }) {
  return <button onClick={() => onDataChange('New Data')}>Click</button>;
}
Vue:
<script setup>
const emit = defineEmits(['data-change']);

function changeData() {
  emit('data-change', 'New Data');
}
</script>
<template>
  <button @click="changeData">Click</button>
</template>
Vue:
JSX as Prop vs Vue Slots

React:

<ChildComponent>
  <h1>Hello</h1>
</ChildComponent>
Vue:
<ChildComponent>
  <template #default>
    <h1>Hello</h1>
  </template>
</ChildComponent>
Vue slots are used in templates with <slot></slot>. Named slots allow multiple insertion points:
<template>
  <slot name="header"></slot>
  <slot></slot> <!-- default -->
  <slot name="footer"></slot>
</template>
Render Props vs Scoped Slots

React:

<MouseTracker render={({ x, y }) => <p>{x}, {y}</p>} />
Vue:
<MouseTracker v-slot="{ position }">
  <p>{{ position.x }}, {{ position.y }}</p>
</MouseTracker>
Scoped slots let you expose internal data to the parent for full control over rendering.
Context API vs Provide/Inject

React:

<ThemeContext.Provider value={{ theme, setTheme }}>
  <App />
</ThemeContext.Provider>
Vue:
<script setup>
const theme = ref('light');
provide('theme', theme);
</script>
To consume:
<script setup>
const theme = inject('theme');
</script>
Polymorphic Components

React pattern using as prop:

function Box({ as: Component = 'div', ...props }) {
  return <Component {...props} />;
}
Vue dynamic components:
<component :is="as"></component>
You can pass either an HTML tag name or a component to the is attribute.
Conclusion
Migrating from React to Vue is easier when you map familiar patterns. Vue's Composition API and template syntax offer similar flexibility with a different philosophy. This comparison shows that while the syntax differs, most React patterns have Vue equivalents.
Whether you're building your next frontend app or just exploring another JavaScript framework, Vue is a powerful alternative for modern UI development.