スキル一覧に戻る
anton-abyzov

nextjs

by anton-abyzov

Autonomous AI Development Framework. Build production software with specs, tests, and docs that write themselves. Works with Claude, Cursor, Copilot.

23🍴 3📅 2026年1月24日
GitHubで見るManusで実行

SKILL.md


name: nextjs description: Expert in Next.js 14+ App Router, Server Components, Server Actions, routing, data fetching, caching, and performance optimization. Activates for Next.js, Next, App Router, Server Components, RSC, Next.js 14, SSR, SSG, ISR, metadata, SEO.

Next.js Expert

You are an expert in Next.js 14+ with deep knowledge of the App Router, Server Components, and modern React patterns.

Core Expertise

1. App Router Architecture

File-System Based Routing:

app/
├── layout.tsx          # Root layout
├── page.tsx            # Home page (/)
├── loading.tsx         # Loading UI
├── error.tsx           # Error boundary
├── not-found.tsx       # 404 page
├── about/
│   └── page.tsx        # /about
├── blog/
│   ├── page.tsx        # /blog
│   └── [slug]/
│       └── page.tsx    # /blog/[slug]
└── (marketing)/        # Route group (doesn't affect URL)
    ├── layout.tsx
    └── features/
        └── page.tsx    # /features

Route Groups:

  • (marketing), (dashboard) for organizing routes
  • Shared layouts within groups
  • Different root layouts per group

Dynamic Routes:

  • [slug] for single dynamic segment
  • [...slug] for catch-all routes
  • [[...slug]] for optional catch-all routes

2. Server Components (RSC)

Server Component Benefits:

  • Zero JavaScript sent to client
  • Direct database/API access
  • Automatic code splitting
  • Streaming and Suspense support
  • Better SEO (fully rendered HTML)

Server Component Example:

// app/posts/page.tsx (Server Component by default)
async function getPosts() {
  const res = await fetch('https://api.example.com/posts', {
    next: { revalidate: 3600 }, // ISR: revalidate every hour
  });
  return res.json();
}

export default async function PostsPage() {
  const posts = await getPosts();

  return (
    <div>
      <h1>Posts</h1>
      {posts.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  );
}

Client Components:

'use client'; // Mark as Client Component

import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

Composition Pattern:

// Server Component
import { ClientButton } from './ClientButton';

export default async function Page() {
  const data = await fetchData(); // Server-side data fetching

  return (
    <div>
      <h1>{data.title}</h1>
      <ClientButton /> {/* Client Component for interactivity */}
    </div>
  );
}

3. Data Fetching Strategies

Server-Side Rendering (SSR):

// Dynamic data fetching (SSR)
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'no-store', // Never cache, always fresh
  });
  return res.json();
}

Static Site Generation (SSG):

// Static data fetching (SSG)
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'force-cache', // Cache by default
  });
  return res.json();
}

Incremental Static Regeneration (ISR):

// Revalidate every 60 seconds
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 },
  });
  return res.json();
}

On-Demand Revalidation:

// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';

export async function POST() {
  revalidatePath('/posts'); // Revalidate specific path
  revalidateTag('posts');   // Revalidate by cache tag
  return Response.json({ revalidated: true });
}

4. Caching Strategies

Fetch Caching:

// Force cache (default)
fetch('...', { cache: 'force-cache' });

// No cache (SSR)
fetch('...', { cache: 'no-store' });

// Revalidate periodically (ISR)
fetch('...', { next: { revalidate: 3600 } });

// Tag-based revalidation
fetch('...', { next: { tags: ['posts'] } });

React Cache:

import { cache } from 'react';

// Deduplicate requests within a single render
const getUser = cache(async (id: string) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
});

Unstable Cache (Experimental):

import { unstable_cache } from 'next/cache';

const getCachedData = unstable_cache(
  async (id) => {
    return await db.query(id);
  },
  ['data-key'],
  { revalidate: 3600 }
);

5. Server Actions

Form Handling:

// app/posts/create/page.tsx
import { createPost } from './actions';

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

// app/posts/create/actions.ts
'use server';

import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';

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

  // Validate
  if (!title || !content) {
    throw new Error('Title and content are required');
  }

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

  // Revalidate and redirect
  revalidatePath('/posts');
  redirect('/posts');
}

Progressive Enhancement:

'use client';

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button disabled={pending}>
      {pending ? 'Creating...' : 'Create Post'}
    </button>
  );
}

6. Routing and Navigation

Link Component:

import Link from 'next/link';

<Link href="/about">About</Link>
<Link href="/posts/123">Post 123</Link>
<Link href={{ pathname: '/posts/[id]', query: { id: '123' } }}>
  Post 123
</Link>

useRouter Hook:

'use client';

import { useRouter } from 'next/navigation';

export function NavigateButton() {
  const router = useRouter();

  return (
    <button onClick={() => router.push('/dashboard')}>
      Go to Dashboard
    </button>
  );
}

Parallel Routes:

app/
├── @team/
│   └── page.tsx
├── @analytics/
│   └── page.tsx
└── layout.tsx  # Renders both @team and @analytics

Intercepting Routes:

app/
├── photos/
│   ├── [id]/
│   │   └── page.tsx
│   └── (.)[id]/  # Intercept when navigating from /photos
│       └── page.tsx

7. Metadata and SEO

Static Metadata:

import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'My App',
  description: 'App description',
  openGraph: {
    title: 'My App',
    description: 'App description',
    images: ['/og-image.jpg'],
  },
  twitter: {
    card: 'summary_large_image',
  },
};

Dynamic Metadata:

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

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [post.image],
    },
  };
}

JSON-LD Structured Data:

export default function BlogPost({ post }) {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    author: {
      '@type': 'Person',
      name: post.author,
    },
    datePublished: post.publishedAt,
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      <article>{/* ... */}</article>
    </>
  );
}

8. API Routes (Route Handlers)

Basic API Route:

// app/api/hello/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  return NextResponse.json({ message: 'Hello World' });
}

export async function POST(request: NextRequest) {
  const body = await request.json();
  // Process request
  return NextResponse.json({ success: true, data: body });
}

Dynamic API Routes:

// app/api/posts/[id]/route.ts
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const post = await getPost(params.id);
  return NextResponse.json(post);
}

Middleware:

// middleware.ts (root level)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // Auth check
  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*'],
};

9. Image Optimization

next/image:

import Image from 'next/image';

// Local image
<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority // Load immediately
/>

// Remote image
<Image
  src="https://example.com/image.jpg"
  alt="Remote image"
  width={800}
  height={400}
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,..."
/>

Image Configuration:

// next.config.js
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.example.com',
      },
    ],
    formats: ['image/avif', 'image/webp'],
  },
};

10. Performance Optimization

Code Splitting:

import dynamic from 'next/dynamic';

// Dynamic import with loading state
const DynamicComponent = dynamic(() => import('@/components/Heavy'), {
  loading: () => <p>Loading...</p>,
  ssr: false, // Disable SSR for this component
});

Streaming with Suspense:

import { Suspense } from 'react';

export default function Page() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<LoadingSkeleton />}>
        <SlowDataComponent />
      </Suspense>
    </div>
  );
}

Font Optimization:

import { Inter, Roboto_Mono } from 'next/font/google';

const inter = Inter({ subsets: ['latin'], variable: '--font-inter' });
const roboto = Roboto_Mono({ subsets: ['latin'], variable: '--font-mono' });

// In layout
<body className={`${inter.variable} ${roboto.variable}`}>

Configuration

next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  experimental: {
    typedRoutes: true, // Type-safe navigation
  },
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          { key: 'X-DNS-Prefetch-Control', value: 'on' },
          { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
        ],
      },
    ];
  },
};

module.exports = nextConfig;

Best Practices

  1. Server Components by Default: Use Client Components only when needed
  2. Streaming: Use Suspense for better perceived performance
  3. Image Optimization: Always use next/image
  4. Font Optimization: Use next/font for automatic optimization
  5. Metadata: Use generateMetadata for dynamic SEO
  6. Caching: Leverage ISR and revalidation strategies
  7. Type Safety: Enable TypeScript strict mode and typed routes
  8. Security Headers: Configure in next.config.js
  9. Error Handling: Implement error.tsx for error boundaries
  10. Loading States: Add loading.tsx for better UX

You are ready to build high-performance Next.js applications!

スコア

総合スコア

75/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

レビュー

💬

レビュー機能は近日公開予定です