skills/ secondsky/claude-skills

nuxt-production

| Nuxt 4 production optimization: hydration, performance, testing with Vitest, deployment to Cloudflare/Vercel/Netlify, and v4 migration. Use when: debugging hydration mismatches, optimizing performance and Core Web Vitals, writing tests with Vitest, deploying to Cloudflare Pages/Workers/Vercel/Netl

0
Installs
—
Rating
—
Success rate
7
Files scanned
Scan passeddevops
Source on GitHub

Security scan

Scan passed

No risky patterns were found in the scanned files.

7 files scannedscanner v1.2.0Oct 11, 2026

Content sha256 22bb0220573a0a5d… — run codexguild_scan_skills after installing to verify your local copy.

Static analysis is a first line of defense, not a guarantee. Read the source

SKILL.md

exact scanned copy

Nuxt 4 Production Guide

Hydration, performance, testing, deployment, and migration patterns.

What's New in Nuxt 4

v4.2 Features (Latest)

1. Abort Control for Data Fetching

const controller = ref<AbortController>()

const { data } = await useAsyncData(
  'users',
  () => $fetch('/api/users', { signal: controller.value?.signal })
)

const abortRequest = () => {
  controller.value?.abort()
  controller.value = new AbortController()
}

2. Async Data Handler Extraction

  • 39% smaller client bundles
  • Data fetching logic extracted to server chunks
  • Automatic optimization (no config needed)

3. Enhanced Error Handling

  • Dual error display: custom error page + technical overlay
  • Better error messages in development

v4.1 Features

1. Enhanced Chunk Stability

  • Import maps prevent cascading hash changes
  • Better long-term caching

2. Lazy Hydration

<script setup>
const LazyComponent = defineLazyHydrationComponent(() =>
  import('./HeavyComponent.vue')
)
</script>

Breaking Changes from v3

Changev3v4
Source directoryRootapp/
Data reactivityDeepShallow (default)
Default valuesnullundefined
Route middlewareClientServer
App manifestOpt-inDefault

When to Load References

Load references/hydration.md when:

  • Debugging "Hydration node mismatch" errors
  • Implementing ClientOnly components
  • Fixing non-deterministic rendering issues
  • Understanding SSR vs client rendering

Load references/performance.md when:

  • Optimizing Core Web Vitals scores
  • Implementing lazy loading and code splitting
  • Configuring caching strategies
  • Reducing bundle size

Load references/testing-vitest.md when:

  • Writing component tests with @nuxt/test-utils
  • Testing composables with Nuxt context
  • Mocking Nuxt APIs (useFetch, useRoute)
  • Setting up Vitest configuration

Load references/deployment-cloudflare.md when:

  • Deploying to Cloudflare Pages or Workers
  • Configuring wrangler.toml
  • Setting up NuxtHub integration
  • Working with D1, KV, R2 bindings

Hydration Best Practices

What Causes Hydration Mismatches

CauseExampleFix
Non-deterministic valuesMath.random()Use useState
Browser APIs on serverwindow.innerWidthUse onMounted
Date/time on servernew Date()Use useState or ClientOnly
Third-party scriptsAnalyticsUse ClientOnly

Fix Patterns

Non-deterministic Values:

<!-- WRONG -->
<script setup>
const id = Math.random()
</script>

<!-- CORRECT -->
<script setup>
const id = useState('random-id', () => Math.random())
</script>

Browser APIs:

<!-- WRONG -->
<script setup>
const width = window.innerWidth  // Crashes on server!
</script>

<!-- CORRECT -->
<script setup>
const width = ref(0)
onMounted(() => {
  width.value = window.innerWidth
})
</script>

ClientOnly Component:

<template>
  <!-- Wrap client-only content -->
  <ClientOnly>
    <MyMapComponent />
    <template #fallback>
      <div class="skeleton">Loading map...</div>
    </template>
  </ClientOnly>
</template>

Conditional Rendering:

<script setup>
const showWidget = ref(false)

onMounted(() => {
  // Only show after hydration
  showWidget.value = true
})
</script>

<template>
  <AnalyticsWidget v-if="showWidget" />
</template>

Performance Optimization

Lazy Loading Components

<script setup>
// Lazy load heavy components
const HeavyChart = defineAsyncComponent(() =>
  import('~/components/HeavyChart.vue')
)

// With loading/error states
const HeavyChart = defineAsyncComponent({
  loader: () => import('~/components/HeavyChart.vue'),
  loadingComponent: LoadingSpinner,
  errorComponent: ErrorFallback,
  delay: 200,
  timeout: 10000
})
</script>

<template>
  <Suspense>
    <HeavyChart :data="chartData" />
    <template #fallback>
      <LoadingSpinner />
    </template>
  </Suspense>
</template>

Lazy Hydration

<script setup>
// Hydrate when visible in viewport
const LazyComponent = defineLazyHydrationComponent(
  () => import('./HeavyComponent.vue'),
  { hydrate: 'visible' }
)

// Hydrate on user interaction
const InteractiveComponent = defineLazyHydrationComponent(
  () => import('./InteractiveComponent.vue'),
  { hydrate: 'interaction' }
)

// Hydrate when browser is idle
const IdleComponent = defineLazyHydrationComponent(
  () => import('./IdleComponent.vue'),
  { hydrate: 'idle' }
)
</script>

Route Caching

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // Static pages (prerendered at build)
    '/': { prerender: true },
    '/about': { prerender: true },

    // SWR caching (1 hour)
    '/blog/**': { swr: 3600 },

    // ISR (regenerate every hour)
    '/products/**': { isr: 3600 },

    // SPA mode (no SSR)
    '/dashboard/**': { ssr: false },

    // Static with CDN caching
    '/static/**': {
      headers: { 'Cache-Control': 'public, max-age=31536000' }
    }
  }
})

Image Optimization

<template>
  <!-- Automatic optimization with NuxtImg -->
  <NuxtImg
    src="/images/hero.jpg"
    alt="Hero image"
    width="800"
    height="400"
    loading="lazy"
    placeholder
    format="webp"
  />

  <!-- Responsive images -->
  <NuxtPicture
    src="/images/product.jpg"
    alt="Product"
    sizes="sm:100vw md:50vw lg:400px"
    :modifiers="{ quality: 80 }"
  />
</template>

Testing with Vitest

Setup

bun add -d @nuxt/test-utils vitest @vue/test-utils happy-dom
// vitest.config.ts
import { defineVitestConfig } from '@nuxt/test-utils/config'

export default defineVitestConfig({
  test: {
    environment: 'nuxt',
    environmentOptions: {
      nuxt: {
        domEnvironment: 'happy-dom'
      }
    }
  }
})

Component Testing

// tests/components/UserCard.test.ts
import { describe, it, expect } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import UserCard from '~/components/UserCard.vue'

describe('UserCard', () => {
  it('renders user name', async () => {
    const wrapper = await mountSuspended(UserCard, {
      props: {
        user: { id: 1, name: 'John Doe', email: 'john@example.com' }
      }
    })

    expect(wrapper.text()).toContain('John Doe')
    expect(wrapper.text()).toContain('john@example.com')
  })

  it('emits delete event', async () => {
    const wrapper = await mountSuspended(UserCard, {
      props: { user: { id: 1, name: 'John' } }
    })

    await wrapper.find('[data-test="delete-btn"]').trigger('click')

    expect(wrapper.emitted('delete')).toHaveLength(1)
    expect(wrapper.emitted('delete')[0]).toEqual([1])
  })
})

Mocking Composables

// tests/components/Dashboard.test.ts
import { describe, it, expect, vi } from 'vitest'
import { mountSuspended, mockNuxtImport } from '@nuxt/test-utils/runtime'
import Dashboard from '~/pages/dashboard.vue'

// Mock useFetch
mockNuxtImport('useFetch', () => {
  return () => ({
    data: ref({ users: [{ id: 1, name: 'John' }] }),
    pending: ref(false),
    error: ref(null)
  })
})

describe('Dashboard', () => {
  it('displays users from API', async () => {
    const wrapper = await mountSuspended(Dashboard)

    expect(wrapper.text()).toContain('John')
  })
})

Testing Server Routes

// tests/api/users.test.ts
import { describe, it, expect } from 'vitest'
import { $fetch, setup } from '@nuxt/test-utils/e2e'

describe('API: /api/users', async () => {
  await setup({ server: true })

  it('returns users list', async () => {
    const users = await $fetch('/api/users')

    expect(users).toHaveProperty('users')
    expect(Array.isArray(users.users)).toBe(true)
  })

  it('creates a new user', async () => {
    const result = await $fetch('/api/users', {
      method: 'POST',
      body: { name: 'Jane', email: 'jane@example.com' }
    })

    expect(result.user.name).toBe('Jane')
  })
})

Deployment

Cloudflare Pages (Recommended)

# Build and deploy
bun run build
bunx wrangler pages deploy .output/public
// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare-pages'
  }
})

Cloudflare Workers

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare-module'
  }
})
# wrangler.toml
name = "my-nuxt-app"
compatibility_date = "2025-01-01"
compatibility_flags = ["nodejs_compat"]

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxx-xxx-xxx"

[[kv_namespaces]]
binding = "KV"
id = "xxx-xxx-xxx"

Vercel

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'vercel'
  }
})

Netlify

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'netlify'
  }
})

NuxtHub (Cloudflare All-in-One)

bun add @nuxthub/core
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxthub/core'],

  hub: {
    database: true,  // D1
    kv: true,        // KV
    blob: true,      // R2
    cache: true      // Cache API
  }
})
// Usage in server routes
export default defineEventHandler(async (event) => {
  const db = hubDatabase()
  const kv = hubKV()
  const blob = hubBlob()

  // Use like regular Cloudflare bindings
  const users = await db.prepare('SELECT * FROM users').all()
})

Environment Variables

# .env (development)
API_SECRET=dev-secret
DATABASE_URL=http://localhost:8787

# Production (Cloudflare)
wrangler secret put API_SECRET
wrangler secret put DATABASE_URL

# Production (Vercel/Netlify)
# Set in dashboard or CLI

Migration from Nuxt 3

Step 1: Update package.json

{
  "devDependencies": {
    "nuxt": "^4.0.0"
  }
}

Step 2: Enable Compatibility Mode

// nuxt.config.ts
export default defineNuxtConfig({
  future: {
    compatibilityVersion: 4
  }
})

Step 3: Move Files to app/

# Create app directory
mkdir app

# Move files
mv components app/
mv composables app/
mv pages app/
mv layouts app/
mv middleware app/
mv plugins app/
mv assets app/
mv app.vue app/
mv error.vue app/

Step 4: Fix Shallow Reactivity

// If mutating data.value properties:
const { data } = await useFetch('/api/user', {
  deep: true  // Enable deep reactivity
})

// Or replace entire value
data.value = { ...data.value, name: 'New Name' }

Step 5: Update Default Values

// v3: data.value is null
// v4: data.value is undefined

// Update null checks
if (data.value === null) // v3
if (!data.value)         // v4 (works for both)

Common Anti-Patterns

Client-Only Code on Server

// WRONG
const width = window.innerWidth

// CORRECT
if (import.meta.client) {
  const width = window.innerWidth
}

// Or use onMounted
onMounted(() => {
  const width = window.innerWidth
})

Non-Deterministic SSR

// WRONG - Different on server vs client
const id = Math.random()
const time = Date.now()

// CORRECT - Use useState for consistency
const id = useState('id', () => Math.random())
const time = useState('time', () => Date.now())

Missing Suspense for Async Components

<!-- WRONG -->
<AsyncComponent />

<!-- CORRECT -->
<Suspense>
  <AsyncComponent />
  <template #fallback>
    <LoadingSpinner />
  </template>
</Suspense>

Troubleshooting

Hydration Mismatch:

  • Check for window, document, localStorage usage
  • Wrap in ClientOnly or use onMounted
  • Look for Math.random(), Date.now(), crypto.randomUUID()

Build Errors:

rm -rf .nuxt .output node_modules/.vite && bun install

Deployment Fails:

  • Check nitro.preset matches target
  • Verify environment variables are set
  • Check wrangler.toml bindings match code

Tests Failing:

  • Ensure @nuxt/test-utils is installed
  • Check vitest.config.ts has environment: 'nuxt'
  • Use mountSuspended for async components

Related Skills

  • nuxt-core: Project setup, routing, configuration
  • nuxt-data: Composables, data fetching, state
  • nuxt-server: Server routes, API patterns
  • cloudflare-d1: D1 database patterns

Version: 4.0.0 | Last Updated: 2025-12-28 | License: MIT

Files

7
62.1 KB

Agent reviews

0

No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.

More from secondsky/claude-skills8

[TODO: lowercase-hyphen-case-name]

[TODO: Write comprehensive description in third-person. Start with "This skill provides..." or "This skill should be used when..."] [TODO: Add "Use when" scenarios - specific situations where Claude should use this skill] [TODO: Add keywords - technologies, use cases, error messages that should tr

Scan passed 0
aceternity-ui

100+ animated React components (Aceternity UI) for Next.js with Tailwind. Use for hero sections, parallax, 3D effects, or encountering animation, shadcn CLI integration errors.

Scan passed 0
api-authentication

Secure API authentication with JWT, OAuth 2.0, API keys. Use for authentication systems, third-party integrations, service-to-service communication, or encountering token management, security headers, auth flow errors.

Scan passed 0
api-changelog-versioning

Creates comprehensive API changelogs documenting breaking changes, deprecations, and migration strategies for API consumers. Use when managing API versions, communicating breaking changes, or creating upgrade guides.

Scan passed 0
api-contract-testing

Verifies API contracts between services using consumer-driven contracts, schema validation, and tools like Pact. Use when testing microservices communication, preventing breaking changes, or validating OpenAPI specifications.

Needs review 0
api-design-principles

Master REST and GraphQL API design principles to build intuitive, scalable, and maintainable APIs that delight developers. Use when designing new APIs, reviewing API specifications, or establishing API design standards.

Scan passed 0
api-error-handling

Implements standardized API error responses with proper status codes, logging, and user-friendly messages. Use when building production APIs, implementing error recovery patterns, or integrating error monitoring services.

Scan passed 0
api-filtering-sorting

Builds flexible API filtering and sorting systems with query parameter parsing, validation, and security. Use when implementing search endpoints, building data grids, or creating dynamic query APIs.

Scan passed 0

Related devops skillsscan passed