home / skills / doanchienthangdev / omgkit / vue

This skill helps you bootstrap production-ready Vue 3 apps using Composition API, TypeScript, Pinia, and Vue Router.

npx playbooks add skill doanchienthangdev/omgkit --skill vue

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
4.5 KB
---
name: building-vue-apps
description: Builds production Vue 3 applications with Composition API, TypeScript, Pinia, and Vue Router. Use when creating Vue.js SPAs, component libraries, or reactive web interfaces.
---

# Vue.js

## Quick Start

```vue
<script setup lang="ts">
import { ref, computed } from 'vue';

const count = ref(0);
const doubled = computed(() => count.value * 2);
</script>

<template>
  <button @click="count++">
    Count: {{ count }} (doubled: {{ doubled }})
  </button>
</template>
```

## Features

| Feature | Description | Guide |
|---------|-------------|-------|
| Composition API | Reactive state, composables, lifecycle | [COMPOSITION.md](COMPOSITION.md) |
| TypeScript | Props, emits, refs typing | [TYPESCRIPT.md](TYPESCRIPT.md) |
| Pinia | State management, stores, plugins | [PINIA.md](PINIA.md) |
| Vue Router | Navigation, guards, lazy loading | [ROUTER.md](ROUTER.md) |
| Testing | Vitest, Vue Test Utils patterns | [TESTING.md](TESTING.md) |
| Performance | Lazy loading, memoization, virtual lists | [PERFORMANCE.md](PERFORMANCE.md) |

## Common Patterns

### Component with Props and Emits

```vue
<script setup lang="ts">
interface Props {
  user: { id: string; name: string; email: string };
  showActions?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  showActions: true,
});

const emit = defineEmits<{
  (e: 'edit', user: Props['user']): void;
  (e: 'delete', id: string): void;
}>();
</script>

<template>
  <div class="user-card">
    <h3>{{ user.name }}</h3>
    <p>{{ user.email }}</p>
    <div v-if="showActions">
      <button @click="emit('edit', user)">Edit</button>
      <button @click="emit('delete', user.id)">Delete</button>
    </div>
  </div>
</template>
```

### Composable for Reusable Logic

```typescript
// composables/useFetch.ts
import { ref, watch, type Ref } from 'vue';

export function useFetch<T>(url: Ref<string>) {
  const data = ref<T | null>(null);
  const loading = ref(false);
  const error = ref<Error | null>(null);

  async function fetchData() {
    loading.value = true;
    error.value = null;
    try {
      const response = await fetch(url.value);
      data.value = await response.json();
    } catch (e) {
      error.value = e as Error;
    } finally {
      loading.value = false;
    }
  }

  watch(url, fetchData, { immediate: true });

  return { data, loading, error, refetch: fetchData };
}
```

### Pinia Store

```typescript
// stores/user.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useUserStore = defineStore('user', () => {
  const user = ref<User | null>(null);
  const token = ref<string | null>(null);

  const isAuthenticated = computed(() => !!user.value);

  async function login(credentials: { email: string; password: string }) {
    const res = await authService.login(credentials);
    user.value = res.user;
    token.value = res.token;
  }

  function logout() {
    user.value = null;
    token.value = null;
  }

  return { user, token, isAuthenticated, login, logout };
});
```

## Workflows

### Component Development

1. Define props interface with TypeScript
2. Define emits with typed events
3. Use `<script setup>` syntax
4. Create composables for reusable logic
5. Write tests with Vitest + Vue Test Utils

### Router Setup

```typescript
const routes = [
  { path: '/', component: () => import('./views/Home.vue') },
  {
    path: '/dashboard',
    component: () => import('./views/Dashboard.vue'),
    meta: { requiresAuth: true },
  },
];

router.beforeEach((to, from, next) => {
  const userStore = useUserStore();
  if (to.meta.requiresAuth && !userStore.isAuthenticated) {
    next({ path: '/login', query: { redirect: to.fullPath } });
  } else {
    next();
  }
});
```

## Best Practices

| Do | Avoid |
|----|-------|
| Use Composition API with `<script setup>` | Options API in new code |
| Type props/emits with interfaces | `any` types |
| Create composables for shared logic | Duplicating reactive logic |
| Use Pinia for global state | Overusing provide/inject |
| Lazy load routes and components | Bundling everything upfront |

## Project Structure

```
src/
├── App.vue
├── main.ts
├── components/         # Reusable components
├── composables/        # Composition functions
├── views/              # Page components
├── stores/             # Pinia stores
├── router/             # Route definitions
├── services/           # API services
└── types/              # TypeScript types
```

For detailed examples and patterns, see reference files above.

Overview

This skill builds production-ready Vue 3 applications using the Composition API, TypeScript, Pinia, and Vue Router. It provides practical patterns, composables, typed components, store examples, and router workflows to scaffold SPAs, component libraries, and reactive interfaces. The focus is on maintainable, testable, and performant Vue projects.

How this skill works

The skill supplies ready-to-adopt patterns and code snippets for <script setup> with TypeScript, composables for shared logic, Pinia stores for global state, and lazy-loaded router configurations with navigation guards. It also outlines testing and performance strategies like Vitest patterns, lazy loading, and virtual lists to optimize bundle size and runtime behavior.

When to use it

  • Starting a new Vue 3 SPA with TypeScript and modern patterns.
  • Building a component library or set of reusable UI components.
  • Implementing global state and auth flows using Pinia.
  • Adding typed props, emits, and composables to an existing app.
  • Setting up lazy-loaded routes and route guards for protected pages.

Best practices

  • Use Composition API and <script setup> for new components.
  • Type props, emits, refs, and composables to avoid any and runtime bugs.
  • Create composables for shared async logic, fetching, and stateful behavior.
  • Prefer Pinia stores for predictable global state and keep stores small and focused.
  • Lazy load routes and components to reduce initial bundle size and improve performance.

Example use cases

  • A dashboard SPA with authenticated routes and lazy-loaded feature modules.
  • A reusable user-card component with typed props and typed emits for edit/delete actions.
  • A composable useFetch(url) that handles loading, errors, and auto-refetch on URL change.
  • A Pinia-based auth store that exposes login, logout, and isAuthenticated computed state.
  • Unit testing components and composables with Vitest and Vue Test Utils patterns.

FAQ

Should I still use the Options API for new code?

No. Prefer the Composition API and <script setup> for new code for better type inference and composability.

How do I protect routes that require authentication?

Use router.beforeEach with a Pinia auth store check. Redirect unauthenticated users to /login and preserve the original target in query params.