Le Do Nghiem

Le Do Nghiem

AI Engineer

About meBooksSnippetsBlog

© 2026 Le Do Nghiem. All rights reserved.

Contact |

Back to Blog

Vue.js 3: Building Reactive UIs

Le Do Nghiem
Le Do NghiemAI Engineer
2025-09-05 4 min read
Share

Why Vue still gets a look

I reach for Vue when I want a productive SPA without ceremony. Vue 3's Composition API gave me the grouping-by-feature style I liked from React hooks, with reactivity that often feels lighter for form-heavy UIs.

This post is not "Vue vs React winner takes all." It is how I build a small reactive UI with ref, computed, and watch — and when that choice still makes sense for me.


Composition API vs Options API

Options API: data, methods, computed in one object. Familiar if you learned Vue 2.

Composition API: setup() or <script setup> — logic grouped by feature, reusable composables.

When I use Composition API:

  • New projects on Vue 3
  • Shared logic extracted into composables (useAuth, useCart)
  • Components with more than a little state

When Options API is fine:

  • Tiny widgets, legacy codebases, team comfort

Red flag: Rewriting a stable Options API app to Composition API for fashion. Migrate when you touch the file for a real reason.


Reactivity: ref, computed, watch

ref — reactive primitive values and object references.

computed — derived state; caches until dependencies change.

watch — side effects when something changes (API calls, localStorage).

<script setup>
import { ref, computed, watch } from "vue";

const todos = ref([]);
const newTodo = ref("");
const filter = ref("all");

const visibleTodos = computed(() => {
  if (filter.value === "active") return todos.value.filter((t) => !t.done);
  if (filter.value === "done") return todos.value.filter((t) => t.done);
  return todos.value;
});

function addTodo() {
  const text = newTodo.value.trim();
  if (!text) return;
  todos.value.push({ id: Date.now(), text, done: false });
  newTodo.value = "";
}

watch(
  todos,
  (val) => localStorage.setItem("todos", JSON.stringify(val)),
  { deep: true }
);
</script>

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo" placeholder="Add a todo" />
    <button @click="addTodo">Add</button>
    <ul>
      <li v-for="todo in visibleTodos" :key="todo.id">
        <input type="checkbox" v-model="todo.done" />
        <span :class="{ 'line-through': todo.done }">{{ todo.text }}</span>
      </li>
    </ul>
  </div>
</template>

What: visibleTodos recalculates when todos or filter changes — no manual sync.

Why: Less boilerplate than syncing derived lists in event handlers.

Red flag: Deep watch on huge objects — performance and debugging pain. Watch specific fields when you can.


When I reach for Vue vs React

I pick Vue when…I pick React when…
Greenfield SPA, team knows VueNext.js App Router, RSC, Vercel deploy
Heavy forms, v-model ergonomicsHuge ecosystem package I need
Nuxt for SSR with Vue DXCompany standard is React

No tribal war. Both are tools. I optimize for team velocity and hiring pool on client work.


Ecosystem notes

  • Pinia for global state — simpler than Vuex for most apps I build
  • Vue Router — fine-grained lazy routes for code splitting
  • Nuxt — when I want file-based routing and SSR like Next.js for the Vue world

Composables — logic reuse

When two components share fetch + filter logic, I extract a composable:

// composables/useTodos.js
import { ref, computed } from "vue";

export function useTodos() {
  const todos = ref([]);
  const remaining = computed(() => todos.value.filter((t) => !t.done).length);
  function add(text) {
    todos.value.push({ id: Date.now(), text, done: false });
  }
  return { todos, remaining, add };
}

Same idea as React custom hooks — keeps components thin.


First Composition API mistakes

  • Mutating reactive objects without understanding proxies — sometimes I replaced whole objects instead of updating fields; todos.value = [...] clarity helped.
  • Over-using watch — many watchers became computed once I thought in derived state.
  • Giant single-file components — composables fixed that; same lesson as React custom hooks.

Try this on one feature

Build one small feature with <script setup>, one computed, one watch. Feel where reactivity saves clicks and where it hides bugs.

Vue 3 is not niche — it is a deliberate choice for approachable reactivity. If your stack is React + Next.js, that is fine too. Know why you picked the tool, not just the logo.

On this page

  • Why Vue still gets a look
  • Composition API vs Options API
  • Reactivity: ref, computed, watch
  • When I reach for Vue vs React
  • Ecosystem notes
  • Composables — logic reuse
  • First Composition API mistakes
  • Try this on one feature
Share
Previous Post

Building AI-Powered Apps with Next.js and LangChain

Next Post

Optimizing Next.js Apps with AI-Powered Image Processing