Props Mental Model: React vs Vue Data flow

January 20, 2026

Props Mental Model: React vs Vue

As a React developer, I wanted to dive into Vue to learn how it approached building JavaScript UI. How do they differ? Is one better? Let's start by looking at their taglines. React is "a declarative, efficient, and flexible JavaScript library for building user interfaces." Vue is "a progressive, incrementally-adoptable JavaScript framework for building UI on the web

Highlights

At a high level, the frameworks take similar approaches to the same goal. React is JavaScript-centric vs Vue uses hybrid HTML template/JS. React uses a push update model vs Vue implements reactivity via observing. Vue has more built-in. React is more barebones and relies on community.

1App ──props──> Profile ──props──> Greeting
2  ↑ callbacks              β”‚
3  (setUser, onLogout)      (read-only display)
4
5
6

React props are plain JavaScript objects passed as function arguments. Components are pure functions: Greeting({ name, role }). Data flows strictly down via props. Actions flow up via callback props like onClick, onChange. Parent owns state, children are read-onlyβ€”no mutations allowed. Re-renders happen when parent state changes, cascading fresh props down. Explicit control, predictable, scales with discipline

Vue: Reactive, Template-Driven (HTML-First)

1
2App ──props──> Profile ──props──> Greeting
3  ↑ emits              β”‚ emits
4($emit('update:user')) (read-only)
5
6

Vue props work similarlyβ€”data down via (note kebab-case in templates). But Vue's reactive system auto-tracks dependencies. Child communicates via $emit('custom-event'), parent listens with @custom-event="handler". Props are still immutable, but Vue's reactivity makes updates feel automaticβ€”change user.name and {{ name }} updates everywhere it's used. Magical DX, less boilerplate

React forces you to think in explicit state updates. Vue hides reactivity complexity behind templates. Both enforce data down, events upβ€”just different flavors

Language approach

Let's jump right in and look at a pretty full-featured component. I'm going ahead with the Vue 3 composition API because it seems like the direction Vue is heading. There are obvious parallels: the Vue component options API is to React class components as Vue 3 composition API is to React hooks.

1// UserProfile.vue
2<div>
3  <div>{{ id }}</div>
4  <Avatar v-if="showAvatar" :id="id" />
5  <UserBody v-if="user" :user="user" />
6  <button @click="$emit('follow-click')">Follow</button>
7</div>
8defineComponent({
9  props: {
10    id: { type: String },
11    showAvatar: { type: Boolean },
12  },
13  setup(props) {
14    const {id} = toRefs(props);
15    const user = ref(undefined);
16    function updateUser() {
17      fetchUser(id.value).then(data => {
18        user.value = data;
19      });
20    }
21    onMounted(updateUser);
22    watch(id, updateUser);
23    return {user};
24  }
25})
26
27

React

1
2// React
3function UserProfile({id, showAvatar, onFollowClick}: {
4  id: string,
5  showAvatar: boolean,
6  onFollowClick: () => void,
7}) {
8  const [user, setUser] = React.useState(undefined);
9  React.useEffect(() => {
10    fetchUser(id).then(setUser);
11  }, [id]);
12  return (
13    <div>
14      <div>{id}</div>
15      {showAvatar ? <Avatar id={id} /> : null}
16      {user !== undefined ? <UserBody user={user} /> : null}
17      <button onClick={onFollowClick}>Follow</button>
18    </div>
19  );
20}
21
22
23

React's JSX is just sugar for JavaScript.

In a way, you could also say that Vue's templates are also JavaScript sugar. However, the transform is more involved and Vue-specific. Pros and Cons One advantage of Vue's template syntax is that because it is more restrictive, the compiler is able to perform more optimizations, such as separating out static template content to avoid rerenders. React can do something similar with a Babel plugin but this is not common. Theoretically, I believe Vue could make more optimizations from template syntax.

A disadvantage with Vue templates is that there are times when JavaScript's expressiveness is sorely missed or even necessary. In those cases, Vue recommends using a render function, either via the more verbose createElement or JSX. An example I ran into is wanting a local variable inside a template loop. Translating between Vue template and JSX is a manual process. I think you probably need to be familiar with both template and JSX to be a Vue developer, in which case React's one approach seems better. If you use React hooks, React components are just functions. All the logic lives inside this function. Vue separates the component definition from the template (or render function). Using the new Composition API above, the component definition is inside one setup function. This is a notable difference; React hooks are run on every render but setup is only run once on initialization. Vue sets up listeners (lifecycle and reactive values) whereas React specifies effects on each render.

Event Handling

Event handling is another example of the differing language approach. React has no special syntax; it's just JavaScript functions. Vue provides syntax for listening to and emitting events.

1// MyVueComponent
2<button @click="$emit('increment')">Increment</button>
3<MyVueComponent @increment="methodName" />
4// MyReactComponent
5<button onClick={props.onIncrement}>Increment</button>
6<MyReactComponent onIncrement={jsFunction} />
7
8
9

You can see here the differing approaches to events. React passes a JavaScript function to the component. Vue components emit events, which are identified as strings with associated data. Static analysis

At a high level, React is better suited for static analysis, such as TypeScript. Its JavaScript-centric approach puts it closer to the language so most editor/tooling just works. I set up VSCode with Vetur (Vue's recommended tooling) and didn't get semantic langauge features (e.g. checking, autocomplete, go to definition) inside the Vue template. Note: I realised Vetur has an experimental setting for Template Interpolation Service which adds a lot of these features but it still misses features like find references.

Some Vue features like named slots, events, and their props as React children equivalent are too dynamic for full static analysis. For example, components can emit custom events but there isn't an obvious way to write out that contract.

Vue provides a global namespace although it is not always recommended. For example, you can register components by name to the global namespace. Vue plugins can inject global methods, properties, and mixins. Global namespaces, while convenient at times, play less nicely with tooling and scalable codebases.

Update model

The biggest functional difference between Vue and React is how they handle updates. Vue uses observables (via JavaScript Proxies or defineProperty) to implement reactivity. In short, it modifies data to track when properties are read or written. This allows for fine-grained dependency tracking; Vue knows which properties have been read so it can rerender and update views only when those properties change. This is smarter than a stock React.memo, which compares equality for all props. In comparison, React uses a push update model. Rerenders are triggered by a function call somewhere (state update or reducer dispatch). When a React component updates, it will rerender all its children as well.

1
2// MyVueComponent
3<button @click="count += 1">{{ count }}</button>
4Vue.extend({
5  data: {
6    count: 0
7  }
8})
9function MyReactComponent() {
10  const [count, setCount] = React.useState(0);
11  return <button onClick={() => setCount(count => count + 1)}>{count}</button>;
12}
13

The way I think of Vue's update model is as if all components were wrapped in React.memo and the equality function was a dynamic one that compared only props/state that were used on the last render.

Vue's reactivity model operates like React components universally wrapped in React.memo() with dynamically generated equality functionsβ€”it automatically tracks exactly which props and reactive state (data, ref, reactive) were accessed during the last render, then only re-renders when those specific dependencies change.

Unlike React's static dependency arrays in useEffect/useMemo, Vue uses Proxy-based reactivity (ES6 Proxies intercept property access/set operations) to build a dependency graph at runtime: when your template accesses {{ user.name }}, Vue records that user.name is a dependency; when user.name changes later, only components using that exact property re-render.

This granular tracking happens out-of-the-box without manual optimization, making Vue's default performance superior for complex UIs. It's strikingly similar to MobX where reactive "atoms" (individual properties) trigger derived "computed" values, but Vue extends this to templates:

1computed: {
2  displayName() {
3    return this.user.name.toUpperCase();
4  }
5}
6

automatically reruns only when user.name changesβ€”React would need useMemo with a stable user reference or a wrapper component.

1undefined
2

However, Vue's model isn't perfect. Computed properties currently re-run their getter whenever underlying data changes, even if the output stays identical (user.name = user.name still triggers), lacking React's useMemo strictness.

Vue's reactivity also has destructuring gotchasβ€”pulling { count } = store into local variables breaks reactivity since Proxies only track direct property access. Composition API ref() requires explicit .value access or toRefs() wrappers, while reactive() objects must be consumed directly.

React's explicitness forces discipline (React.memo, useCallback) but makes performance predictable; Vue hides complexity behind "automatic" reactivity, trading boilerplate for potential stale closure bugs.

Both achieve data down/actions up, but Vue prioritizes DX while React prioritizes explicit control (Vue reactivity fundamentals, Vue Composition API refs).

Out-of-the-box, Vue performs more granular updates so Vue updates are more performant by default. Of course, React has React.memo but that requires understanding of closures and when to use React.useMemo and React.useCallback.

Vue isn't off the hook though. Reactivity via injecting observables comes with its gotchas.

API surface area

It's hard for me to be objective because I have a lot more familiarity with the React API. However, I still feel that React has a smaller API and fewer React-specific concepts to learn (ignoring concurrent mode and time-slicing). A number of things are more convenient in Vue. Here are a few examples. v-model Vue has sugar for two-way data binding. It's quite nice.

1// MyVueComponent
2<div>
3  <input v-model="message" />
4  <p>{{ message }}</p>
5</div>
6Vue.extend({
7  data: {
8    message: ''
9  }
10})
11

The following is quoted from the React docs: In React, data flows one way: from owner to child. We think that this makes your app's code easier to understand. You can think of it as "one-way data binding."

1function MyReactComponent() {
2  const [message, setMessage] = React.useState('');
3  return (
4    <div>
5      <input
6        value={message}
7        onChange={e => setMessage(e.target.value)}
8      />
9      <p>{message}</p>
10    </div>
11  );
12}
13

Combining class names

Vue has special class and style handling. These properties get merged and also handle object maps and arrays.

Combining class names - Vue's magic vs React's reality

Vue - Automatic merging, object syntax, arrays. Clean and declarative:

1<!-- Parent sets base + conditional -->
2<MyButton
3  baseClass="btn"
4  :class="{ 'btn-primary': isPrimary, 'btn-large': isLarge }"
5  :style="{ '--accent': accentColor }"
6/>
7
8<!-- Child component - auto-merges everything -->
9<template>
10  <button :class="[baseClass, buttonClass]" :style="mergedStyle">
11    {{ label }}
12  </button>
13</template>
14
15<script>
16export default {
17  props: ['baseClass', 'class', 'style']
18}
19</script>
20

React - Manual string building or third-party libs like clsx/classnames:

1function MyButton({
2  className,
3  style,
4  isPrimary = false,
5  isLarge = false,
6  accentColor,
7  label = 'Button'
8}) {
9  const baseClasses = clsx(
10    'btn',
11    {
12      'btn-primary': isPrimary,
13      'btn-large': isLarge
14    },
15    className  // parent override
16  );
17
18  return (
19    <button
20      className={baseClasses}
21      style={{
22        '--accent': accentColor,
23        ...style
24      }}
25    >
26      {label}
27    </button>
28  );
29}
30
31// Usage
32<MyButton
33  isPrimary={isPrimary}
34  isLarge={true}
35  accentColor="#ff6b6b"
36  className="rounded shadow-lg"
37/>
38
39
40

Reactivity Philosophy: Vue's built-in merging feels magical - pass :class="{ active: isActive }" and it auto-combines. React forces you to build the string yourself or add clsx (40kb). Vue = less typing, React = total control over the exact CSS string generated.

1<button @click="count += 1">{{ count }}</button>
2defineComponent({
3  reset() {
4    // This causes rerender
5    this.count = 0;
6  }
7})
8
9

The fact that mutating what looks like a local variable causes a rerender is still a little beyond my comfort zone πŸ™‚

Vue as a framework

React pitches itself as a library and Vue as a framework. The line is blurry but Vue does more out-of-the-box than React. Vue has transitions and animations built-in. It has blessed libraries for routing and state management (vuex).

React, as in the core React, focuses on only the rendering layer. The other pieces are provided by the ecosystem, which fortunately, is very vibrant.

With my limited experience, bootstrapping an app feels about the same both with vue-cli and create-react-app. I like Vue Single File Components, which allows you to define component-scoped CSS in the same file as the template and component logic.

Not Too Different After All

While I've focused on differences, React and Vue share core concepts that map cleanly between them:

Vue React
Virtual DOM + JSX Virtual DOM + JSX
Slots Children
Props/Data Props/State
Teleport Portal

Which One Should You Choose?

No definitive answerβ€”it depends on your needs:

Choose React if:

  • You're a type system purist (TypeScript integration is superior)
  • Building large, multi-engineer codebases (purer JS approach, fewer global gotchas)
  • Need maximum ecosystem (React Native, huge hiring pool, render targets)

Choose Vue if:

  • You prefer HTML-first development (templates + progressive enhancement)
  • Building content-heavy sites with sprinkled interactivity
  • Onboarding non-JS-heavy developers (intuitive templates, less re-render thinking)

Reality check: Both make you productive. Vue feels magical for rapid prototyping. React feels predictable for enterprise scale.

I still prefer React's explicitness, but Vue's DX is genuinely impressive.

Pick based on team skills + project needs, not framework wars.

Chat Avatar