VueConf US 2025 โ€” Epicmax is speaking!Register now โ†’
Epicmax
June 5, 2025ยท by Epicmax Team

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.

Vue.jsReact
React vs Vue: Common Patterns Between Frameworks

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:

ConcernReactVue
Defining markup to renderJSXTemplates
Reuse logic between componentsHooksComposables
Passing data between componentsCallback propsEvents
Passing content to renderJSX as a propSlots
Rendering content based on dataRender propsScoped slots
Sharing state deeplyContextProvide / inject
Polymorphic componentsas 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 ๐Ÿ˜