distributions/codex/skills/nextjs-fullstack-patterns/SKILL.md
Build production Next.js applications with App Router patterns, server components, data fetching strategies, authentication, and deployment. Covers the full stack from database to UI. Triggers on Next.js development, React server components, App Router, or full-stack TypeScript requests.
npx skillsauth add a-organvm/a-i--skills nextjs-fullstack-patternsInstall 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.
Production patterns for modern Next.js applications.
app/
├── layout.tsx # Root layout (required)
├── page.tsx # Home page (/)
├── loading.tsx # Loading UI
├── error.tsx # Error boundary
├── not-found.tsx # 404 page
├── globals.css # Global styles
│
├── (marketing)/ # Route group (no URL impact)
│ ├── about/
│ │ └── page.tsx # /about
│ └── blog/
│ └── page.tsx # /blog
│
├── dashboard/
│ ├── layout.tsx # Dashboard layout
│ ├── page.tsx # /dashboard
│ └── settings/
│ └── page.tsx # /dashboard/settings
│
├── api/
│ └── route.ts # API route (/api)
│
└── [slug]/
└── page.tsx # Dynamic route
| File | Purpose |
|------|---------|
| page.tsx | Unique UI for route |
| layout.tsx | Shared UI, preserves state |
| template.tsx | Like layout but re-mounts |
| loading.tsx | Loading UI (Suspense) |
| error.tsx | Error boundary |
| not-found.tsx | 404 UI |
| route.ts | API endpoint |
// app/posts/page.tsx (Server Component by default)
async function PostsPage() {
const posts = await db.posts.findMany(); // Direct DB access
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
export default PostsPage;
'use client'; // Required directive
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}
| Server Components | Client Components | |-------------------|-------------------| | Data fetching | Event handlers (onClick, etc.) | | Access backend resources | useState, useEffect | | Sensitive data (tokens, keys) | Browser APIs | | Large dependencies | Interactivity | | SEO-critical content | Real-time updates |
// Server Component with Client island
import { Counter } from './counter'; // Client component
async function Page() {
const data = await fetchData(); // Server-side
return (
<div>
<h1>{data.title}</h1>
<Counter /> {/* Client island */}
</div>
);
}
// Direct async/await in component
async function Page() {
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache', // Default: cache
// cache: 'no-store', // No cache (dynamic)
// next: { revalidate: 60 } // ISR: revalidate every 60s
});
return <div>{data.title}</div>;
}
async function Page() {
// Parallel fetching (don't await sequentially)
const [posts, comments] = await Promise.all([
getPosts(),
getComments(),
]);
return (
<>
<Posts data={posts} />
<Comments data={comments} />
</>
);
}
import { Suspense } from 'react';
async function Page() {
return (
<div>
<h1>Dashboard</h1>
{/* Streams in when ready */}
<Suspense fallback={<Loading />}>
<SlowComponent />
</Suspense>
</div>
);
}
// app/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;
await db.posts.create({ data: { title } });
revalidatePath('/posts');
redirect('/posts');
}
// app/posts/new/page.tsx
import { createPost } from '../actions';
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Create</button>
</form>
);
}
'use client';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving...' : 'Save'}
</button>
);
}
'use client';
import { useActionState } from 'react';
import { createPost } from './actions';
export function PostForm() {
const [state, action, pending] = useActionState(createPost, null);
return (
<form action={action}>
<input name="title" />
{state?.error && <p>{state.error}</p>}
<button disabled={pending}>Create</button>
</form>
);
}
// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const posts = await db.posts.findMany();
return NextResponse.json(posts);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const post = await db.posts.create({ data: body });
return NextResponse.json(post, { status: 201 });
}
// app/api/posts/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const post = await db.posts.findUnique({
where: { id: params.id }
});
if (!post) {
return NextResponse.json(
{ error: 'Not found' },
{ status: 404 }
);
}
return NextResponse.json(post);
}
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('token'); <!-- allow-secret -->
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*'],
};
// lib/auth.ts
import { cookies } from 'next/headers';
import { cache } from 'react';
export const getUser = cache(async () => {
const token = cookies().get('token')?.value; <!-- allow-secret -->
if (!token) return null;
// Validate token, fetch user
return await validateAndFetchUser(token);
});
// app/dashboard/page.tsx
import { getUser } from '@/lib/auth';
import { redirect } from 'next/navigation';
export default async function Dashboard() {
const user = await getUser();
if (!user) {
redirect('/login');
}
return <div>Welcome, {user.name}</div>;
}
// lib/db.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const db = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = db;
}
// lib/data/posts.ts
import { db } from '@/lib/db';
import { cache } from 'react';
export const getPosts = cache(async () => {
return db.post.findMany({
orderBy: { createdAt: 'desc' },
include: { author: true },
});
});
export const getPost = cache(async (id: string) => {
return db.post.findUnique({
where: { id },
include: { author: true },
});
});
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
// app/posts/[id]/page.tsx
import { notFound } from 'next/navigation';
export default async function Post({ params }: { params: { id: string } }) {
const post = await getPost(params.id);
if (!post) {
notFound();
}
return <article>{post.content}</article>;
}
// app/about/page.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'About Us',
description: 'Learn about our company',
};
// app/posts/[id]/page.tsx
import type { Metadata } from 'next';
export async function generateMetadata({
params
}: {
params: { id: string }
}): Promise<Metadata> {
const post = await getPost(params.id);
return {
title: post?.title,
description: post?.excerpt,
};
}
├── app/ # App Router
├── components/
│ ├── ui/ # Reusable UI components
│ └── features/ # Feature-specific components
├── lib/
│ ├── db.ts # Database client
│ ├── auth.ts # Auth utilities
│ └── utils.ts # General utilities
├── actions/ # Server Actions
├── types/ # TypeScript types
├── hooks/ # Custom React hooks
└── public/ # Static assets
references/deployment-checklist.md - Production deploymentreferences/performance-patterns.md - Optimization techniquesreferences/testing-patterns.md - Testing Next.js appsdevelopment
Create algorithmic and generative art using mathematical patterns, noise functions, particle systems, and procedural generation. Covers flow fields, L-systems, fractals, and creative coding foundations. Triggers on generative art, algorithmic art, creative coding, procedural generation, or mathematical visualization requests.
development
Audits web applications and architectures for compliance with GDPR, CCPA, and other privacy regulations, focusing on consent, data minimization, and user rights.
development
Optimize Google Cloud Platform resource allocation and manage cloud credits efficiently. Use when planning GCP deployments, analyzing cloud spend, maximizing value from expiring credits, right-sizing instances, or designing cost-effective architectures. Triggers on GCP cost optimization, credit management, resource allocation planning, or cloud budget concerns.
testing
Designs engaging gameplay loops, economies, and progression systems, balancing challenge and reward for interactive experiences.