用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/doanchienthangdev/omgkit --skill nextjs命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Automatic design system context injection for UI consistency
AI agent practices test-first development with the Red-Green-Refactor cycle for confident, well-designed code. Use when implementing features, fixing bugs, or establishing testing practices.
The agent enforces mandatory test completion before any task or feature can be marked as done, ensuring code quality through strict validation gates and evidence-based completion criteria.
基于 SOC 职业分类
正在显示 SKILL.md
| name | nextjs |
| description | Next.js development with App Router, Server Components, API routes, and full-stack patterns |
| category | frameworks |
| triggers | ["nextjs","next.js","next","app router","server components","rsc"] |
Modern Next.js development with App Router following industry best practices. This skill covers Server Components, data fetching, API routes, middleware, authentication, and full-stack patterns.
Build production-ready Next.js applications:
// app/layout.tsx - Root layout
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: { default: 'My App', template: '%s | My App' },
description: 'A Next.js application',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
// app/page.tsx - Home page
import { Suspense } from 'react';
export default function HomePage() {
return (
<div className="container mx-auto py-8">
<h1 className="text-3xl font-bold">Featured Products</h1>
<Suspense fallback={<ProductListSkeleton />}>
<ProductList />
</Suspense>
</div>
);
}
// app/products/[id]/page.tsx - Dynamic route
import { notFound } from 'next/navigation';
interface ProductPageProps {
params: { id: string };
}
export async function generateMetadata({ params }: ProductPageProps) {
const product = await getProduct(params.id);
if (!product) return { title: 'Not Found' };
return { title: product.name, description: product.description };
}
export default async function ProductPage({ params }: ProductPageProps) {
const product = await getProduct(params.id);
if (!product) notFound();
return <ProductDetails product={product} />;
}
// Server Component (default)
// app/components/product-list.tsx
import { getProducts } from '@/lib/products';
export async function ProductList() {
const products = await getProducts();
return (
<div className="grid grid-cols-3 gap-6">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
// Client Component
// app/components/add-to-cart-button.tsx
'use client';
import { useState, useTransition } from 'react';
import { addToCart } from '@/lib/actions';
export function AddToCartButton({ productId }: { productId: string }) {
const [isPending, startTransition] = useTransition();
const [message, setMessage] = useState<string | null>(null);
const handleClick = () => {
startTransition( () => {
result = (productId);
(result.);
});
};
(
);
}
// app/lib/actions.ts
'use server';
import { revalidatePath, revalidateTag } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
import { auth } from '@/lib/auth';
const createProductSchema = z.object({
name: z.string().min(1),
price: z.number().positive(),
description: z.string().optional(),
});
export async function createProduct(formData: FormData) {
const session = await auth();
if (!session?.user?.id) throw new Error('Unauthorized');
const validatedFields = createProductSchema.safeParse({
name: formData.get('name'),
price: Number(formData.get('price')),
: formData.(),
});
(!validatedFields.) {
{ : , : validatedFields..(). };
}
product = db..({
: { ...validatedFields., : session.. },
});
();
();
}
() {
session = ();
(!session?.?.) {
{ : , : };
}
db..({
: { : { : session.., productId } },
: { : { : } },
: { : session.., productId, : },
});
();
{ : , : };
}
// Cached data fetching
import { unstable_cache } from 'next/cache';
export const getProducts = unstable_cache(
async () => {
return db.product.findMany({
include: { category: true },
orderBy: { createdAt: 'desc' },
});
},
['products'],
{ tags: ['products'], revalidate: 3600 }
);
// Fetch with built-in caching
async function getUser(id: string) {
const res = await fetch(`https://api.example.com/users/${id}`, {
next: { revalidate: 3600 },
});
return res.json();
}
// No caching for dynamic data
async function getCurrentPrice(symbol: string) {
const res = await fetch(`https://api.example.com/stocks/${symbol}`, {
: ,
});
res.();
}
() {
[product, relatedProducts, reviews] = .([
(id),
(id),
(id),
]);
{ product, relatedProducts, reviews };
}
// app/api/products/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const page = Number(searchParams.get('page') || '1');
const limit = Number(searchParams.get('limit') || '10');
const [products, total] = await Promise.all([
db.product.findMany({ skip: (page - 1) * limit, take: limit }),
db.product.count(),
]);
return NextResponse.json({
data: products,
pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
});
}
export async function () {
session = ();
(!session?.?.) {
.({ : }, { : });
}
body = request.();
product = db..({
: { ...body, : session.. },
});
.(product, { : });
}
() {
product = db..({ : { : params. } });
(!product) {
.({ : }, { : });
}
.(product);
}
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { auth } from '@/lib/auth';
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Public routes
const publicRoutes = ['/', '/login', '/register'];
if (publicRoutes.some((route) => pathname.startsWith(route))) {
return NextResponse.next();
}
// Check authentication
const session = await auth();
if (!session) {
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('callbackUrl', pathname);
return NextResponse.redirect(loginUrl);
}
(pathname.() && session.. !== ) {
.( (, request.));
}
.();
}
config = {
: [],
};
// app/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h2 className="text-2xl font-bold">Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
// app/not-found.tsx
import Link from 'next/link';
export default function NotFound() {
return (
<div className="text-center py-12">
<h2 className="text-2xl font-bold">Page Not Found</h2>
<Link href="/">Return Home</Link>
</>
);
}
// Image optimization
import Image from 'next/image';
export function OptimizedImage() {
return (
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority
placeholder="blur"
/>
);
}
// Dynamic imports
import dynamic from 'next/dynamic';
const DynamicChart = dynamic(() => import('./Chart'), {
loading: () => <p>Loading...</p>,
ssr: false,
});
// Static generation
export async function generateStaticParams() {
const products = await getProducts();
return products.map((product) => ({ : product. }));
}
export default async function ProductPage({ params, searchParams }) {
const product = await getProductBySlug(params.slug);
if (!product) notFound();
const selectedVariant = product.variants.find(
(v) => v.id === searchParams.variant
) || product.variants[0];
return (
<div className="grid md:grid-cols-2 gap-8">
<ProductGallery images={product.images} />
<div>
<h1>{product.name}</h1>
<p>${selectedVariant.price}</p>
<VariantSelector variants={product.variants} />
<AddToCartButton productId={product.id} />
</div>
</div>
);
}