library/specializations/web-development/skills/nextjs/SKILL.md
Next.js specific patterns including App Router, React Server Components, Server Actions, streaming, caching, and Vercel deployment.
npx skillsauth add a5c-ai/babysitter nextjsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert assistance for building full-stack Next.js applications with App Router, React Server Components, Server Actions, and modern deployment patterns.
Invoke this skill when you need to:
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | projectName | string | Yes | Name of the Next.js project | | router | string | No | app (default) or pages | | features | array | No | List of features to scaffold | | database | string | No | prisma, drizzle, none | | auth | string | No | nextauth, clerk, none | | styling | string | No | tailwind, css-modules, styled-components |
{
"projectName": "my-saas",
"features": [
"authentication",
"api-routes",
"server-actions",
"parallel-routes",
"middleware"
],
"database": "prisma",
"auth": "nextauth",
"styling": "tailwind"
}
my-saas/
├── app/
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ ├── loading.tsx # Loading UI
│ ├── error.tsx # Error boundary
│ ├── not-found.tsx # 404 page
│ ├── globals.css # Global styles
│ ├── (auth)/ # Route group
│ │ ├── login/
│ │ │ └── page.tsx
│ │ └── register/
│ │ └── page.tsx
│ ├── dashboard/
│ │ ├── layout.tsx # Dashboard layout
│ │ ├── page.tsx
│ │ ├── @sidebar/ # Parallel route
│ │ │ └── default.tsx
│ │ └── settings/
│ │ └── page.tsx
│ └── api/
│ └── [...route]/
│ └── route.ts # API routes
├── components/
│ ├── ui/ # Reusable UI components
│ └── features/ # Feature-specific components
├── lib/
│ ├── actions/ # Server Actions
│ │ └── user-actions.ts
│ ├── db/ # Database utilities
│ │ └── prisma.ts
│ └── utils/ # Utility functions
├── middleware.ts # Edge middleware
├── next.config.ts # Next.js configuration
├── tailwind.config.ts # Tailwind configuration
└── package.json
// app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: {
default: 'My SaaS',
template: '%s | My SaaS',
},
description: 'A modern SaaS application',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}
// app/dashboard/page.tsx
import { Suspense } from 'react';
import { getUser } from '@/lib/db/user';
import { DashboardStats } from '@/components/features/dashboard-stats';
import { DashboardSkeleton } from '@/components/ui/skeletons';
export default async function DashboardPage() {
const user = await getUser();
return (
<main className="container mx-auto py-8">
<h1 className="text-3xl font-bold">Welcome, {user.name}</h1>
<Suspense fallback={<DashboardSkeleton />}>
<DashboardStats userId={user.id} />
</Suspense>
</main>
);
}
// lib/actions/user-actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
import { prisma } from '@/lib/db/prisma';
const updateProfileSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
});
export async function updateProfile(formData: FormData) {
const validatedFields = updateProfileSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
});
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
};
}
const { name, email } = validatedFields.data;
try {
await prisma.user.update({
where: { id: getCurrentUserId() },
data: { name, email },
});
} catch (error) {
return { error: 'Failed to update profile' };
}
revalidatePath('/dashboard/settings');
redirect('/dashboard');
}
// app/dashboard/settings/page.tsx
import { updateProfile } from '@/lib/actions/user-actions';
import { SubmitButton } from '@/components/ui/submit-button';
export default function SettingsPage() {
return (
<form action={updateProfile} className="space-y-4">
<div>
<label htmlFor="name">Name</label>
<input
id="name"
name="name"
type="text"
required
className="w-full border rounded px-3 py-2"
/>
</div>
<div>
<label htmlFor="email">Email</label>
<input
id="email"
name="email"
type="email"
required
className="w-full border rounded px-3 py-2"
/>
</div>
<SubmitButton>Update Profile</SubmitButton>
</form>
);
}
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
const response = NextResponse.next();
// Add security headers
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/db/prisma';
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const page = parseInt(searchParams.get('page') ?? '1');
const limit = parseInt(searchParams.get('limit') ?? '10');
const users = await prisma.user.findMany({
skip: (page - 1) * limit,
take: limit,
select: {
id: true,
name: true,
email: true,
},
});
return NextResponse.json({ users, page, limit });
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await prisma.user.create({
data: body,
});
return NextResponse.json(user, { status: 201 });
}
// Revalidate every hour
export const revalidate = 3600;
export default async function Page() {
const data = await fetchData();
return <div>{/* content */}</div>;
}
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const { path, tag, secret } = await request.json();
if (secret !== process.env.REVALIDATION_SECRET) {
return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
}
if (tag) {
revalidateTag(tag);
} else if (path) {
revalidatePath(path);
}
return NextResponse.json({ revalidated: true });
}
{
"dependencies": {
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"typescript": "^5.7.0"
}
}
development
Model documentation skill for generating model cards following Google's model card framework.
development
MLflow integration skill for experiment tracking, model registry, and artifact management. Enables LLMs to log experiments, compare runs, manage model lifecycle, and retrieve artifacts through the MLflow API.
data-ai
LIME-based local explanation skill for individual predictions across tabular, text, and image data.
devops
Kubeflow Pipelines skill for ML workflow orchestration, component management, and Kubernetes-native ML.