Skip to content
Back to skills

Vue Nuxt Frontend Patterns

ASecurity

Guides Vue 3 Composition API and Nuxt 3 development — composables, reactivity, SSR data fetching, and component patterns. Use when building Vue or Nuxt components, writing composables, fetching data in a Nuxt page or component, managing shared state with Pinia, or debugging a value that "isn't reactive" after being passed around.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
developmenttypescriptgoreactvuenodedebuggingapifrontend

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add shennawardana23/skillme --skill vue-nuxt-frontend-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Vue Nuxt Frontend Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Vue Nuxt Frontend Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/shennawardana23-vue-nuxt-frontend-patterns/badge)](https://www.skillsdirectory.com/skills/shennawardana23-vue-nuxt-frontend-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: vue-nuxt-frontend-patterns
description: Guides Vue 3 Composition API and Nuxt 3 development — composables, reactivity, SSR data fetching, and component patterns. Use when building Vue or Nuxt components, writing composables, fetching data in a Nuxt page or component, managing shared state with Pinia, or debugging a value that "isn't reactive" after being passed around.
license: Apache-2.0
metadata:
  version: "0.1.0"
---

# Vue 3 / Nuxt 3 Frontend Patterns

Composition API over Options API for new code. Reactivity is opt-in per
value (`ref`/`reactive`), not automatic on plain destructuring — most
"reactivity broke" bugs come from losing that connection, not from Vue
itself misbehaving.

## The reactivity-loss gotcha (highest-value thing to know)

Destructuring a `reactive()` object breaks reactivity on the destructured
values — they become plain, disconnected values at the moment of
destructuring:

```typescript
const state = reactive({ count: 0, name: "task" });

// BROKEN: count is now a plain number, frozen at 0 forever
const { count } = state;

// CORRECT: toRefs preserves the reactive connection
const { count } = toRefs(state);
```

`ref()` values avoid this entirely because the ref object itself is passed
around and only unwrapped at the point of use — prefer `ref()` for
single values, `reactive()` only for grouped object state you won't
destructure before the template.

## Composables

A composable is a function starting with `use` that encapsulates
reusable reactive logic — Vue's equivalent of a custom React hook, but
without the rules-of-hooks ordering constraint, since Vue's reactivity
doesn't depend on call order.

```typescript
// composables/useDebounce.ts
export function useDebounce<T>(source: Ref<T>, delayMs: number) {
  const debounced = ref(source.value) as Ref<T>;
  let timer: ReturnType<typeof setTimeout>;
  watch(source, (value) => {
    clearTimeout(timer);
    timer = setTimeout(() => { debounced.value = value; }, delayMs);
  });
  return debounced;
}
```

## Nuxt 3 data fetching

Use `useFetch`/`useAsyncData` inside `<script setup>` for SSR-safe data
fetching — they dedupe requests between server and client render and
integrate with Nuxt's payload serialization. Use plain `$fetch` only inside
event handlers or outside the SSR render path (a click handler, a
composable called imperatively, not during initial setup).

```vue
<script setup lang="ts">
const route = useRoute();
const { data: reservation, error, pending } = await useFetch(
  () => `/api/reservations/${route.params.id}`
);
</script>
```

## Gotchas

- Calling `$fetch` directly inside `<script setup>`'s top-level body (rather
  than `useFetch`/`useAsyncData`) runs the request twice — once on the
  server during SSR, once again on the client during hydration — because
  there's no dedup/payload mechanism attached to a bare `$fetch` call there.
- Mutating a prop directly (`props.items.push(x)`) works in dev but creates
  a component whose behavior depends on the parent's internal object
  identity — treat props as read-only; emit an event or use `v-model` for
  two-way binding instead.
- A `v-for` without a stable `:key` (using the array index when items can
  reorder or be removed) causes Vue to reuse the wrong DOM node's local
  state across re-renders — a component that "remembers" input from a
  different row after a delete is almost always this.
- Server-rendered output that differs from what the client re-renders
  during hydration (e.g., using `Date.now()` or `Math.random()` directly in
  a template) produces a hydration mismatch — this class of bug is common
  to every SSR framework (Vue, Nuxt, Next, SvelteKit), not unique to Nuxt,
  and the fix is always the same: make the value deterministic between
  server and client, or defer it to a client-only lifecycle hook.

## State sharing (Pinia)

```typescript
export const useReservationStore = defineStore("reservations", () => {
  const items = ref<Reservation[]>([]);
  const confirmed = computed(() => items.value.filter(r => r.status === "confirmed"));
  async function load(hotelId: string) {
    items.value = await $fetch(`/api/hotels/${hotelId}/reservations`);
  }
  return { items, confirmed, load };
});
```

## Verification

- [ ] No `reactive()` object destructured without `toRefs`
- [ ] Data fetching in `<script setup>` uses `useFetch`/`useAsyncData`, not a bare top-level `$fetch`
- [ ] Props are never mutated directly
- [ ] Every `v-for` has a stable, unique `:key`
- [ ] No `Date.now()`/`Math.random()`/browser-only API used directly in a template that renders during SSR

Files in this skill

  • SKILL.md4.5 KB
  • evals/evals.json2.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.