SuperAPI AI Directory

vue

Frontend & websites

View repository

Updated 10-16 14:57

About this skill

Vue.js

Vue is a progressive framework for building user interfaces, featuring the reactive Composition API and high-performance Vapor Mode compilation.

When to Use

  • Progressive Full-Stack Web Applications: Vue 3.5+ with <script setup> and Composition API.
  • Single Page Applications (SPAs): Building fast, reactive frontends with Vue Router and Pinia.
  • Interactive Micro-Frontends & Widgets: Embedding reactive components into existing server-rendered templates.
  • Design System Implementation: Building accessible, scoped component libraries with PrimeVue or Vuetify.

Quick Start

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

const count = ref(0);
const double = computed(() => count.value * 2);

function increment() { count.value++; }

Core Concepts

Composition API with & TypeScript

Reactive primitives using ref, computed, and watch:

<!-- components/ProductCounter.vue -->
<script setup lang="ts">
import { ref, computed, watch } from "vue";

interface Props {
  initialCount?: number;
  unitPrice: number;
}

const props = withDefaults(defineProps<Props>(), {
  initialCount: 1,
});

const emit = defineEmits<{
  (e: "totalChanged", total: number): void;
}>();

const quantity = ref(props.initialCount);
const totalPrice = computed(() => quantity.value * props.unitPrice);

watch(totalPrice, (newTotal) => {
  emit("totalChanged", newTotal);
});

function increment() {
  quantity.value += 1;
}

function decrement() {
  if (quantity.value > 1) quantity.value -= 1;
}
</script>

<template>
  <div class="counter-box">
    <button @click="decrement">-</button>
    <span>{{ quantity }}</span>
    <button @click="increment">+</button>
    <p>Total: ${{ totalPrice.toFixed(2) }}</p>
  </div>
</template>

State Management with Pinia

Centralized reactive store with type inference:

// stores/cart.ts
import { defineStore } from "pinia";
import { ref, computed } from "vue";

export interface CartItem {
  id: number;
  name: string;
  price: number;
}

export const useCartStore = defineStore("cart", () => {
  const items = ref<CartItem[]>([]);

  const totalCost = computed(() =>
    items.value.reduce((sum, item) => sum + item.price, 0),
  );
  const itemCount = computed(() => items.value.length);

  function addItem(item: CartItem) {
    items.value.push(item);
  }

  function clearCart() {
    items.value = [];
  }

  return { items, totalCost, itemCount, addItem, clearCart };
});

Asynchronous Components & Suspense

Lazy-loading components for optimal code splitting:

<script setup lang="ts">
import { defineAsyncComponent } from "vue";

const HeavyChart = defineAsyncComponent(
  () => import("./HeavyAnalyticsChart.vue"),
);
</script>

<template>
  <Suspense>
    <template #default>
      <HeavyChart />
    </template>
    <template #fallback>
      <div class="loading-skeleton">Loading interactive chart...</div>
    </template>
  </Suspense>
</template>

Common Patterns

Composable Pattern with Auto-Cleanup

Problem: Encapsulating reactive stateful logic for reuse across multiple components.

Solution: Build typed composables with Vue 3 lifecycle hooks:

import { ref, onMounted, onUnmounted } from "vue";

export function useMouse() {
  const x = ref(0);
  const y = ref(0);

  function update(event: MouseEvent) {
    x.value = event.pageX;
    y.value = event.pageY;
  }

  onMounted(() => window.addEventListener("mousemove", update));
  onUnmounted(() => window.removeEventListener("mousemove", update));

  return { x, y };
}

Best Practices

Do:

  • Use <script setup lang="ts"> as the default syntax for all Vue 3 components.
  • Use Pinia for global state management; deprecate legacy Vuex.
  • Use shallowRef or shallowReactive for large arrays/objects that do not need deep reactivity.
  • Scope component CSS (<style scoped>) to avoid leaking styles across the application.

Don't:

  • Use Options API (data(), methods) in new greenfield TypeScript codebases.
  • Mutate props directly inside child components; emit events for the parent to update state.
  • Use v-if and v-for on the exact same HTML element.

Troubleshooting

Error Cause Solution
TypeError: Cannot set property of undefined in ref Forgetting .value when updating ref in JavaScript. Always read and write refs using .value in <script>: count.value++.
Reactivity lost when destructuring reactive object Destructuring reactive({}) strips the underlying Proxy. Wrap object with toRefs(state) before destructuring.
Component emitted event but no listener registered Event name casing mismatch between component and parent. Use kebab-case for event listeners in templates: @update-item="handle".

References