Back to Skills

vue-application-structure

aj-geddes
Updated Today
13 views
7
7
View on GitHub
Metaapidesign

About

This skill helps developers structure scalable Vue 3 applications using the Composition API and TypeScript. It provides guidance on component organization, creating reusable composables, and managing complex state. Use it when building large-scale Vue applications to ensure proper separation of concerns and maintainability.

Documentation

Vue Application Structure

Overview

Build well-organized Vue 3 applications using Composition API, proper file organization, and TypeScript for type safety and maintainability.

When to Use

  • Large-scale Vue applications
  • Component library development
  • Reusable composable hooks
  • Complex state management
  • Performance optimization

Implementation Examples

1. Vue 3 Composition API Component

// useCounter.ts (Composable)
import { ref, computed } from 'vue';

export function useCounter(initialValue = 0) {
  const count = ref(initialValue);

  const doubled = computed(() => count.value * 2);
  const increment = () => count.value++;
  const decrement = () => count.value--;
  const reset = () => count.value = initialValue;

  return {
    count,
    doubled,
    increment,
    decrement,
    reset
  };
}

// Counter.vue
<template>
  <div class="counter">
    <p>Count: {{ count }}</p>
    <p>Doubled: {{ doubled }}</p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="reset">Reset</button>
  </div>
</template>

<script setup lang="ts">
import { useCounter } from './useCounter';

const { count, doubled, increment, decrement, reset } = useCounter(0);
</script>

<style scoped>
.counter {
  padding: 20px;
  border: 1px solid #ccc;
}
</style>

2. Async Data Fetching Composable

// useFetch.ts
import { ref, computed, onMounted } from 'vue';

interface UseFetchOptions {
  immediate?: boolean;
}

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

  const isLoading = computed(() => loading.value);
  const hasError = computed(() => error.value !== null);

  const fetch = async () => {
    loading.value = true;
    error.value = null;

    try {
      const response = await globalThis.fetch(url);
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      data.value = await response.json();
    } catch (e) {
      error.value = e instanceof Error ? e : new Error(String(e));
    } finally {
      loading.value = false;
    }
  };

  const refetch = () => fetch();

  if (options.immediate !== false) {
    onMounted(fetch);
  }

  return {
    data,
    loading: isLoading,
    error: hasError,
    fetch,
    refetch
  };
}

// UserList.vue
<template>
  <div>
    <button @click="refetch">Refresh</button>
    <p v-if="loading">Loading...</p>
    <p v-if="error" class="text-red-500">Error loading users</p>
    <ul v-else>
      <li v-for="user in data" :key="user.id">{{ user.name }}</li>
    </ul>
  </div>
</template>

<script setup lang="ts">
import { useFetch } from './useFetch';

interface User {
  id: number;
  name: string;
}

const { data, loading, error, refetch } = useFetch<User[]>('/api/users');
</script>

3. Component Organization Structure

src/
├── components/
│   ├── common/
│   │   ├── Button.vue
│   │   ├── Card.vue
│   │   └── Modal.vue
│   ├── forms/
│   │   ├── FormInput.vue
│   │   └── FormSelect.vue
│   └── layouts/
│       ├── Header.vue
│       └── Sidebar.vue
├── composables/
│   ├── useCounter.ts
│   ├── useFetch.ts
│   └── useForm.ts
├── services/
│   ├── api.ts
│   └── auth.ts
├── stores/
│   ├── user.ts
│   └── auth.ts
├── types/
│   ├── models.ts
│   └── api.ts
├── App.vue
└── main.ts

4. Form Handling Composable

// useForm.ts
import { ref, reactive } from 'vue';

interface UseFormOptions<T> {
  onSubmit: (data: T) => Promise<void>;
  initialValues: T;
}

export function useForm<T extends Record<string, any>>(
  options: UseFormOptions<T>
) {
  const formData = reactive<T>(options.initialValues);
  const errors = reactive<Record<string, string>>({});
  const isSubmitting = ref(false);

  const handleSubmit = async (e?: Event) => {
    e?.preventDefault();
    isSubmitting.value = true;

    try {
      await options.onSubmit(formData);
    } catch (error) {
      const err = error as any;
      if (err.fieldErrors) {
        Object.assign(errors, err.fieldErrors);
      }
    } finally {
      isSubmitting.value = false;
    }
  };

  const reset = () => {
    Object.assign(formData, options.initialValues);
    Object.keys(errors).forEach(key => delete errors[key]);
  };

  return {
    formData,
    errors,
    isSubmitting,
    handleSubmit,
    reset
  };
}

// LoginForm.vue
<template>
  <form @submit="handleSubmit">
    <input v-model="formData.email" type="email" />
    <span v-if="errors.email" class="error">{{ errors.email }}</span>

    <input v-model="formData.password" type="password" />
    <span v-if="errors.password" class="error">{{ errors.password }}</span>

    <button type="submit" :disabled="isSubmitting">Login</button>
  </form>
</template>

<script setup lang="ts">
import { useForm } from './useForm';

const { formData, errors, isSubmitting, handleSubmit } = useForm({
  initialValues: { email: '', password: '' },
  onSubmit: async (data) => {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(data)
    });
    if (!response.ok) throw new Error('Login failed');
  }
});
</script>

5. Pinia Store (State Management)

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

interface User {
  id: number;
  name: string;
  email: string;
}

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

  const isLoggedIn = computed(() => user.value !== null);

  const fetchUser = async (id: number) => {
    isLoading.value = true;
    try {
      const response = await fetch(`/api/users/${id}`);
      user.value = await response.json();
    } finally {
      isLoading.value = false;
    }
  };

  const logout = () => {
    user.value = null;
  };

  return {
    user,
    isLoading,
    isLoggedIn,
    fetchUser,
    logout
  };
});

// Usage in component
import { useUserStore } from '@/stores/user';

export default {
  setup() {
    const userStore = useUserStore();
    userStore.fetchUser(1);
    return { userStore };
  }
};

Best Practices

  • Organize by features or domains
  • Use Composition API for logic reuse
  • Extract composables for shared logic
  • Use TypeScript for type safety
  • Implement proper error handling
  • Keep components focused and testable
  • Use Pinia for state management

Resources

Quick Install

/plugin add https://github.com/aj-geddes/useful-ai-prompts/tree/main/vue-application-structure

Copy and paste this command in Claude Code to install this skill

GitHub 仓库

aj-geddes/useful-ai-prompts
Path: skills/vue-application-structure

Related Skills

evaluating-llms-harness

Testing

This Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.

View skill

langchain

Meta

LangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.

View skill

Algorithmic Art Generation

Meta

This skill helps developers create algorithmic art using p5.js, focusing on generative art, computational aesthetics, and interactive visualizations. It automatically activates for topics like "generative art" or "p5.js visualization" and guides you through creating unique algorithms with features like seeded randomness, flow fields, and particle systems. Use it when you need to build reproducible, code-driven artistic patterns.

View skill

webapp-testing

Testing

This Claude Skill provides a Playwright-based toolkit for testing local web applications through Python scripts. It enables frontend verification, UI debugging, screenshot capture, and log viewing while managing server lifecycles. Use it for browser automation tasks but run scripts directly rather than reading their source code to avoid context pollution.

View skill