Harness-engineering nuxt-composables-pattern

Nuxt Composables Pattern

install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/nuxt-composables-pattern" ~/.claude/skills/intense-visions-harness-engineering-nuxt-composables-pattern && rm -rf "$T"
manifest: agents/skills/claude-code/nuxt-composables-pattern/SKILL.md
source content

Nuxt Composables Pattern

Fetch data and coordinate async state across server and client using Nuxt's built-in composables

When to Use

  • You need to fetch data in a Nuxt page or component with SSR/SSG support
  • You want to avoid duplicate requests (server fetches, client rehydrates)
  • You are choosing between
    useFetch
    ,
    useAsyncData
    , and
    useLazyFetch
  • You need access to the Nuxt application instance (
    useNuxtApp
    ) for plugins or runtime config

Instructions

  1. Use
    useFetch
    as the default for HTTP requests — it wraps
    useAsyncData
    +
    $fetch
    and handles SSR deduplication automatically:
// pages/posts/[id].vue
const { data: post, pending, error } = await useFetch(`/api/posts/${route.params.id}`);
  1. Use
    useAsyncData
    when the async operation is not a simple HTTP call (e.g., database query via a server composable, computed key based on reactive state):
const { data: user } = await useAsyncData(
  'user-profile', // unique cache key — required
  () => fetchUserProfile(userId.value),
  { watch: [userId] } // re-fetch when userId changes
);
  1. Use
    useLazyFetch
    /
    useLazyAsyncData
    to defer data loading — the page renders immediately and
    pending
    is true until data arrives:
// Good for non-critical data below the fold
const { data: comments, pending } = useLazyFetch('/api/comments');
  1. Always provide a unique string key to

    useAsyncData
    . Keys collide across components — use route params or namespacing to avoid cache pollution.

  2. Use

    $fetch
    directly only in event handlers (button clicks, form submissions) — not at the top of
    setup()
    , which would bypass SSR deduplication:

// Good: event handler
async function submitForm() {
  await $fetch('/api/contact', { method: 'POST', body: formData.value });
}

// Bad: top-level setup — use useFetch instead
const data = await $fetch('/api/posts'); // runs on both server AND client
  1. Transform responses inline with the
    transform
    option to avoid storing raw API shapes:
const { data: posts } = await useFetch('/api/posts', {
  transform: (raw) => raw.items.map((p) => ({ id: p.id, title: p.title })),
});
  1. Access Nuxt internals with
    useNuxtApp()
    :
const { $toast, $i18n, ssrContext } = useNuxtApp();

Details

Nuxt's data fetching composables solve a fundamental SSR problem: if you use plain

fetch()
or
axios
in
setup()
, the request fires on the server during SSR and again on the client during hydration — doubling your API calls.
useFetch
and
useAsyncData
solve this by serializing the server-fetched data into the HTML payload and rehydrating it on the client without a second network request.

Key differences:

ComposableUse caseBlocks navigation
useFetch
Simple HTTP callsYes (await)
useAsyncData
Custom async logic, reactive keysYes (await)
useLazyFetch
Non-blocking HTTPNo
useLazyAsyncData
Non-blocking custom asyncNo
$fetch
Event handlers onlyN/A

Cache key design:

useAsyncData
keys are global within a Nuxt app instance. Two components using
useAsyncData('posts', ...)
will share data. This is often desired (shared cache) but causes bugs when the same key is used with different fetch functions. Pattern:
<entity>-<id>
or
<page>-<section>
.

Error handling:

const { data, error } = await useFetch('/api/posts');
if (error.value) {
  throw createError({ statusCode: 500, message: 'Failed to load posts' });
}

Refreshing data:

const { data, refresh } = await useFetch('/api/posts');
// Later:
await refresh();

Source

https://nuxt.com/docs/getting-started/data-fetching

Process

  1. Read the instructions and examples in this document.
  2. Apply the patterns to your implementation, adapting to your specific context.
  3. Verify your implementation against the details and edge cases listed above.

Harness Integration

  • Type: knowledge — this skill is a reference document, not a procedural workflow.
  • No tools or state — consumed as context by other skills and agents.

Success Criteria

  • The patterns described in this document are applied correctly in the implementation.
  • Edge cases and anti-patterns listed in this document are avoided.