# Nextjs Template

> Next.js Template Skill

- Skill: `adelabdelgawad/nextjs-template` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add adelabdelgawad/nextjs-template`
- Raw SKILL.md: https://api.skillmd.com/api/skills/adelabdelgawad/nextjs-template/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: adelabdelgawad (https://skillmd.com/u/adelabdelgawad)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/adelabdelgawad/nextjs-template

---

# 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

```
┌─────────────────────────────────────────────────────────────┐
│                    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

```
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:

1. **Types** (types/{entity}.d.ts)
2. **Server Actions** (lib/actions/{entity}.actions.ts)
3. **API Routes** (app/api/setting/{entity}/...)
4. **Context** (app/(pages)/setting/{entity}/context/)
5. **Components** (app/(pages)/setting/{entity}/_components/)
6. **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

