Next.js Template Skill
Generate production-ready Next.js pages with SSR initial load, SWR client-side data management, and server-response-based cache updates.
When to Use This Skill
Use this skill when asked to:
- •Create a new Next.js page with data table
- •Add CRUD functionality to a Next.js application
- •Generate pages with server-side rendering and client-side updates
- •Build admin/settings pages with filtering, pagination, and bulk actions
Architecture Overview
code
┌─────────────────────────────────────────────────────────────┐
│ Browser Request │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ page.tsx (Server Component) │
│ • auth() check │
│ • await searchParams (Next.js 15+) │
│ • Fetch initial data via server actions │
│ • Pass initialData to client component │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ *-table.tsx (Client Component) │
│ • useSWR with fallbackData: initialData │
│ • Context Provider for actions │
│ • updateItems() uses server response (NOT optimistic) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ API Routes (app/api/...) │
│ • withAuth() wrapper │
│ • Proxy to FastAPI backend │
│ • backendGet/Post/Put/Delete helpers │
└─────────────────────────────────────────────────────────────┘
File Structure
code
app/(pages)/setting/{entity}/
├── page.tsx # SSR page (server component)
├── context/
│ └── {entity}-actions-context.tsx # Actions provider
└── _components/
├── actions/
│ ├── add-{entity}-button.tsx
│ └── {entity}-actions-menu.tsx
├── modal/
│ ├── add-{entity}-sheet.tsx
│ └── edit-{entity}-sheet.tsx
├── sidebar/
│ └── status-panel.tsx
└── table/
├── {entity}-table.tsx # Main client component
├── {entity}-table-body.tsx
└── {entity}-table-columns.tsx
app/api/setting/{entity}/
├── route.ts # GET (list), POST (create)
└── [{entityId}]/
├── route.ts # GET, PUT, DELETE
└── status/
└── route.ts # PUT (toggle status)
lib/
├── actions/
│ └── {entity}.actions.ts # Server actions
└── fetch/
├── client.ts # Client-side fetch
├── server.ts # Server-side fetch
└── api-route-helper.ts # API route helpers
types/
└── {entity}.d.ts # TypeScript types
Core Principles
1. SSR + SWR Hybrid Pattern
Server Component (page.tsx):
tsx
// NO "use client" - this is a server component
import { auth } from "@/lib/auth/server-auth";
import { getItems } from "@/lib/actions/items.actions";
import { redirect } from "next/navigation";
import ItemsTable from "./_components/table/items-table";
export default async function ItemsPage({
searchParams,
}: {
searchParams: Promise<{ page?: string; limit?: string; search?: string }>;
}) {
const session = await auth();
if (!session?.accessToken) redirect("/login");
const params = await searchParams; // Next.js 15+ requires await
const page = Number(params.page) || 1;
const limit = Number(params.limit) || 10;
const items = await getItems(limit, (page - 1) * limit, params);
return <ItemsTable initialData={items} />;
}
Client Component (items-table.tsx):
tsx
"use client";
import useSWR from "swr";
import { fetchClient } from "@/lib/fetch/client";
const fetcher = (url: string) => fetchClient.get(url).then(r => r.data);
function ItemsTable({ initialData }) {
const searchParams = useSearchParams();
const page = Number(searchParams?.get("page") || "1");
const { data, mutate, isLoading } = useSWR(
`/api/setting/items?page=${page}`,
fetcher,
{
fallbackData: initialData, // SSR data as initial cache
keepPreviousData: true, // Smooth transitions
revalidateOnMount: false, // Don't refetch on mount
revalidateOnFocus: false, // Don't refetch on focus
}
);
// ...
}
2. Server Response Updates (NOT Optimistic)
tsx
// ✅ CORRECT: Use server response to update cache
const updateItems = async (serverResponse: Item[]) => {
const currentData = data;
if (!currentData) return;
const responseMap = new Map(serverResponse.map(i => [i.id, i]));
const updatedList = currentData.items.map(item =>
responseMap.has(item.id) ? responseMap.get(item.id)! : item
);
await mutate(
{ ...currentData, items: updatedList },
{ revalidate: false }
);
};
// In action handler:
const { data: updatedItem } = await fetchClient.put(`/api/items/${id}`, payload);
await updateItems([updatedItem]); // Use server response!
tsx
// ❌ WRONG: Optimistic update
const updatedList = currentData.items.map(item =>
item.id === id ? { ...item, ...localChanges } : item // Don't do this!
);
3. Context Pattern for Actions
tsx
// context/{entity}-actions-context.tsx
"use client";
import { createContext, useContext, ReactNode } from "react";
interface ActionsContextType {
onToggleStatus: (id: string, isActive: boolean) => Promise<ActionResult>;
onUpdate: (id: string, data: UpdateData) => Promise<ActionResult>;
updateItems: (items: Item[]) => Promise<void>;
onRefresh: () => Promise<void>;
}
const ActionsContext = createContext<ActionsContextType | null>(null);
export function ActionsProvider({ children, actions }: Props) {
return (
<ActionsContext.Provider value={actions}>
{children}
</ActionsContext.Provider>
);
}
export function useTableActions() {
const context = useContext(ActionsContext);
if (!context) {
throw new Error("useTableActions must be used within ActionsProvider");
}
return context;
}
4. API Route Pattern
tsx
// app/api/setting/items/route.ts
import { NextRequest } from "next/server";
import { withAuth, backendGet, backendPost } from "@/lib/fetch/api-route-helper";
export async function GET(request: NextRequest) {
const params = request.nextUrl.searchParams.toString();
return withAuth(token => backendGet(`/setting/items/?${params}`, token));
}
export async function POST(request: NextRequest) {
const body = await request.json();
return withAuth(token => backendPost('/setting/items/', token, body));
}
Generation Order
When creating a new entity page, generate files in this order:
- •Types (types/{entity}.d.ts)
- •Server Actions (lib/actions/{entity}.actions.ts)
- •API Routes (app/api/setting/{entity}/...)
- •Context (app/(pages)/setting/{entity}/context/)
- •Components (app/(pages)/setting/{entity}/_components/)
- •Page (app/(pages)/setting/{entity}/page.tsx)
Quick Reference
SWR Configuration
- •
fallbackData- SSR data for instant render - •
keepPreviousData: true- Smooth pagination - •
revalidateOnMount: false- Trust SSR data - •
revalidateOnFocus: false- Reduce API calls
Key Files
- •
lib/fetch/client.ts- Client-side API calls - •
lib/fetch/server.ts- Server action API calls - •
lib/fetch/api-route-helper.ts- API route wrappers
References
See the references/ directory for detailed patterns:
- •
page-pattern.md- SSR page structure - •
table-pattern.md- SWR table component - •
context-pattern.md- Actions context - •
api-route-pattern.md- API routes - •
fetch-pattern.md- Fetch utilities