environment-setup-guide
Guide developers through setting up development environments with proper tools, dependencies, and configurations
nextjs-app-router-patterns
Master Next.js 14+ App Router with Server Components, streaming, parallel routes, and advanced data fetching. Use when building Next.js applications, implementing SSR/SSG, or optimizing React Server Components.
Full skill instructions
Comprehensive patterns for Next.js 14+ App Router architecture, Server Components, and modern full-stack React development.
| Mode | Where | When to Use |
|---|---|---|
| Server Components | Server only | Data fetching, heavy computation, secrets |
| Client Components | Browser | Interactivity, hooks, browser APIs |
| Static | Build time | Content that rarely changes |
| Dynamic | Request time | Personalized or real-time data |
| Streaming | Progressive | Large pages, slow data sources |
app/
├── layout.tsx # Shared UI wrapper
├── page.tsx # Route UI
├── loading.tsx # Loading UI (Suspense)
├── error.tsx # Error boundary
├── not-found.tsx # 404 UI
├── route.ts # API endpoint
├── template.tsx # Re-mounted layout
├── default.tsx # Parallel route fallback
└── opengraph-image.tsx # OG image generation
// app/layout.tsx
import { Inter } from 'next/font/google'
import { Providers } from './providers'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: { default: 'My App', template: '%s | My App' },
description: 'Built with Next.js App Router',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<Providers>{children}</Providers>
</body>
</html>
)
}
// app/page.tsx - Server Component by default
async function getProducts() {
const res = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 }, // ISR: revalidate every hour
})
return res.json()
}
export default async function HomePage() {
const products = await getProducts()
return (
<main>
<h1>Products</h1>
<ProductGrid products={products} />
</main>
)
}
// app/products/page.tsx
import { Suspense } from 'react'
import { ProductList, ProductListSkeleton } from '@/components/products'
import { FilterSidebar } from '@/components/filters'
interface SearchParams {
category?: string
sort?: 'price' | 'name' | 'date'
page?: string
}
export default async function ProductsPage({
searchParams,
}: {
searchParams: Promise<SearchParams>
}) {
const params = await searchParams
return (
<div className="flex gap-8">
<FilterSidebar />
<Suspense
key={JSON.stringify(params)}
fallback={<ProductListSkeleton />}
>
<ProductList
category={params.category}
sort={params.sort}
page={Number(params.page) || 1}
/>
</Suspense>
</div>
)
}
// components/products/ProductList.tsx - Server Component
async function getProducts(filters: ProductFilters) {
const res = await fetch(
`${process.env.API_URL}/products?${new URLSearchParams(filters)}`,
{ next: { tags: ['products'] } }
)
if (!res.ok) throw new Error('Failed to fetch products')
return res.json()
}
export async function ProductList({ category, sort, page }: ProductFilters) {
const { products, totalPages } = await getProducts({ category, sort, page })
return (
<div>
<div className="grid grid-cols-3 gap-4">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
<Pagination currentPage={page} totalPages={totalPages} />
</div>
)
}
// components/products/AddToCartButton.tsx
'use client'
import { useState, useTransition } from 'react'
import { addToCart } from '@/app/actions/cart'
export function AddToCartButton({ productId }: { productId: string }) {
const [isPending, startTransition] = useTransition()
const [error, setError] = useState<string | null>(null)
const handleClick = () => {
setError(null)
startTransition(async () => {
const result = await addToCart(productId)
if (result.error) {
setError(result.error)
}
})
}
return (
<div>
<button
onClick={handleClick}
disabled={isPending}
className="btn-primary"
>
{isPending ? 'Adding...' : 'Add to Cart'}
</button>
{error && <p className="text-red-500 text-sm">{error}</p>}
</div>
)
}
// app/actions/cart.ts
"use server";
import { revalidateTag } from "next/cache";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export async function addToCart(productId: string) {
const cookieStore = await cookies();
const sessionId = cookieStore.get("session")?.value;
if (!sessionId) {
redirect("/login");
}
try {
await db.cart.upsert({
where: { sessionId_productId: { sessionId, productId } },
update: { quantity: { increment: 1 } },
create: { sessionId, productId, quantity: 1 },
});
revalidateTag("cart");
return { success: true };
} catch (error) {
return { error: "Failed to add item to cart" };
}
}
export async function checkout(formData: FormData) {
const address = formData.get("address") as string;
const payment = formData.get("payment") as string;
// Validate
if (!address || !payment) {
return { error: "Missing required fields" };
}
// Process order
const order = await processOrder({ address, payment });
// Redirect to confirmation
redirect(`/orders/${order.id}/confirmation`);
}
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
analytics,
team,
}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<div className="dashboard-grid">
<main>{children}</main>
<aside className="analytics-panel">{analytics}</aside>
<aside className="team-panel">{team}</aside>
</div>
)
}
// app/dashboard/@analytics/page.tsx
export default async function AnalyticsSlot() {
const stats = await getAnalytics()
return <AnalyticsChart data={stats} />
}
// app/dashboard/@analytics/loading.tsx
export default function AnalyticsLoading() {
return <ChartSkeleton />
}
// app/dashboard/@team/page.tsx
export default async function TeamSlot() {
const members = await getTeamMembers()
return <TeamList members={members} />
}
// File structure for photo modal
// app/
// ├── @modal/
// │ ├── (.)photos/[id]/page.tsx # Intercept
// │ └── default.tsx
// ├── photos/
// │ └── [id]/page.tsx # Full page
// └── layout.tsx
// app/@modal/(.)photos/[id]/page.tsx
import { Modal } from '@/components/Modal'
import { PhotoDetail } from '@/components/PhotoDetail'
export default async function PhotoModal({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const photo = await getPhoto(id)
return (
<Modal>
<PhotoDetail photo={photo} />
</Modal>
)
}
// app/photos/[id]/page.tsx - Full page version
export default async function PhotoPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const photo = await getPhoto(id)
return (
<div className="photo-page">
<PhotoDetail photo={photo} />
<RelatedPhotos photoId={id} />
</div>
)
}
// app/layout.tsx
export default function RootLayout({
children,
modal,
}: {
children: React.ReactNode
modal: React.ReactNode
}) {
return (
<html>
<body>
{children}
{modal}
</body>
</html>
)
}
// app/product/[id]/page.tsx
import { Suspense } from 'react'
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
// This data loads first (blocking)
const product = await getProduct(id)
return (
<div>
{/* Immediate render */}
<ProductHeader product={product} />
{/* Stream in reviews */}
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews productId={id} />
</Suspense>
{/* Stream in recommendations */}
<Suspense fallback={<RecommendationsSkeleton />}>
<Recommendations productId={id} />
</Suspense>
</div>
)
}
// These components fetch their own data
async function Reviews({ productId }: { productId: string }) {
const reviews = await getReviews(productId) // Slow API
return <ReviewList reviews={reviews} />
}
async function Recommendations({ productId }: { productId: string }) {
const products = await getRecommendations(productId) // ML-based, slow
return <ProductCarousel products={products} />
}
// app/api/products/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const category = searchParams.get("category");
const products = await db.product.findMany({
where: category ? { category } : undefined,
take: 20,
});
return NextResponse.json(products);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const product = await db.product.create({
data: body,
});
return NextResponse.json(product, { status: 201 });
}
// app/api/products/[id]/route.ts
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const { id } = await params;
const product = await db.product.findUnique({ where: { id } });
if (!product) {
return NextResponse.json({ error: "Product not found" }, { status: 404 });
}
return NextResponse.json(product);
}
// app/products/[slug]/page.tsx
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
type Props = {
params: Promise<{ slug: string }>
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
const product = await getProduct(slug)
if (!product) return {}
return {
title: product.name,
description: product.description,
openGraph: {
title: product.name,
description: product.description,
images: [{ url: product.image, width: 1200, height: 630 }],
},
twitter: {
card: 'summary_large_image',
title: product.name,
description: product.description,
images: [product.image],
},
}
}
export async function generateStaticParams() {
const products = await db.product.findMany({ select: { slug: true } })
return products.map((p) => ({ slug: p.slug }))
}
export default async function ProductPage({ params }: Props) {
const { slug } = await params
const product = await getProduct(slug)
if (!product) notFound()
return <ProductDetail product={product} />
}
// No cache (always fresh)
fetch(url, { cache: "no-store" });
// Cache forever (static)
fetch(url, { cache: "force-cache" });
// ISR - revalidate after 60 seconds
fetch(url, { next: { revalidate: 60 } });
// Tag-based invalidation
fetch(url, { next: { tags: ["products"] } });
// Invalidate via Server Action
("use server");
import { revalidateTag, revalidatePath } from "next/cache";
export async function updateProduct(id: string, data: ProductData) {
await db.product.update({ where: { id }, data });
revalidateTag("products");
revalidatePath("/products");
}
Guide developers through setting up development environments with proper tools, dependencies, and configurations
Implement Exa lint rules, policy enforcement, and automated guardrails. Use when setting up code quality rules for Exa integrations, implementing pre-commit hooks, or configuring CI policy checks for Exa best practices. Trigger with phrases like "exa policy", "exa lint", "exa guardrails", "exa be...
Configure CI/CD pipelines for Documenso integrations. Use when setting up automated testing, deployment pipelines, or continuous integration for Documenso projects. Trigger with phrases like "documenso CI", "documenso GitHub Actions", "documenso pipeline", "documenso automated testing".
Build check model availability and implement fallback chains. Use when building resilient systems or handling model outages. Trigger with phrases like 'openrouter availability', 'openrouter fallback', 'openrouter model down', 'openrouter health check'.
Configure Langfuse enterprise organization management and access control. Use when implementing team access controls, configuring organization settings, or setting up role-based permissions for Langfuse projects. Trigger with phrases like "langfuse RBAC", "langfuse teams", "langfuse organization"...
Execute compliance and security auditing for Cursor usage. Triggers on "cursor compliance", "cursor audit", "cursor security review", "cursor soc2", "cursor gdpr". Use when analyzing or auditing cursor compliance audit. Trigger with phrases like "cursor compliance audit", "cursor audit", "cursor".
Configure Apollo.io CI/CD integration. Use when setting up automated testing, continuous integration, or deployment pipelines for Apollo integrations. Trigger with phrases like "apollo ci", "apollo github actions", "apollo pipeline", "apollo ci/cd", "apollo automated tests".
Create robust Python automation with full logging and safety checks. Use when tasks need complex data processing, authenticated API work, conditional file operations, or error handling beyond simple shell commands.
Configure PostHog across development, staging, and production environments. Use when setting up multi-environment deployments, configuring per-environment secrets, or implementing environment-specific PostHog configurations. Trigger with phrases like "posthog environments", "posthog staging", "po...
Expert in designing and building autonomous AI agents. Masters tool use, memory systems, planning strategies, and multi-agent orchestration. Use when: build agent, AI agent, autonomous agent, tool use, function calling.
Validate insecure deserialization checker operations. Auto-activating skill for Security Fundamentals. Triggers on: insecure deserialization checker, insecure deserialization checker Part of the Security Fundamentals skill category. Use when working with insecure deserialization checker functiona...
Manage path traversal finder operations. Auto-activating skill for Security Fundamentals. Triggers on: path traversal finder, path traversal finder Part of the Security Fundamentals skill category. Use when working with path traversal finder functionality. Trigger with phrases like "path traversa...
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience