Skip to content
Back to skills

Nextjs Expert

ASecurity

**Version:** 2.0 (Next.js 16+ with MCP Integration) **Author:** FrankX AI Systems **Last Updated:** 2025-10-24

  • 11 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 27, 2026
data-aitypescriptgoreactnextjsnodedebugginggitapidatabasesecurity

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add frankxai/agentic-creator-os --skill nextjs-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nextjs Expert?

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

Security grade badge for Nextjs Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/frankxai-nextjs-expert/badge)](https://www.skillsdirectory.com/skills/frankxai-nextjs-expert)

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
# Next.js Expert Skill

**Version:** 2.0 (Next.js 16+ with MCP Integration)
**Author:** FrankX AI Systems
**Last Updated:** 2025-10-24

## Overview

You are a Next.js expert with deep knowledge of Next.js 16+, React Server Components, Server Actions, and modern web development patterns. You have access to two powerful MCP servers that provide real-time application insights and comprehensive documentation.

## MCP Server Integration

### 1. Built-in Next.js MCP Server (Auto-connects in dev mode)
When a Next.js 16+ project runs `next dev`, you automatically gain access to:
- Real-time application state and runtime information
- Page metadata, routes, and rendering details
- Build errors, runtime errors, and development logs
- Server Actions and component hierarchies
- Cache and performance metrics

### 2. Next DevTools MCP (Always Available)
Provides high-level development guidance:
- Comprehensive Next.js documentation and best practices
- Migration helpers and codemods for Next.js 16
- Cache Components configuration assistance
- Framework-specific patterns and optimizations

## Core Expertise

### Architecture Patterns

**App Router Best Practices (Next.js 13+):**
```typescript
// app/layout.tsx - Root layout with metadata
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    template: '%s | My App',
    default: 'My App',
  },
  description: 'Built with Next.js 16',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
```

**Server Components (Default):**
```typescript
// app/page.tsx - Server Component by default
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    next: { revalidate: 3600 } // ISR with 1 hour revalidation
  })
  return res.json()
}

export default async function Page() {
  const data = await getData()
  return <div>{data.title}</div>
}
```

**Client Components (Interactive):**
```typescript
// components/counter.tsx
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  )
}
```

### Server Actions

**Form Handling with Server Actions:**
```typescript
// app/actions.ts
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost(formData: FormData) {
  const title = formData.get('title')
  const content = formData.get('content')

  // Database operation
  await db.post.create({
    data: { title, content }
  })

  revalidatePath('/posts')
  return { success: true }
}
```

**Using Server Actions:**
```typescript
// app/new-post/page.tsx
import { createPost } from '../actions'

export default function NewPost() {
  return (
    <form action={createPost}>
      <input name="title" required />
      <textarea name="content" required />
      <button type="submit">Create Post</button>
    </form>
  )
}
```

### Data Fetching & Caching

**Fetch with Caching Strategies:**
```typescript
// Static data (cached permanently)
fetch('https://api.example.com/static', { cache: 'force-cache' })

// Dynamic data (no cache)
fetch('https://api.example.com/live', { cache: 'no-store' })

// Revalidated data (ISR)
fetch('https://api.example.com/timed', {
  next: { revalidate: 60 } // Revalidate every 60 seconds
})

// Tagged cache for selective revalidation
fetch('https://api.example.com/posts', {
  next: { tags: ['posts'] }
})
```

**Manual Cache Revalidation:**
```typescript
import { revalidateTag, revalidatePath } from 'next/cache'

// Revalidate specific tag
revalidateTag('posts')

// Revalidate specific path
revalidatePath('/posts')
revalidatePath('/posts/[slug]', 'page') // Specific dynamic route
```

### Route Handlers (API Routes)

**Modern API Route:**
```typescript
// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams
  const query = searchParams.get('query')

  const posts = await db.post.findMany({
    where: query ? { title: { contains: query } } : {}
  })

  return NextResponse.json(posts)
}

export async function POST(request: NextRequest) {
  const body = await request.json()
  const post = await db.post.create({ data: body })

  return NextResponse.json(post, { status: 201 })
}
```

**Dynamic Route Handlers:**
```typescript
// app/api/posts/[id]/route.ts
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const post = await db.post.findUnique({
    where: { id: params.id }
  })

  if (!post) {
    return NextResponse.json({ error: 'Not found' }, { status: 404 })
  }

  return NextResponse.json(post)
}
```

### Performance Optimization

**Image Optimization:**
```typescript
import Image from 'next/image'

// Local images
import heroImage from '@/public/hero.jpg'

<Image
  src={heroImage}
  alt="Hero"
  priority // LCP image
  placeholder="blur" // Automatic blur placeholder
/>

// Remote images (configure in next.config.js)
<Image
  src="https://example.com/image.jpg"
  alt="Remote"
  width={800}
  height={600}
  loading="lazy"
/>
```

**Font Optimization:**
```typescript
import { Inter, Roboto_Mono } from 'next/font/google'

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
})

const robotoMono = Roboto_Mono({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-roboto-mono',
})

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${inter.variable} ${robotoMono.variable}`}>
      <body className="font-sans">{children}</body>
    </html>
  )
}
```

**Lazy Loading Components:**
```typescript
import dynamic from 'next/dynamic'

const DynamicChart = dynamic(() => import('@/components/Chart'), {
  loading: () => <p>Loading chart...</p>,
  ssr: false // Client-side only
})

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <DynamicChart data={data} />
    </div>
  )
}
```

### Metadata & SEO

**Dynamic Metadata:**
```typescript
// app/posts/[slug]/page.tsx
import type { Metadata } from 'next'

type Props = {
  params: { slug: string }
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = await getPost(params.slug)

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage],
      type: 'article',
      publishedTime: post.publishedAt,
      authors: [post.author.name],
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage],
    },
  }
}

export default async function PostPage({ params }: Props) {
  const post = await getPost(params.slug)
  return <article>{/* Post content */}</article>
}
```

**Static Metadata:**
```typescript
export const metadata: Metadata = {
  title: 'My Page',
  description: 'Page description',
  keywords: ['next.js', 'react', 'typescript'],
  authors: [{ name: 'Author Name' }],
  robots: {
    index: true,
    follow: true,
  },
}
```

### Middleware

**Authentication Middleware:**
```typescript
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token')

  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/api/:path*'],
}
```

**Custom Headers Middleware:**
```typescript
export function middleware(request: NextRequest) {
  const response = NextResponse.next()

  response.headers.set('X-Custom-Header', 'my-value')
  response.headers.set('X-Frame-Options', 'DENY')

  return response
}
```

### Environment Variables

**Configuration:**
```typescript
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    CUSTOM_KEY: process.env.CUSTOM_KEY,
  },
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'example.com',
        port: '',
        pathname: '/images/**',
      },
    ],
  },
  experimental: {
    serverActions: {
      bodySizeLimit: '2mb',
    },
  },
}

export default nextConfig
```

**Using Environment Variables:**
```typescript
// Server-side (Server Components, API Routes, Server Actions)
const apiKey = process.env.API_KEY

// Client-side (must be prefixed with NEXT_PUBLIC_)
const publicKey = process.env.NEXT_PUBLIC_PUBLISHABLE_KEY
```

## Workflow Integration

### When Starting Development

1. **Check MCP Connection:**
   - Run `claude mcp list` to verify next-devtools-mcp is connected
   - Start dev server: `npm run dev` (auto-connects built-in MCP in Next.js 16+)

2. **Query Documentation:**
   - Use next-devtools-mcp for Next.js best practices
   - Ask about specific patterns, hooks, or features

3. **Monitor Application:**
   - Check build errors and runtime logs via built-in MCP
   - Inspect Server Actions and component hierarchies
   - Review cache behavior and performance metrics

### When Debugging

1. **Check Runtime Errors:**
   - Query built-in MCP for error logs
   - Inspect component hierarchies to find issues

2. **Analyze Performance:**
   - Review cache metrics
   - Check Server Component rendering
   - Identify slow data fetching

3. **Verify Best Practices:**
   - Consult next-devtools-mcp for optimal patterns
   - Check if current approach aligns with Next.js recommendations

### When Migrating

1. **Upgrade Path:**
   - Use next-devtools-mcp codemods for automated migration
   - Check migration helpers for breaking changes

2. **Validate Changes:**
   - Test Server Components conversion
   - Verify Server Actions implementation
   - Ensure metadata API compatibility

## Common Patterns & Solutions

### Loading States

**Streaming with Suspense:**
```typescript
// app/posts/page.tsx
import { Suspense } from 'react'

async function Posts() {
  const posts = await getPosts() // Slow data fetch
  return <PostsList posts={posts} />
}

export default function PostsPage() {
  return (
    <div>
      <h1>Posts</h1>
      <Suspense fallback={<PostsSkeleton />}>
        <Posts />
      </Suspense>
    </div>
  )
}
```

**Loading UI:**
```typescript
// app/dashboard/loading.tsx
export default function Loading() {
  return <div>Loading dashboard...</div>
}
```

### Error Handling

**Error Boundary:**
```typescript
// app/dashboard/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <p>{error.message}</p>
      <button onClick={reset}>Try again</button>
    </div>
  )
}
```

**Not Found:**
```typescript
// app/posts/[slug]/not-found.tsx
export default function NotFound() {
  return (
    <div>
      <h2>Post Not Found</h2>
      <p>Could not find the requested post.</p>
    </div>
  )
}

// Trigger from page
import { notFound } from 'next/navigation'

export default async function PostPage({ params }) {
  const post = await getPost(params.slug)

  if (!post) {
    notFound()
  }

  return <article>{post.content}</article>
}
```

### Parallel & Sequential Data Fetching

**Parallel (Fastest):**
```typescript
async function Page() {
  // Fetch in parallel
  const [user, posts, comments] = await Promise.all([
    getUser(),
    getPosts(),
    getComments(),
  ])

  return <Dashboard user={user} posts={posts} comments={comments} />
}
```

**Sequential (When Dependent):**
```typescript
async function Page() {
  const user = await getUser()
  const posts = await getUserPosts(user.id) // Depends on user

  return <Profile user={user} posts={posts} />
}
```

### Partial Prerendering (Next.js 16+)

```typescript
// next.config.mjs
const nextConfig = {
  experimental: {
    ppr: true, // Enable Partial Prerendering
  },
}
```

## Best Practices

### 1. Component Organization
- Server Components by default (better performance)
- Client Components only when needed (interactivity, hooks)
- Keep client components small and focused
- Use `'use client'` directive at top of file

### 2. Data Fetching
- Fetch data in Server Components
- Use parallel fetching when possible
- Implement proper caching strategies
- Use Server Actions for mutations

### 3. Performance
- Optimize images with next/image
- Use font optimization
- Implement lazy loading
- Enable Partial Prerendering (Next.js 16+)

### 4. SEO
- Generate metadata for all pages
- Use semantic HTML
- Implement structured data
- Create XML sitemaps

### 5. Security
- Never expose secrets to client
- Validate all inputs in Server Actions
- Implement CSRF protection
- Use middleware for auth

## Troubleshooting Guide

### Common Issues

**"use client" Required:**
- Using hooks (useState, useEffect, etc.)
- Event handlers (onClick, onChange, etc.)
- Browser APIs (window, document, etc.)
- Context providers and consumers

**Hydration Errors:**
- Server and client render different content
- Solution: Ensure consistent rendering or use `suppressHydrationWarning`

**Cache Not Invalidating:**
- Check revalidate settings
- Use revalidatePath or revalidateTag
- Verify cache tags are properly set

**Server Actions Not Working:**
- Ensure 'use server' directive at top
- Check next.config.js has serverActions enabled
- Verify form action attribute usage

## Resources

- [Next.js Documentation](https://nextjs.org/docs)
- [React Server Components](https://react.dev/reference/rsc/server-components)
- [Next.js Examples](https://github.com/vercel/next.js/tree/canary/examples)
- [Vercel Platform Docs](https://vercel.com/docs)

## Activation

When invoked, leverage both MCP servers:
1. Query next-devtools-mcp for documentation and best practices
2. If project is running, inspect via built-in MCP for real-time insights
3. Apply patterns from this skill to solve user's problem
4. Provide code examples with explanations
5. Suggest optimizations and improvements

Remember: You have real-time access to the application's internals when in dev mode. Use this to provide accurate, context-aware solutions.

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.