React vs Vue: Common Patterns Between Frameworks
A React-to-Vue comparison for developers switching frameworks: JSX vs templates, hooks vs composables, callback props vs events, context vs provide/inject.

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:
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.
Teams planning to upgrade or migrate projects may benefit from guidance from a Vue.js migration company to ensure best practices and a seamless transition.
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:
| Concern | React | Vue |
|---|---|---|
| Defining markup to render | JSX | Templates |
| Reuse logic between components | Hooks | Composables |
| Passing data between components | Callback props | Events |
| Passing content to render | JSX as a prop | Slots |
| Rendering content based on data | Render props | Scoped slots |
| Sharing state deeply | Context | Provide / inject |
| Polymorphic components | as prop | <component :is="..." > |
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.
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 };
}
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 child:
function ChildComponent({ onDataChange }) {
return <button onClick={() => onDataChange('New Data')}>Click</button>;
}
Vue child:
<script setup>
const emit = defineEmits(['data-change']);
function changeData() {
emit('data-change', 'New Data');
}
</script>
<template>
<button @click="changeData">Click</button>
</template>
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 the 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.
Vue.js is our passion. Open Source is our culture ๐