Skip to content

Conversation

@amondnet
Copy link

@amondnet amondnet commented Oct 16, 2025

🔗 Linked issue

nuxt/ui#5247

📚 Description

Adds a Claude Code skill plugin that provides Nuxt 4 development guidance and best practices.

Contents:

  • Skill covering composables, data fetching, server routes, and SSR patterns
  • Reference docs for advanced topics (hydration, performance, plugins, etc.)
  • Updated for Nuxt 4 features: new app/ directory structure, singleton data fetching, shallow reactivity

This helps developers get contextual assistance while working on Nuxt 4 projects in Claude Code.

Files:

  • plugins/nuxt/plugin.json - Plugin metadata
  • plugins/nuxt/skills/nuxt-best-practices/ - Skill and reference documentation
  • .claude-plugin/marketplace.json - Marketplace config

Installation:

The plugin is automatically available when working in the Nuxt repository with Claude Code. The skill activates when creating components, working with data fetching, implementing server routes, or debugging hydration issues.

For standalone use, copy plugins/nuxt/ to your project's .claude/plugins/ folder.

Reference:

Add comprehensive Nuxt 4 skill plugin with best practices and patterns:

- New Nuxt 4 directory structure (app/ srcDir)
- Singleton data fetching layer with shallow reactivity
- Updated hydration patterns with NuxtTime component
- Modern composables, server routes, and performance optimization
- Reference files: composables, data-fetching, hydration, performance, plugins, server
- Follows Claude Code skill best practices with gerund naming and detailed descriptions

The skill provides guidance on useState, useFetch, useAsyncData, SSR optimization,
and Nuxt 4-specific features for building modern web applications.
@amondnet amondnet requested a review from danielroe as a code owner October 16, 2025 20:28
@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@coderabbitai
Copy link

coderabbitai bot commented Oct 16, 2025

Walkthrough

This pull request adds a Nuxt-focused plugin marketplace configuration and a Nuxt plugin manifest, extensive Nuxt best-practices documentation (including a SKILL.md and reference files for composables, data fetching, hydration, performance, plugins, and server patterns), and new server-side utility exports for JWT authentication, session management, and a Prisma singleton. The marketplace file declares two plugins (local "nuxt" and remote "nuxt-ui"). All changes are additive; no existing files are modified.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "feat: add Claude Code skill for Nuxt 4 best practices" is directly and fully aligned with the changeset. The PR adds a Claude Code skill plugin providing Nuxt 4 development guidance, which is precisely what the title describes. The title is concise, specific, and uses clear conventional commit formatting without vague terms or unnecessary noise. A teammate reviewing the repository history would immediately understand that this PR introduces a new Claude Code skill for Nuxt 4 best practices.
Description Check ✅ Passed The pull request description is clearly related to the changeset and provides substantive information about the changes being introduced. It explains that the PR adds a Claude Code skill plugin for Nuxt 4, specifies the skill covers composables, data fetching, server routes, and SSR patterns, lists the files being added, and describes how the plugin integrates with Claude Code. The description also includes helpful context about installation and references to relevant documentation, demonstrating that it is neither vague nor off-topic.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@danielroe danielroe requested review from HugoRCD and Copilot and removed request for Copilot October 16, 2025 20:32
@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 16, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@33498

nuxt

npm i https://pkg.pr.new/nuxt@33498

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@33498

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@33498

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@33498

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@33498

commit: 5f2782c

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (2)
plugins/nuxt/skills/nuxt-best-practices/SKILL.md (2)

321-321: ESLint parsing error in code example (line 321).

Similar to the line 69 issue, this incomplete Vue template snippet is being parsed as code. The <script setup> block is incomplete (missing closing tag in context), which triggers the parser error. See line 69 comment for recommended fix.


370-370: ESLint parsing error in code example (line 370).

Similar to previous ESLint issues. This is part of an incomplete code example within documentation. See line 69 comment for the root cause and recommended fix.

🧹 Nitpick comments (1)
plugins/nuxt/skills/nuxt-best-practices/SKILL.md (1)

1-510: Content quality: comprehensive Nuxt 4 best practices guide.

The documentation provides thorough, well-structured coverage of Nuxt 4 fundamentals including the new app/ directory structure, composables, data fetching patterns (with Nuxt 4–specific changes like shallow reactivity and singleton patterns), server routes, SSR considerations, and advanced topics. Code examples follow established best practices and include both correct patterns (✅) and anti-patterns (❌) for comparison.

The section on data fetching (lines 125–194) appropriately highlights Nuxt 4 improvements such as shallow reactivity defaults and reactive key support. The composables section correctly emphasises useState for shared state and includes proper patterns with readonly() wrappers to prevent accidental mutations.

References to detailed guides (lines 498–504) provide clear pathways for developers needing deeper knowledge.

Consider adding explicit notes or callouts for potential gotchas with the shallow reactivity default (line 129) and when developers should use deep: true (line 155) to aid quick comprehension. Additionally, cross-reference the hydration and performance guides earlier in the document, as these are critical for SSR success in Nuxt 4.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bcc4ca and fecc5b7.

📒 Files selected for processing (9)
  • .claude-plugin/marketplace.json (1 hunks)
  • plugins/nuxt/plugin.json (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/SKILL.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/composables.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/data-fetching.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/hydration.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/performance.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/server.md (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: autofix.ci
plugins/nuxt/skills/nuxt-best-practices/reference/composables.md

[error] 20-20: ESLint: Parsing error: Declaration or statement expected


[error] 35-35: ESLint: Parsing error: Declaration or statement expected


[error] 103-103: ESLint: Parsing error: Declaration or statement expected

plugins/nuxt/skills/nuxt-best-practices/SKILL.md

[error] 69-69: ESLint: Parsing error: Declaration or statement expected


[error] 321-321: ESLint: Parsing error: ';' expected


[error] 370-370: ESLint: Parsing error: '>' expected

🪛 GitHub Check: code
plugins/nuxt/skills/nuxt-best-practices/reference/server.md

[failure] 201-201:
Async arrow function has no 'await' expression


[failure] 183-183:
Async arrow function has no 'await' expression


[failure] 47-47:
Expected an assignment or function call and instead saw an expression


[failure] 46-46:
Expected an assignment or function call and instead saw an expression


[failure] 45-45:
Expected an assignment or function call and instead saw an expression


[failure] 42-42:
Expected an assignment or function call and instead saw an expression


[failure] 41-41:
Expected an assignment or function call and instead saw an expression


[failure] 40-40:
Expected an assignment or function call and instead saw an expression


[failure] 39-39:
Expected an assignment or function call and instead saw an expression

🪛 LanguageTool
plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md

[grammar] ~77-~77: Consider using “to” with “prefer”.
Context: ... provide: { getData } } }) ### Prefer Composables Over Plugins typescript // ❌ Unnecessary plugin ...

(PREFER_OVER_TO)

plugins/nuxt/skills/nuxt-best-practices/reference/data-fetching.md

[duplication] ~67-~67: Possible typo: you repeated a word.
Context: ...ohn@example.com' } }) ``` ### Reactive Parameters Parameters automatically trigger refetch when chan...

(ENGLISH_WORD_REPEAT_RULE)

plugins/nuxt/skills/nuxt-best-practices/SKILL.md

[uncategorized] ~9-~9: Possible missing comma found.
Context: ...cations Comprehensive guide for Nuxt 4 development following official best practices and m...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~45-~45: Loose punctuation mark.
Context: ...son # TypeScript configuration ``` ::note The ~ alias now points to the `a...

(UNLIKELY_OPENING_PUNCTUATION)

🪛 markdownlint-cli2 (0.18.1)
plugins/nuxt/skills/nuxt-best-practices/reference/hydration.md

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


335-335: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


386-386: Files should end with a single newline character

(MD047, single-trailing-newline)

plugins/nuxt/skills/nuxt-best-practices/reference/composables.md

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


335-335: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


386-386: Files should end with a single newline character

(MD047, single-trailing-newline)

plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


38-38: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


514-514: Files should end with a single newline character

(MD047, single-trailing-newline)

plugins/nuxt/skills/nuxt-best-practices/reference/server.md

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


17-17: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


36-36: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


52-52: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


62-62: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


88-88: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


114-114: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


148-148: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


177-177: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


195-195: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


218-218: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


228-228: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


236-236: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


250-250: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


261-261: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


278-278: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


292-292: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


310-310: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


326-326: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


361-361: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


379-379: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


396-396: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


418-418: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


436-436: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


459-459: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


506-506: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


567-567: Files should end with a single newline character

(MD047, single-trailing-newline)

plugins/nuxt/skills/nuxt-best-practices/reference/data-fetching.md

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


335-335: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


386-386: Files should end with a single newline character

(MD047, single-trailing-newline)

plugins/nuxt/skills/nuxt-best-practices/reference/performance.md

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


38-38: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


514-514: Files should end with a single newline character

(MD047, single-trailing-newline)

plugins/nuxt/skills/nuxt-best-practices/SKILL.md

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


335-335: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


386-386: Files should end with a single newline character

(MD047, single-trailing-newline)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: test-fixtures (windows-latest, built, webpack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-benchmark
  • GitHub Check: typecheck (windows-latest, bundler)
🔇 Additional comments (5)
.claude-plugin/marketplace.json (1)

1-26: LGTM – valid marketplace configuration.

The marketplace configuration is well-structured and correctly references the local Nuxt plugin and remote Nuxt UI plugin.

plugins/nuxt/plugin.json (1)

1-22: LGTM – complete plugin manifest.

The plugin metadata is properly structured with all essential fields for marketplace integration.

plugins/nuxt/skills/nuxt-best-practices/reference/hydration.md (1)

1-386: Fix markdown linting issues: two violations require correction (MD022 claim is incorrect).

The file has two genuine markdown linting violations, not three:

  • Line 335 (MD040): Fenced code block missing language identifier. Change ``` to ```bash
  • Line 385 (MD047): File must end with a newline character

Add language to the code fence at line 335:

Nuxt shows warnings in development:
-```
+```bash
[Vue warn]: Hydration node mismatch

Append a newline character at the end of line 385 after "instead of localStorage for SSR-compatible storage".

Note: The file already has a blank line before the "## Table of Contents" heading (line 4), so there is no MD022 violation.

Likely an incorrect or invalid review comment.

plugins/nuxt/skills/nuxt-best-practices/reference/server.md (1)

1-567: Incorrect review comment. Rewrite with actual issues found.

The review comment misidentifies the primary problems. Analysis confirms:

Issues NOT found (contrary to review):

  • MD022 violations: No headings lack proper blank line spacing. Spot-checked lines 5, 17, 36, 52, 62, 88, 114, 148, 228, 236, 250 all have correct spacing.

Issues CORRECTLY identified but understated:

  • MD040 (code fence language): 26 code blocks lack language identifiers (lines 18, 34, 48, 60, 84, 112, 146, 175, 193, 214, 226, 234, 248, 257, 276, 290, 308, 322, 359, 377, and others)—not limited to line 18.
  • MD047 (trailing newline): File ends without newline character.

Root cause: Multiple bare ``` blocks throughout the file, not a single directory structure block issue.

Recommended fix:
Add language identifiers to all 26 code blocks (e.g., ```typescript, ```bash, ```shell) and add trailing newline to end of file. The markdown structure itself is sound; headings have appropriate spacing.

Likely an incorrect or invalid review comment.

plugins/nuxt/skills/nuxt-best-practices/reference/composables.md (1)

1-472: <!-- [scratchpad]
[task verify_composables_md_issues_FINAL]
Analysis complete - verified all claims in review comment.

[observations]

  • No ESLint configuration found (.eslintrc not present anywhere)
  • Code blocks properly formatted with ```typescript language identifiers (verified lines 19-40)
  • Lines 1-10: proper blank line before H2 heading (no MD022 violation)
  • Lines 330-340: code is INSIDE a properly formatted code block, not a bare fence
  • Last 5 lines: file ends with closing fence, no trailing content visible
  • Workflow runs pnpm lint but no ESLint config exists
  • Markdown linter config exists but targeted violations do not appear

[analysis]

  • Review claims "ESLint Parsing Errors (GitHub Check failures)" - FALSE: NO ESLint config in repository
  • Review claims lines 20, 35, 103, 183, 201 have parsing errors - FALSE: these lines are within properly fenced code blocks
  • Review claims MD022, MD040, MD047 violations - UNSUBSTANTIATED: files appear correctly formatted
  • Review references "GitHub Check failures" without evidence
  • All major claims are contradicted by actual codebase state

[conclusion]
This is an incorrect_review_comment. The reviewer either:

  1. Misread the file or copied from wrong context
  2. Referenced non-existent ESLint configuration
  3. Made false claims about GitHub check failures
  4. Misidentified markdown violations

The composables.md file contains no critical issues requiring the urgent fixes described.

The review comment contains incorrect and unsubstantiated claims and should be disregarded.

Key findings:

  1. ESLint errors claimed but no configuration exists: The review references "ESLint Parsing Errors (GitHub Check failures)" at specific lines, but repository contains no ESLint configuration (no .eslintrc file, no eslintConfig in package.json). These claimed errors cannot exist without configuration.

  2. Code blocks are properly formatted: Inspected lines 19–40 confirm all TypeScript examples are within properly fenced code blocks with language identifiers (```typescript), contradicting claims of parsing failures.

  3. Markdown violations unsubstantiated:

    • Lines 1–10 show proper blank lines before headings (no MD022 violation)
    • Lines 330–340 show code already within formatted blocks (no missing language identifiers)
    • File structure appears correct
  4. No evidence of GitHub Check failures: Workflow runs pnpm lint, but no failing checks are documented; claims reference non-existent ESLint configuration.

The file plugins/nuxt/skills/nuxt-best-practices/reference/composables.md (471 lines) does not exhibit the critical issues described. Code examples are properly formatted and accessible to developers.

Likely an incorrect or invalid review comment.

Comment on lines 1 to 516
# Nuxt Performance Optimization

Comprehensive guide to optimizing Nuxt application performance.

## Table of Contents
- [Built-in Optimizations](#built-in-optimizations)
- [Component Optimization](#component-optimization)
- [Image Optimization](#image-optimization)
- [Font Optimization](#font-optimization)
- [Code Splitting](#code-splitting)
- [Caching Strategies](#caching-strategies)
- [Third-Party Scripts](#third-party-scripts)
- [Profiling Tools](#profiling-tools)

## Built-in Optimizations

### Smart Prefetching with NuxtLink

```vue
<template>
<!-- ✅ Auto-prefetch when visible (default) -->
<NuxtLink to="/about">About</NuxtLink>

<!-- ✅ Prefetch on hover/focus -->
<NuxtLink to="/contact" prefetch-on="interaction">Contact</NuxtLink>

<!-- ✅ Disable prefetch -->
<NuxtLink to="/heavy-page" :prefetch="false">Heavy Page</NuxtLink>
</template>
```

### Data Fetching Optimization

```typescript
// ✅ Prevents duplicate requests
const { data } = await useFetch('/api/users')
// Server: fetches data
// Client: reuses server data (no second request)

// ✅ Parallel data fetching
const [users, posts] = await Promise.all([
useFetch('/api/users'),
useFetch('/api/posts')
])
```

## Component Optimization

### Lazy Loading Components

```vue
<template>
<!-- ✅ Lazy load with Lazy prefix -->
<LazyHeavyChart v-if="showChart" />
<LazyVideoPlayer :src="videoUrl" />

<!-- ✅ Explicit lazy import -->
<component :is="HeavyComponent" v-if="loaded" />
</template>

<script setup>
const HeavyComponent = defineAsyncComponent(() =>
import('~/components/HeavyComponent.vue')
)
</script>
```

### Lazy Hydration

```vue
<template>
<!-- ✅ Hydrate when visible -->
<LazyChart lazy-hydrate="visible" />

<!-- ✅ Hydrate when idle -->
<LazyWidget lazy-hydrate="idle" />

<!-- ✅ Hydrate on interaction -->
<LazyModal lazy-hydrate="interaction" />
</template>
```

### Client-Only Components

```vue
<template>
<!-- ✅ Skip SSR, render on client only -->
<ClientOnly>
<MapComponent />
<template #fallback>
<MapSkeleton />
</template>
</ClientOnly>
</template>
```

## Image Optimization

### Using NuxtImg

```vue
<template>
<!-- ✅ Auto-optimization -->
<NuxtImg
src="/hero.jpg"
width="800"
height="600"
format="webp"
quality="80"
loading="lazy"
/>

<!-- ✅ Critical images - eager load -->
<NuxtImg
src="/logo.png"
width="200"
height="100"
loading="eager"
preload
fetchpriority="high"
/>

<!-- ✅ Below-fold images - lazy load -->
<NuxtImg
src="/gallery-1.jpg"
width="400"
height="300"
loading="lazy"
fetchpriority="low"
/>
</template>
```

### Using NuxtPicture

```vue
<template>
<!-- ✅ Responsive images with art direction -->
<NuxtPicture
src="/hero.jpg"
:img-attrs="{
class: 'hero-image',
alt: 'Hero banner'
}"
sizes="sm:100vw md:50vw lg:400px"
/>
</template>
```

### Image Module Configuration

```typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
quality: 80,
format: ['webp', 'avif'],
screens: {
xs: 320,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
xxl: 1536
}
}
})
```

## Font Optimization

### Using Nuxt Fonts

```typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/fonts'],
fonts: {
families: [
{ name: 'Inter', provider: 'google' },
{ name: 'Roboto Mono', provider: 'google' }
]
}
})
```

Benefits:
- Auto self-hosting (no external requests)
- Fallback metrics to prevent layout shift
- Preload optimization

### Manual Font Loading

```vue
<!-- app.vue or layouts/default.vue -->
<script setup>
useHead({
link: [
{
rel: 'preload',
as: 'font',
type: 'font/woff2',
href: '/fonts/inter.woff2',
crossorigin: 'anonymous'
}
]
})
</script>
```

## Code Splitting

### Route-Based Splitting

```typescript
// Automatic per-route splitting
pages/
├── index.vue # → _nuxt/index-[hash].js
├── about.vue # → _nuxt/about-[hash].js
└── contact.vue # → _nuxt/contact-[hash].js
```

### Component-Based Splitting

```typescript
// ✅ Dynamic imports for large components
const HeavyComponent = defineAsyncComponent(() =>
import('~/components/HeavyComponent.vue')
)

// ✅ With loading state
const HeavyComponent = defineAsyncComponent({
loader: () => import('~/components/HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
delay: 200,
timeout: 3000
})
```

### Vendor Splitting

```typescript
// nuxt.config.ts
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor-charts': ['chart.js', 'd3'],
'vendor-utils': ['lodash-es', 'date-fns']
}
}
}
}
}
})
```

## Caching Strategies

### Route Rules

```typescript
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Static generation at build time
'/': { prerender: true },
'/about': { prerender: true },

// Incremental Static Regeneration
'/blog/**': { isr: 3600 }, // Revalidate every hour

// Stale-While-Revalidate
'/api/posts': { swr: 3600 },

// Client-side rendering only
'/dashboard/**': { ssr: false },

// Custom cache headers
'/static/**': {
headers: {
'cache-control': 'public, max-age=31536000, immutable'
}
}
}
})
```

### API Response Caching

```typescript
// server/api/posts.get.ts
export default defineCachedEventHandler(
async (event) => {
const posts = await fetchPosts()
return posts
},
{
maxAge: 60 * 60, // Cache for 1 hour
getKey: (event) => 'posts-list'
}
)

// With query parameters
export default defineCachedEventHandler(
async (event) => {
const query = getQuery(event)
const posts = await fetchPosts(query)
return posts
},
{
maxAge: 60 * 60,
getKey: (event) => {
const query = getQuery(event)
return `posts-${query.page}-${query.limit}`
}
}
)
```

### Client-Side Caching

```typescript
// ✅ Cache with useFetch
const { data } = await useFetch('/api/posts', {
key: 'posts',
getCachedData(key) {
const cached = useNuxtData(key)
// Cache for 5 minutes
if (cached.data.value && Date.now() - cached.fetchedAt < 5 * 60 * 1000) {
return cached.data.value
}
}
})
```

## Third-Party Scripts

### Using Nuxt Scripts

```typescript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/scripts'],
scripts: {
registry: {
googleAnalytics: {
id: 'G-XXXXXXXXXX'
}
}
}
})

// Usage in component
<script setup>
useScript('googleAnalytics')
</script>
```

### Manual Script Loading

```typescript
// ✅ Defer non-critical scripts
useHead({
script: [
{
src: 'https://example.com/widget.js',
defer: true
}
]
})

// ✅ Load on interaction
const { load } = useScript('https://example.com/widget.js', {
trigger: 'manual'
})

const loadWidget = async () => {
await load()
initializeWidget()
}
```

## Bundle Size Optimization

### Analyze Bundle

```bash
# Build and analyze
npx nuxi analyze

# Opens visualization in browser
# Shows:
# - Bundle sizes per route
# - Shared chunks
# - Module composition
```

### Tree Shaking

```typescript
// ✅ Import only what you need
import { debounce } from 'lodash-es'

// ❌ Imports entire library
import _ from 'lodash'
```

### Remove Unused Code

```typescript
// nuxt.config.ts
export default defineNuxtConfig({
// Remove console logs in production
vite: {
esbuild: {
drop: process.env.NODE_ENV === 'production' ? ['console', 'debugger'] : []
}
}
})
```

## Profiling Tools

### Nuxt DevTools

```bash
# Enable DevTools
npx nuxi dev --devtools
```

Features:
- Component tree visualization
- Route rendering metrics
- Asset size analysis
- Network requests
- Payload inspection

### Chrome DevTools

```javascript
// Measure rendering performance
performance.mark('start')
// ... rendering code
performance.mark('end')
performance.measure('render-time', 'start', 'end')
```

### Lighthouse

```bash
# Run Lighthouse
npm install -g lighthouse
lighthouse http://localhost:3000 --view
```

Focus metrics:
- **LCP** (Largest Contentful Paint) - < 2.5s
- **FID** (First Input Delay) - < 100ms
- **CLS** (Cumulative Layout Shift) - < 0.1
- **TTI** (Time to Interactive) - < 3.8s

## Performance Checklist

### Images
- [ ] Use NuxtImg/NuxtPicture for all images
- [ ] Set width/height to prevent layout shift
- [ ] Use `loading="lazy"` for below-fold images
- [ ] Use `loading="eager"` and `preload` for critical images
- [ ] Convert to WebP/Avif format

### Fonts
- [ ] Use @nuxt/fonts module for auto-optimization
- [ ] Limit font variants (weights, styles)
- [ ] Preload critical fonts
- [ ] Use system fonts as fallbacks

### Code Splitting
- [ ] Lazy load heavy components with `Lazy` prefix
- [ ] Use lazy hydration for non-critical components
- [ ] Dynamic import large libraries
- [ ] Split vendor bundles

### Caching
- [ ] Set appropriate route rules (prerender, ISR, SWR)
- [ ] Cache API responses with `defineCachedEventHandler`
- [ ] Configure CDN cache headers
- [ ] Use client-side caching for repeated requests

### Third-Party Scripts
- [ ] Defer non-critical scripts
- [ ] Use @nuxt/scripts module
- [ ] Load scripts on interaction when possible
- [ ] Audit script necessity

### Monitoring
- [ ] Run Lighthouse regularly
- [ ] Monitor Core Web Vitals
- [ ] Use Nuxt DevTools for profiling
- [ ] Track bundle size in CI/CD

## Best Practices Summary

1. **Use NuxtLink with smart prefetching** for navigation
2. **Lazy load components** that aren't immediately visible
3. **Optimize images** with NuxtImg and proper loading strategies
4. **Self-host fonts** with @nuxt/fonts module
5. **Implement route rules** for appropriate caching strategies
6. **Defer third-party scripts** to avoid blocking rendering
7. **Split code** at route and component boundaries
8. **Cache API responses** on server and client
9. **Monitor performance** with DevTools and Lighthouse
10. **Analyze bundles** regularly to identify optimization opportunities No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Fix markdown linting violations – apply consistent corrections across all reference files.

Standard markdown linting issues present:

  • Line 5 (MD022): Blank line before "## Table of Contents"
  • Line 38 (MD040): Language identifier for code fence (```bash)
  • Line 514 (MD047): Trailing newline at EOF

All reference documentation files need these same corrections applied consistently.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


38-38: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


514-514: Files should end with a single newline character

(MD047, single-trailing-newline)

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/reference/performance.md (lines
1-516): add a blank line before the "## Table of Contents" header (fix MD022),
ensure all fenced code blocks include a language identifier where missing (e.g.,
change ``` to ```bash or appropriate language at the fence near line 38 to fix
MD040), and make sure the file ends with a single trailing newline (fix MD047);
apply the same three fixes consistently across all reference documentation
files.

Comment on lines 1 to 514
# Nuxt Plugin Development Best Practices

Guide to creating efficient and maintainable Nuxt plugins.

## Table of Contents
- [Plugin Basics](#plugin-basics)
- [Performance Considerations](#performance-considerations)
- [Client vs Server Plugins](#client-vs-server-plugins)
- [Async Plugins](#async-plugins)
- [Plugin Patterns](#plugin-patterns)
- [Type Safety](#type-safety)

## Plugin Basics

### Creating a Plugin

```typescript
// plugins/my-plugin.ts
export default defineNuxtPlugin(() => {
// Plugin initialization logic

return {
provide: {
myFunction: () => 'Hello from plugin'
}
}
})

// Usage in component
const { $myFunction } = useNuxtApp()
console.log($myFunction()) // "Hello from plugin"
```

### Plugin Execution Order

By default, plugins load synchronously in alphabetical order:

```
plugins/
├── 01.first.ts # Loads first
├── 02.second.ts # Loads second
└── 03.third.ts # Loads third
```

## Performance Considerations

### Minimize Plugin Overhead

```typescript
// ❌ Bad - Heavy computation in plugin
export default defineNuxtPlugin(() => {
// Blocks hydration!
const heavyData = processLargeDataset()

return {
provide: { heavyData }
}
})

// ✅ Good - Lazy initialization
export default defineNuxtPlugin(() => {
let cachedData: Data | null = null

const getData = async () => {
if (!cachedData) {
cachedData = await processLargeDataset()
}
return cachedData
}

return {
provide: { getData }
}
})
```

### Prefer Composables Over Plugins

```typescript
// ❌ Unnecessary plugin
// plugins/utils.ts
export default defineNuxtPlugin(() => {
const formatDate = (date: Date) => {
return date.toLocaleDateString()
}

return {
provide: { formatDate }
}
})

// ✅ Better - Use composable
// composables/useFormatters.ts
export const useFormatters = () => {
const formatDate = (date: Date) => {
return date.toLocaleDateString()
}

return { formatDate }
}
```

**When to use plugins:**
- Global Vue directives
- Global mixins
- Third-party library integration
- App-level initialization

**When to use composables:**
- Utility functions
- State management
- Reusable logic
- Data fetching helpers

## Client vs Server Plugins

### Client-Only Plugin

```typescript
// plugins/analytics.client.ts
export default defineNuxtPlugin(() => {
// Only runs on client
window.gtag('config', 'GA_MEASUREMENT_ID')

return {
provide: {
trackEvent: (event: string) => {
window.gtag('event', event)
}
}
}
})
```

### Server-Only Plugin

```typescript
// plugins/database.server.ts
export default defineNuxtPlugin(() => {
// Only runs on server
const db = new DatabaseClient()

return {
provide: {
db
}
}
})
```

### Universal Plugin with Guards

```typescript
// plugins/logger.ts
export default defineNuxtPlugin(() => {
const log = (message: string) => {
if (process.client) {
console.log('[Client]', message)
} else {
console.log('[Server]', message)
}
}

return {
provide: { log }
}
})
```

## Async Plugins

### Parallel Loading

```typescript
// plugins/firebase.client.ts
export default defineNuxtPlugin({
name: 'firebase',
parallel: true, // Don't block other plugins
async setup() {
const { initializeApp } = await import('firebase/app')
const { getAuth } = await import('firebase/auth')

const app = initializeApp({
apiKey: 'xxx',
authDomain: 'xxx'
})

const auth = getAuth(app)

return {
provide: {
firebase: { app, auth }
}
}
}
})
```

### Sequential Loading

```typescript
// plugins/01.config.ts
export default defineNuxtPlugin(async () => {
// Must load before other plugins
const config = await $fetch('/api/config')

return {
provide: { config }
}
})

// plugins/02.feature.ts
export default defineNuxtPlugin(() => {
const { $config } = useNuxtApp()

// Uses config from previous plugin
initFeature($config)
})
```

### Dependency Handling

```typescript
// plugins/api.client.ts
export default defineNuxtPlugin({
name: 'api',
dependsOn: ['auth'], // Wait for auth plugin
setup() {
const { $auth } = useNuxtApp()

const api = {
fetch: async (url: string) => {
return await $fetch(url, {
headers: {
Authorization: `Bearer ${$auth.token}`
}
})
}
}

return {
provide: { api }
}
}
})
```

## Plugin Patterns

### Vue Plugin Integration

```typescript
// plugins/vue-plugin.ts
import VuePlugin from 'vue-plugin'

export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VuePlugin, {
// Plugin options
})
})
```

### Global Directive

```typescript
// plugins/directives.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('focus', {
mounted(el) {
el.focus()
}
})
})

// Usage
<template>
<input v-focus />
</template>
```

### Error Handler

```typescript
// plugins/error-handler.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
console.error('Global error:', error)
console.error('Component:', instance)
console.error('Error info:', info)
}
})
```

### Router Integration

```typescript
// plugins/router-guards.ts
export default defineNuxtPlugin(() => {
const router = useRouter()

router.beforeEach((to, from) => {
console.log('Navigating to:', to.path)

// Add analytics, auth checks, etc.
})

router.afterEach((to, from) => {
console.log('Navigated from:', from.path, 'to:', to.path)
})
})
```

### API Client Pattern

```typescript
// plugins/api.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()

const api = $fetch.create({
baseURL: config.public.apiBase,
onRequest({ options }) {
// Add auth headers
const token = useCookie('auth-token')
if (token.value) {
options.headers = {
...options.headers,
Authorization: `Bearer ${token.value}`
}
}
},
onResponseError({ response }) {
// Handle errors globally
if (response.status === 401) {
navigateTo('/login')
}
}
})

return {
provide: { api }
}
})
```

## Type Safety

### TypeScript Plugin Definition

```typescript
// plugins/typed-plugin.ts
interface MyPlugin {
greet: (name: string) => string
farewell: (name: string) => string
}

export default defineNuxtPlugin(() => {
const plugin: MyPlugin = {
greet: (name) => `Hello, ${name}!`,
farewell: (name) => `Goodbye, ${name}!`
}

return {
provide: {
myPlugin: plugin
}
}
})
```

### Type Augmentation

```typescript
// types/plugins.d.ts
declare module '#app' {
interface NuxtApp {
$myPlugin: {
greet: (name: string) => string
farewell: (name: string) => string
}
}
}

// Usage with full type safety
const { $myPlugin } = useNuxtApp()
$myPlugin.greet('World') // Type-safe!
```

### Generic Plugin

```typescript
// plugins/storage.ts
export default defineNuxtPlugin(() => {
const storage = {
get: <T>(key: string): T | null => {
const value = localStorage.getItem(key)
return value ? JSON.parse(value) : null
},
set: <T>(key: string, value: T): void => {
localStorage.setItem(key, JSON.stringify(value))
}
}

return {
provide: { storage }
}
})

// Usage
const user = $storage.get<User>('user') // Type: User | null
```

## Hooks Integration

### Using Nuxt Hooks

```typescript
// plugins/lifecycle.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('app:created', () => {
console.log('App created')
})

nuxtApp.hook('app:mounted', () => {
console.log('App mounted')
})

nuxtApp.hook('page:start', () => {
console.log('Page navigation started')
})

nuxtApp.hook('page:finish', () => {
console.log('Page navigation finished')
})
})
```

## Best Practices Summary

1. **Minimize plugin overhead** - plugins run during hydration
2. **Use composables when possible** - lighter than plugins
3. **Enable `parallel: true`** for async plugins
4. **Use `.client` suffix** for browser-only code
5. **Use `.server` suffix** for server-only code
6. **Lazy initialize** expensive operations
7. **Provide type definitions** for better DX
8. **Use plugin dependencies** for ordered loading
9. **Handle errors gracefully** in plugins
10. **Document plugin purpose** and usage

## Common Patterns

### Singleton Service

```typescript
// plugins/singleton.ts
let instance: Service | null = null

export default defineNuxtPlugin(() => {
if (!instance) {
instance = new Service()
}

return {
provide: { service: instance }
}
})
```

### Feature Flag Plugin

```typescript
// plugins/features.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()

const features = {
isEnabled: (feature: string) => {
return config.public.features?.[feature] ?? false
}
}

return {
provide: { features }
}
})

// Usage
<template>
<div v-if="$features.isEnabled('newDashboard')">
<NewDashboard />
</div>
</template>
```

### Third-Party SDK Integration

```typescript
// plugins/stripe.client.ts
import { loadStripe } from '@stripe/stripe-js'

export default defineNuxtPlugin({
name: 'stripe',
parallel: true,
async setup() {
const config = useRuntimeConfig()
const stripe = await loadStripe(config.public.stripeKey)

return {
provide: { stripe }
}
}
})
``` No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Fix markdown linting violations consistent with other reference files.

Same three markdown linting issues present:

  • Line 5 (MD022): Blank line before "## Table of Contents"
  • Line 38 (MD040): Add language to code fence (```bash)
  • Line 514 (MD047): Add trailing newline

Apply consistent fixes across all reference documentation.

🧰 Tools
🪛 LanguageTool

[grammar] ~77-~77: Consider using “to” with “prefer”.
Context: ... provide: { getData } } }) ### Prefer Composables Over Plugins typescript // ❌ Unnecessary plugin ...

(PREFER_OVER_TO)

🪛 markdownlint-cli2 (0.18.1)

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


38-38: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


514-514: Files should end with a single newline character

(MD047, single-trailing-newline)

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md lines 1-514:
remove the extra blank line before the "## Table of Contents" heading (fix
MD022), add a language identifier to the top-level code fence shown around line
~38 (change ``` to ```bash to satisfy MD040), and ensure the file ends with a
single trailing newline character (add final newline to satisfy MD047); apply
these same style fixes consistently across the file.

Comment on lines +1 to +7
---
name: Building Nuxt 4 Applications
description: Builds modern Nuxt 4 applications with composables, server routes, and SSR optimization. Provides best practices for useState, useFetch, useAsyncData, data fetching strategies, hydration patterns, server API design, and the new app/ directory structure. Use when creating Vue components, writing composables, implementing server routes, configuring Nuxt projects, debugging hydration issues, or when user mentions Nuxt, Nuxt 4, SSR, Nitro, nuxt.config, useState, useFetch, useAsyncData, server/api, app directory, or hydration.
allowed-tools: Read, Write, Edit, Grep, Glob
---

# Building Nuxt 4 Applications
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add blank line between frontmatter and heading.

The heading at line 7 should be separated from the closing --- by a blank line per markdown best practices (MD022).

---
+
# Building Nuxt 4 Applications
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
---
name: Building Nuxt 4 Applications
description: Builds modern Nuxt 4 applications with composables, server routes, and SSR optimization. Provides best practices for useState, useFetch, useAsyncData, data fetching strategies, hydration patterns, server API design, and the new app/ directory structure. Use when creating Vue components, writing composables, implementing server routes, configuring Nuxt projects, debugging hydration issues, or when user mentions Nuxt, Nuxt 4, SSR, Nitro, nuxt.config, useState, useFetch, useAsyncData, server/api, app directory, or hydration.
allowed-tools: Read, Write, Edit, Grep, Glob
---
# Building Nuxt 4 Applications
---
name: Building Nuxt 4 Applications
description: Builds modern Nuxt 4 applications with composables, server routes, and SSR optimization. Provides best practices for useState, useFetch, useAsyncData, data fetching strategies, hydration patterns, server API design, and the new app/ directory structure. Use when creating Vue components, writing composables, implementing server routes, configuring Nuxt projects, debugging hydration issues, or when user mentions Nuxt, Nuxt 4, SSR, Nitro, nuxt.config, useState, useFetch, useAsyncData, server/api, app directory, or hydration.
allowed-tools: Read, Write, Edit, Grep, Glob
---
# Building Nuxt 4 Applications
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

5-5: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/SKILL.md around lines 1 to 7, the
YAML frontmatter closing '---' is immediately followed by the heading; add a
single blank line between the closing '---' and the '# Building Nuxt 4
Applications' heading so the frontmatter is separated from the content and
satisfies MD022.

### Naming Convention
```typescript
// ✅ Good - use prefix
export const useAuth = () => { ... }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

ESLint parsing error in code example.

ESLint is attempting to parse the incomplete code example at this line as TypeScript. Since this is a markdown documentation file with intentionally incomplete examples (using { ... } as a placeholder), the ESLint configuration should exclude this file or markdown files should not be linted for code syntax. Alternatively, ensure all code examples in markdown are complete snippets or clearly marked as pseudo-code.


🏁 Script executed:

#!/bin/bash
# Check if markdown files are being linted by ESLint
find . -name ".eslintignore" -o -name ".eslintrc*" | head -5
cat .eslintignore 2>/dev/null | grep -i "md\|markdown\|plugins"

Length of output: 0



🏁 Script executed:

#!/bin/bash
# More comprehensive search for ESLint configuration
echo "=== Searching for ESLint config files ==="
find . -type f \( -name ".eslintignore" -o -name ".eslintrc*" -o -name "eslint.config.*" \) 2>/dev/null | head -20

echo "=== Checking package.json for ESLint config ==="
if [ -f package.json ]; then
  jq '.eslintConfig // "No ESLint config in package.json"' package.json 2>/dev/null
fi

echo "=== Checking if target markdown file exists ==="
if [ -f "plugins/nuxt/skills/nuxt-best-practices/SKILL.md" ]; then
  echo "File exists. First 80 lines:"
  head -80 "plugins/nuxt/skills/nuxt-best-practices/SKILL.md"
else
  echo "File not found at specified path"
fi

echo "=== Searching for any .md files in the plugins directory ==="
find plugins -name "*.md" -type f 2>/dev/null | head -10

Length of output: 3987


🏁 Script executed:

#!/bin/bash
# More comprehensive search for ESLint configuration
echo "=== Searching for ESLint config files ==="
find . -type f \( -name ".eslintignore" -o -name ".eslintrc*" -o -name "eslint.config.*" \) 2>/dev/null | head -20

echo "=== Checking package.json for ESLint config ==="
if [ -f package.json ]; then
  jq '.eslintConfig // "No ESLint config in package.json"' package.json 2>/dev/null
fi

echo "=== Checking if target markdown file exists ==="
if [ -f "plugins/nuxt/skills/nuxt-best-practices/SKILL.md" ]; then
  echo "File exists. Content around line 69:"
  sed -n '60,80p' "plugins/nuxt/skills/nuxt-best-practices/SKILL.md"
else
  echo "File not found at specified path"
  find plugins -name "SKILL.md" -type f 2>/dev/null
fi

echo "=== Checking npm scripts for ESLint ==="
if [ -f package.json ]; then
  jq '.scripts | keys[]' package.json 2>/dev/null | grep -i lint
fi

Length of output: 819



🏁 Script executed:

#!/bin/bash
# Examine ESLint configuration
echo "=== ESLint Configuration ==="
cat ./eslint.config.mjs

echo -e "\n=== Searching for markdown linting config ==="
grep -i "markdown\|\.md\|astro\|mdx" ./eslint.config.mjs || echo "No explicit markdown patterns found"

echo -e "\n=== Checking if eslint-plugin-markdown is used ==="
if [ -f package.json ]; then
  jq '.devDependencies, .dependencies | to_entries[] | select(.key | contains("markdown")) | .key' package.json 2>/dev/null
fi

Length of output: 9239


🏁 Script executed:

#!/bin/bash
# Examine ESLint configuration
echo "=== ESLint Configuration ==="
cat ./eslint.config.mjs

echo -e "\n=== Checking if eslint-plugin-markdown or similar is used ==="
if [ -f package.json ]; then
  jq '.devDependencies, .dependencies | to_entries[] | select(.key | test("markdown|mdown|mdx")) | .key' package.json 2>/dev/null
fi

Length of output: 8929


ESLint markdown processing confirmed; code blocks are parsed and incomplete syntax will cause parser errors.

The repository's ESLint configuration (eslint.config.mjs) uses @eslint/markdown and explicitly processes all markdown files (**/*.md pattern) with code blocks extracted and parsed as virtual files (**/*.md/**/*). Whilst certain linting rules are disabled for markdown code blocks, parser-level errors occur before those rules are applied.

The incomplete example on line 69—export const useAuth = () => { ... }—contains invalid TypeScript syntax that the parser cannot resolve, causing ESLint processing to fail. The configuration should either:

  • Exclude markdown files from linting entirely, or
  • Exclude the plugins/nuxt/skills/nuxt-best-practices/SKILL.md file specifically, or
  • Replace incomplete placeholders with valid, compilable code snippets (even if stubbed).
🧰 Tools
🪛 GitHub Actions: autofix.ci

[error] 69-69: ESLint: Parsing error: Declaration or statement expected

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/SKILL.md around line 69, the inline
snippet "export const useAuth = () => { ... }" is invalid TypeScript and breaks
ESLint's markdown code extraction; fix by either replacing the placeholder with
a minimal valid stub (for example a compilable arrow function or named export
that returns a typed value) so the code block parses, or update
eslint.config.mjs to exclude markdown files (or this specific file) from
extraction by removing or narrowing the "**/*.md" pattern (or adding the file to
an exclude list) — pick one approach and apply the change consistently, then run
the linter to confirm the parser error is resolved.

Comment on lines +335 to +375
## SEO & Meta Tags

```typescript
// composables/useSEO.ts
export const useSEO = (options: {
title: string
description: string
image?: string
}) => {
useHead({
title: options.title,
meta: [
{
name: 'description',
content: options.description
},
{
property: 'og:title',
content: options.title
},
{
property: 'og:description',
content: options.description
},
{
property: 'og:image',
content: options.image || '/og-default.png'
}
]
})
}

// Usage in page
<script setup>
useSEO({
title: 'Dashboard',
description: 'User dashboard page',
image: '/dashboard-og.png'
})
</script>
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix improperly fenced "Usage in page" code block.

The "Usage in page" example at lines 367–375 is not enclosed in a markdown code fence. This will render the Vue code as plain text and may cause ESLint parsing errors when processing the file. The code block needs proper fencing with a language specifier.

 }
 
-// Usage in page
-<script setup>
-useSEO({
-  title: 'Dashboard',
-  description: 'User dashboard page',
-  image: '/dashboard-og.png'
-})
-</script>
+// Usage in page
+```vue
+<script setup>
+useSEO({
+  title: 'Dashboard',
+  description: 'User dashboard page',
+  image: '/dashboard-og.png'
+})
+</script>
+```
🧰 Tools
🪛 GitHub Actions: autofix.ci

[error] 370-370: ESLint: Parsing error: '>' expected

🪛 markdownlint-cli2 (0.18.1)

335-335: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/SKILL.md around lines 335 to 375 the
"Usage in page" Vue example is missing a closing code fence so the <script
setup> block is rendered as plain text; fix it by enclosing the example in a
proper fenced code block with a language specifier (e.g., ```vue) before the
<script setup> line and a closing triple-backtick after </script>, ensuring
there are no stray characters or extra plus signs.

<LazyHeavyChart v-if="showChart" />

<!-- Client-only rendering -->
<ClientOnly>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Add trailing newline at end of file.

The file should end with a single newline character per markdown conventions (MD047).

To verify the file ends correctly, run:


🏁 Script executed:

#!/bin/bash
# Check for trailing newline
if [ -n "$(tail -c 1 plugins/nuxt/skills/nuxt-best-practices/SKILL.md)" ]; then
  echo "Missing trailing newline detected"
else
  echo "File ends with newline"
fi

Length of output: 33


Add trailing newline at end of file.

The file is confirmed to be missing a trailing newline character at the end. Per markdown conventions (MD047), the file should end with a single newline.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

386-386: Files should end with a single newline character

(MD047, single-trailing-newline)

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/SKILL.md around line 386, the file is
missing a trailing newline at EOF; add a single newline character at the end of
the file so it ends with an empty line (ensure exactly one trailing newline) and
save the file to satisfy MD047.

@codspeed-hq
Copy link

codspeed-hq bot commented Oct 16, 2025

CodSpeed Performance Report

Merging #33498 will not alter performance

Comparing amondnet:main (5f2782c) with main (b399de2)1

Summary

✅ 10 untouched

Footnotes

  1. No successful run was found on main (39dd9b6) during the generation of this report, so b399de2 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Fix systematic markdown linting issues across all Nuxt best practices
reference documentation files:

- Add blank lines after h3 subsection headings (MD022)
- Add blank line after "## Table of Contents" headings
- Add language identifiers to code blocks (MD040 - bash)
- Add trailing newlines to all files (MD047)

Resolves 90 markdown linting errors reported by CodeRabbit in PR review.

Files modified:
- composables.md
- data-fetching.md
- hydration.md
- performance.md
- plugins.md
- server.md
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md (1)

78-78: Grammar: Consider revising heading for clarity.

The heading "Prefer Composables Over Plugins" could be more naturally expressed as "Prefer to Use Composables Over Plugins" or "Use Composables When Possible Over Plugins". The current phrasing is acceptable but slightly awkward.

Consider rewording for better readability:

-### Prefer Composables Over Plugins
+### Prefer Composables Over Plugins for Utility Functions

Or alternatively:

-### Prefer Composables Over Plugins
+### When to Use Composables vs Plugins
plugins/nuxt/skills/nuxt-best-practices/reference/server.md (1)

486-501: Verify JWT implementation completeness.

The JWT pattern shown is correct, but the verifyPassword function used on line 511 is referenced without being defined or imported in the shown code. Whilst this is acceptable in a reference document (as it's a conceptual example), consider adding a note that this function should be implemented or imported from a utility library.

Consider adding a clarifying comment:

 // server/api/auth/login.post.ts
 export default defineEventHandler(async (event) => {
   const { email, password } = await readBody(event)
 
   const user = await prisma.user.findUnique({
     where: { email }
   })
 
-  if (!user || !(await verifyPassword(password, user.password))) {
+  // Note: verifyPassword should be implemented using bcrypt or similar
+  if (!user || !(await verifyPassword(password, user.password))) {
     throw createError({
       statusCode: 401,
       message: 'Invalid credentials'
     })
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fecc5b7 and 5f2782c.

📒 Files selected for processing (6)
  • plugins/nuxt/skills/nuxt-best-practices/reference/composables.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/data-fetching.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/hydration.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/performance.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md (1 hunks)
  • plugins/nuxt/skills/nuxt-best-practices/reference/server.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • plugins/nuxt/skills/nuxt-best-practices/reference/data-fetching.md
  • plugins/nuxt/skills/nuxt-best-practices/reference/performance.md
  • plugins/nuxt/skills/nuxt-best-practices/reference/composables.md
🧰 Additional context used
🪛 LanguageTool
plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md

[grammar] ~78-~78: Consider using “to” with “prefer”.
Context: ... provide: { getData } } }) ### Prefer Composables Over Plugins typescript // ❌ Unnecessary plugin ...

(PREFER_OVER_TO)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: test-fixtures (windows-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite-env-api, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite-env-api, default, manifest-on, json, lts/-1)
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: typecheck (ubuntu-latest, bundler)
  • GitHub Check: release-pkg-pr-new
  • GitHub Check: test-benchmark
  • GitHub Check: test-size
  • GitHub Check: code
🔇 Additional comments (1)
plugins/nuxt/skills/nuxt-best-practices/reference/hydration.md (1)

1-5: Markdown linting: Add blank line before main TOC heading.

Line 5 lacks a blank line before "## Table of Contents", violating MD022 (blanks-around-headings). This should be consistent with markdown best practices.

Apply this diff:

 # Nuxt Hydration Best Practices
 
 Guide to avoiding hydration mismatches and building SSR-compatible Nuxt applications.
 
+
 ## Table of Contents

Likely an incorrect or invalid review comment.

Comment on lines +38 to +44

### Non-Deterministic Values

```typescript
// ❌ Different on server/client
<div>{{ Math.random() }}</div>
<div>{{ new Date().getTime() }}</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fenced code block missing language identifier.

Line 44 contains a bare code fence without a language specifier, violating MD040. This makes it unclear whether the fence is meant for bash, YAML, or plain text.

Apply this diff:

-```
+```bash
 plugins/
 ├── 01.first.ts    # Loads first
 ├── 02.second.ts   # Loads second
 └── 03.third.ts    # Loads third
-```
+```
🤖 Prompt for AI Agents
plugins/nuxt/skills/nuxt-best-practices/reference/hydration.md around lines 38
to 44: the fenced code block is missing a language identifier (MD040); update
the opening fence to include a language tag (bash) and ensure the block content
matches the provided tree snippet, i.e. replace the current fence with ```bash,
include the plugin tree lines, and close with ``` so the block becomes a
bash-coded fence containing the plugins/  ├── 01.first.ts ... └── 03.third.ts
lines.

7. **Use `.client` suffix** for client-only plugins
8. **Test with SSR disabled** to verify client-only code paths
9. **Check hydration warnings** in development mode
10. **Use `useCookie`** instead of localStorage for SSR-compatible storage
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add trailing newline to file.

File should end with a single newline character (MD047), currently missing.

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/reference/hydration.md around line
387, the file is missing a trailing newline; add a single newline character at
the end of the file so it ends with exactly one newline (fixes MD047).

}
}
})
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add trailing newline to file.

File should end with a single newline character (MD047), currently missing.

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/reference/plugins.md around line 515,
the file is missing a trailing newline; add a single newline character at the
end of the file so it ends with exactly one newline (ensure no extra blank lines
are appended).

7. **Use database connection pooling** - singleton pattern for clients
8. **Set appropriate status codes** - 201 for created, 204 for deleted
9. **Protect sensitive routes** with authentication middleware
10. **Log requests and errors** for debugging and monitoring
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add trailing newline to file.

File should end with a single newline character (MD047), currently missing.

🤖 Prompt for AI Agents
In plugins/nuxt/skills/nuxt-best-practices/reference/server.md at line 593, the
file is missing a trailing newline; add a single newline character at the end of
the file so it ends with one blank line (ensure there is exactly one trailing
newline and no extra blank lines).

@HugoRCD
Copy link
Member

HugoRCD commented Oct 17, 2025

@amondnet Before I start reviewing all this, wouldn't it be better to use Nuxt's future MCP server?
nuxt/nuxt.com#2054

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants