Use Cases (Aktualisiert: 2.6.2026)

Claude Code für Vue 3: Praxisguide mit TypeScript und Pinia

So nutzt du Claude Code für Vue 3, TypeScript, Formulare, Pinia, Composables und Vitest im echten Projekt.

Claude Code für Vue 3: Praxisguide mit TypeScript und Pinia

Claude Code hilft in Vue 3 nicht nur beim schnellen Erzeugen einzelner Komponenten. Der größere Nutzen entsteht, wenn das Tool vorhandene SFCs liest, TypeScript-Typen erhält, wiederverwendbare Logik in Composables verschiebt, gemeinsamen Zustand mit Pinia ordnet und Vitest-Tests ergänzt. Genau diese Schritte kommen in realen Teams häufiger vor als grüne Wiese.

Vue ist flexibel. Diese Flexibilität ist stark, erzeugt aber auch Review-Risiken. Ein Projekt kann Options API, Composition API, script setup, Pinia, alte Vuex-Gewohnheiten, Nuxt-Konventionen und lokale Helper gleichzeitig enthalten. Wenn der Prompt nur “baue ein Vue-Formular” sagt, liefert Claude Code möglicherweise Code, der läuft, aber nicht zur Architektur passt.

Dieser Leitfaden zeigt Claude Code + Vue 3 + TypeScript anhand von fünf Use Cases: bestehende Vue-Komponenten verbessern, ein typisiertes Formular bauen, Pinia-State strukturieren, ein Composable extrahieren und Vitest-Tests hinzufügen. Für die fachliche Basis nutze die offiziellen Seiten zu Vue TypeScript mit Composition API, Pinia und Vitest. Ergänzend passen TypeScript-Tipps und der Testing-Strategie-Guide.

Projektannahmen

Der Ablauf passt zu Vue 3, TypeScript, Vite, Pinia und Vitest. Typische Anwendungen sind interne Tools, Support-Desks, Buchungssysteme, Admin-Oberflächen und Freigabeprozesse. Dort zählen Formulare, Listen, Filter, Pagination, geteilter Zustand und Tests mehr als dekorative UI.

Gib Claude Code vor dem ersten Edit einen Harness, also ein sicheres Arbeitsgerüst für den Agenten. Dazu gehören erlaubte Dateien, Konventionen, Verbote, Prüfkommandos und Review-Fragen. Ohne dieses Gerüst kann Claude Code lokal ein Problem lösen und dabei any, ungeplante UI-Texte oder falsche Zuständigkeiten einführen.

SituationGute Aufgabe für Claude CodeMenschliche Entscheidung
Bestehendes Vue refactorenSFC lesen, Duplikate finden, Typen stärkenUI-Kompatibilität und Release-Risiko
FormularLokaler State, Validierung, Submit-FlowTexte und API-Vertrag
PiniaStore-Typen, Getter, Actions, TestsPersistenz und Zuständigkeit
ComposableFilter, Pagination, WiederverwendungOb Wiederverwendung wirklich nötig ist
TestsNormalfälle, Fehler, RegressionenGewünschter Schutzumfang

Architekturüberblick

Wenn Beispiele größer werden, lautet die wichtigste Frage nicht, ob Claude Code Code schreiben kann. Entscheidend ist, wo die Verantwortung liegt. Klare Grenzen machen den generierten Diff leichter prüfbar.

flowchart LR
  A[Vue SFC] --> B[Composable]
  A --> C[Pinia Store]
  B --> D[Vitest]
  C --> D
  E[Claude Code Prompt] --> A
  E --> B
  E --> C
  E --> D

Das SFC enthält Rendering und Nutzerereignisse. Das Composable enthält wiederverwendbare reaktive Logik wie Filter und Pagination. Der Pinia Store enthält geteilten Anwendungszustand. Vitest schützt Verhalten, das nicht unbeabsichtigt brechen darf.

Vue 3 + TypeScript aufsetzen

Für ein neues Experiment genügt das offizielle Vue-Scaffolding. In einem bestehenden Repository sollte zuerst klar sein, dass Typecheck und Tests im Ausgangszustand erfolgreich sind.

npm create vue@latest support-desk-vue -- --typescript --router --pinia --vitest
cd support-desk-vue
npm install
npm run dev

Schreibe nicht nur “erstelle eine moderne Vue-App”. Nenne Paketmanager, Router, Pinia-Standard und Testkommando. In einem echten Projekt sollte Claude Code zuerst package.json, vite.config.ts, tsconfig.app.json, src/components und src/stores lesen.

Use Case 1: typisiertes Formular-SFC

Ein Formular ist ein guter Start, weil v-model, props, emit, Validierung, Speicherzustand und Store-Actions zusammenkommen. Das Beispiel kann als src/components/TicketForm.vue genutzt werden. Es mutiert keine props, hält Eingaben in lokalen refs und meldet nach dem Speichern die Ticket-ID.

<script setup lang="ts">
import { computed, ref } from 'vue';
import { useTicketStore, type TicketPriority } from '@/stores/ticketStore';

const props = withDefaults(
  defineProps<{
    initialTitle?: string;
    defaultPriority?: TicketPriority;
  }>(),
  {
    initialTitle: '',
    defaultPriority: 'medium',
  },
);

const emit = defineEmits<{
  submitted: [id: string];
}>();

const store = useTicketStore();
const title = ref(props.initialTitle);
const description = ref('');
const priority = ref<TicketPriority>(props.defaultPriority);
const touched = ref(false);

const titleError = computed(() => {
  if (!touched.value) return '';
  if (title.value.trim().length < 5) return 'Use at least 5 characters.';
  return '';
});

const descriptionError = computed(() => {
  if (!touched.value) return '';
  if (description.value.trim().length < 20) return 'Use at least 20 characters.';
  return '';
});

const canSubmit = computed(() => {
  return (
    title.value.trim().length >= 5 &&
    description.value.trim().length >= 20 &&
    !store.isSaving
  );
});

async function submit() {
  touched.value = true;
  if (!canSubmit.value) return;

  const ticket = await store.saveTicket({
    title: title.value.trim(),
    description: description.value.trim(),
    priority: priority.value,
  });

  title.value = '';
  description.value = '';
  priority.value = props.defaultPriority;
  touched.value = false;
  emit('submitted', ticket.id);
}
</script>

<template>
  <form class="ticket-form" @submit.prevent="submit">
    <label>
      Title
      <input v-model="title" name="title" @blur="touched = true" />
    </label>
    <p v-if="titleError" class="error">{{ titleError }}</p>

    <label>
      Description
      <textarea v-model="description" name="description" @blur="touched = true" />
    </label>
    <p v-if="descriptionError" class="error">{{ descriptionError }}</p>

    <label>
      Priority
      <select v-model="priority" name="priority">
        <option value="low">Low</option>
        <option value="medium">Medium</option>
        <option value="high">High</option>
      </select>
    </label>

    <button type="submit" :disabled="!canSubmit">
      {{ store.isSaving ? 'Saving...' : 'Create ticket' }}
    </button>
  </form>
</template>

Für solche Komponenten sollte der Prompt klare Verbote enthalten: props nicht mutieren, kein any, keine neue UI-Bibliothek und keine API-Details im SFC. Diese Regeln verhindern teure Nacharbeit.

Use Case 2: geteilter Zustand mit Pinia

Wenn mehrere Screens die Ticketliste brauchen, darf das Formular nicht den gesamten Zustand besitzen. Pinia hält Liste, Speicherstatus, Getter und Actions an einer klaren Stelle.

import { computed, ref } from 'vue';
import { defineStore } from 'pinia';

export type TicketPriority = 'low' | 'medium' | 'high';
export type TicketStatus = 'open' | 'closed';

export interface Ticket {
  id: string;
  title: string;
  description: string;
  priority: TicketPriority;
  status: TicketStatus;
  createdAt: string;
}

export type NewTicketInput = Pick<Ticket, 'title' | 'description' | 'priority'>;

export const useTicketStore = defineStore('tickets', () => {
  const tickets = ref<Ticket[]>([]);
  const isSaving = ref(false);

  const openTickets = computed(() => {
    return tickets.value.filter((ticket) => ticket.status === 'open');
  });

  function addTicket(input: NewTicketInput) {
    const ticket: Ticket = {
      id: crypto.randomUUID(),
      ...input,
      status: 'open',
      createdAt: new Date().toISOString(),
    };

    tickets.value.unshift(ticket);
    return ticket;
  }

  async function saveTicket(input: NewTicketInput) {
    isSaving.value = true;
    try {
      await new Promise((resolve) => setTimeout(resolve, 150));
      return addTicket(input);
    } finally {
      isSaving.value = false;
    }
  }

  function closeTicket(id: string) {
    const ticket = tickets.value.find((item) => item.id === id);
    if (ticket) ticket.status = 'closed';
  }

  return {
    tickets,
    isSaving,
    openTickets,
    addTicket,
    saveTicket,
    closeTicket,
  };
});

In Produktion ruft saveTicket einen typisierten API-Client auf. Trotzdem ist es besser, erst das Store-Modell zu prüfen, danach Netzwerklogik zu ergänzen und zuletzt Fehlerbehandlung einzubauen.

Use Case 3: Composable extrahieren

Ein Composable ist eine wiederverwendbare Funktion mit Vue-Reaktivität. Einfach gesagt: Logik wird aus dem Bildschirm herausgelöst. Suche, Prioritätsfilter und Pagination sind gute Kandidaten.

import { computed, ref, watch, type Ref } from 'vue';
import type { Ticket, TicketPriority } from '@/stores/ticketStore';

export function useFilteredTickets(tickets: Ref<Ticket[]>) {
  const query = ref('');
  const selectedPriority = ref<TicketPriority | 'all'>('all');
  const currentPage = ref(1);
  const pageSize = ref(10);

  const filteredTickets = computed(() => {
    const normalizedQuery = query.value.trim().toLowerCase();

    return tickets.value.filter((ticket) => {
      const matchesQuery =
        normalizedQuery.length === 0 ||
        ticket.title.toLowerCase().includes(normalizedQuery) ||
        ticket.description.toLowerCase().includes(normalizedQuery);

      const matchesPriority =
        selectedPriority.value === 'all' ||
        ticket.priority === selectedPriority.value;

      return matchesQuery && matchesPriority;
    });
  });

  const totalPages = computed(() => {
    return Math.max(1, Math.ceil(filteredTickets.value.length / pageSize.value));
  });

  const pagedTickets = computed(() => {
    const start = (currentPage.value - 1) * pageSize.value;
    return filteredTickets.value.slice(start, start + pageSize.value);
  });

  watch([query, selectedPriority], () => {
    currentPage.value = 1;
  });

  return {
    query,
    selectedPriority,
    currentPage,
    pageSize,
    filteredTickets,
    pagedTickets,
    totalPages,
  };
}

watch hat hier nur eine Aufgabe: Wenn Filter wechseln, springt die Seite auf 1 zurück. Ableitungen bleiben in computed. Diese Regel sollte in Claude-Code-Prompts stehen.

Use Case 4: Vitest-Tests hinzufügen

Je schneller Claude Code implementiert, desto wichtiger werden Regressionstests. Beginne mit dem Store, weil er nah am Fachverhalten liegt und weniger fragil ist als DOM-Tests.

import { createPinia, setActivePinia } from 'pinia';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useTicketStore } from './ticketStore';

describe('useTicketStore', () => {
  beforeEach(() => {
    setActivePinia(createPinia());
    vi.stubGlobal('crypto', {
      randomUUID: () => 'ticket-1',
    });
  });

  it('adds a new open ticket', () => {
    const store = useTicketStore();

    const ticket = store.addTicket({
      title: 'Cannot submit expense form',
      description: 'The submit button stays disabled after all fields are filled.',
      priority: 'high',
    });

    expect(ticket.id).toBe('ticket-1');
    expect(store.tickets).toHaveLength(1);
    expect(store.openTickets).toHaveLength(1);
  });

  it('closes an existing ticket', () => {
    const store = useTicketStore();
    const ticket = store.addTicket({
      title: 'Profile image is broken',
      description: 'The image URL returns 404 on the account settings page.',
      priority: 'medium',
    });

    store.closeTicket(ticket.id);

    expect(store.openTickets).toHaveLength(0);
    expect(store.tickets[0]?.status).toBe('closed');
  });
});

Bitte nicht nur “mehr Tests” anfordern. Besser sind konkrete Verhaltensfälle: zu kurze Eingabe, gültige Eingabe, Speichern, Erfolg, Fehler und Store-Zustand.

Prompt für bestehende Vue-Komponenten

Bei bestehendem Code ist ein großer Rewrite riskant. Lass Claude Code erst lesen und Risiken nennen, dann Typen ergänzen, ein Composable extrahieren, Pinia bei Bedarf ordnen und Tests hinzufügen.

You are working in a Vue 3 + TypeScript + Pinia project.

Goal:
Refactor the ticket form so validation logic is typed, reusable, and covered by Vitest.

Allowed files:
- src/components/TicketForm.vue
- src/composables/useFilteredTickets.ts
- src/stores/ticketStore.ts
- src/stores/ticketStore.test.ts

Rules:
- Use <script setup lang="ts">.
- Do not mutate props directly.
- Do not introduce any.
- Prefer computed for derived values.
- Use watch only for explicit side effects.
- Keep UI text unchanged unless a validation message is missing.

Verification:
- npm run typecheck
- npm run test:unit

After editing, explain the risk areas and the tests you added.

Der Prompt trennt Werkzeugentscheidungen und Produktentscheidungen. Claude Code kann Extraktion, Typen und Testentwürfe liefern. Menschen prüfen Texte, API-Verträge, Barrierefreiheit und Release-Timing.

Häufige Fallstricke

Erster Fallstrick: Options API und Composition API ohne Plan mischen. Vue 3 erlaubt beides, aber data, methods, setup und script setup in einem Baustein sind schwer zu prüfen.

Zweiter Fallstrick: ref und reactive falsch verstehen. Für primitive Werte, Arrays und austauschbare Werte ist ref oft einfacher. reactive passt für zusammenhängende Objekte, kann aber beim Destructuring überraschen.

Dritter Fallstrick: props mutieren. Ein Kind sollte Zustand des Elternteils nicht heimlich ändern. Nutze lokale refs, emit oder ein vereinbartes defineModel.

Vierter Fallstrick: watch übernutzen. Ableitbare Werte gehören in computed. watch ist für Routen-Synchronisierung, Seitenreset, API-Aufrufe oder Storage-Schreibvorgänge.

Fünfter Fallstrick: Typen in any fallen lassen. Claude Code kann as any verwenden, um einen Fehler zu umgehen. Prüfe any, unknown, Assertions und leere Array-Inferenz.

Sechster Fallstrick: SFCs zu stark aufteilen. Viele nicht wiederverwendete Kleinkomponenten erzeugen vor allem props und emits. Erst Logik in Composables, dann UI aufteilen.

Siebter Fallstrick: zu wenig Tests. Generierter Code kann sauber wirken und Randfälle verfehlen. Bei Formularen sollten kurze Eingabe, gültige Eingabe, Speichern, Erfolg, Fehler und Store-Änderung geprüft werden.

Praxisablauf und CTA

Starte im echten Repository nicht mit “baue den ganzen Screen”. Lass Claude Code erst Risiken sammeln, mache dann eine kleine Änderung, führe Typecheck und Tests aus und gehe weiter. Kleine Diffs ergeben bessere Reviews.

Für Teams gehören Regeln in CLAUDE.md: Vue mit script setup, geteilter Zustand in Pinia setup stores, abgeleitete Werte in computed, Nebenwirkungen in watch, Prüfung mit npm run typecheck und npm run test:unit. ClaudeCodeLab unterstützt beim Aufbau solcher Regeln über Claude Code Training und Beratung.

Zusammenfassung

Claude Code beschleunigt Vue-3-Refactors, Formulare, Pinia, Composables und Tests. Die Qualität hängt vom Prompt-Rahmen ab: erlaubte Dateien, Vue-Stil, unveränderliche Bereiche und Verifikation müssen klar sein.

Einsteiger sollten mit einem SFC, einem Composable, einem Store und einer Testdatei beginnen. Dieser kleine Zyklus vermittelt das Vue-Denken und erzeugt trotzdem produktionsnahen Code.

Ergebnis des Praxistests

Ich habe den Ablauf in einem kleinen Vue 3 + TypeScript-Projekt getestet. Formular, Store, Composable und Tests in einer Anfrage zu bestellen erzeugte einen großen Diff und längere Review-Zeit. Getrennt nach SFC, Pinia, Composable und Vitest war die Prüfung einfacher. Besonders wirksam waren die Regeln “props nicht mutieren”, “kein any einführen” und “abgeleitete Werte mit computed schreiben”. Danach ließen sich die Änderungen gut mit npm run typecheck und npm run test:unit prüfen.

#Claude Code #Vue.js #Nuxt.js #Pinia #Composition API
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.