Claude Skill

nextjs-app-router

Next.js App Router best practices, Server Components, Server Actions, routing patterns, and data fetching strategies. Use when building Next.js applications with the App Router.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download sabahattink-antigravity-fullstack-hq-skills_nextjs-app-router-1acbfa7.zip · 1 KB
Part of sabahattink/antigravity-fullstack-hq — 27 skills

Install

skills CLI npx skills add https://github.com/sabahattink/antigravity-fullstack-hq/tree/main/skills/nextjs-app-router
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sabahattink-antigravity-fullstack-hq@llmmart
Git git clone https://github.com/sabahattink/antigravity-fullstack-hq.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole sabahattink/antigravity-fullstack-hq collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Next.js App Router Patterns

Project Structure

app/
├── (auth)/                 # Route Group
│   ├── login/page.tsx
│   ├── register/page.tsx
│   └── layout.tsx
├── (dashboard)/
│   ├── layout.tsx
│   ├── page.tsx
│   └── [projectId]/
│       └── page.tsx
├── api/
│   └── webhooks/route.ts
├── layout.tsx
├── page.tsx
├── loading.tsx
├── error.tsx
└── not-found.tsx

Server vs Client Components

Decision Tree

  • Need interactivity (onClick, useState)? -> 'use client'
  • Need browser APIs? -> 'use client'
  • Otherwise -> Server Component (default)

Server Component

// No directive needed - Server Component by default
import { prisma } from '@/lib/db'

export default async function UsersPage() {
  const users = await prisma.user.findMany()
  return <UserList users={users} />
}

Client Component

'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}

Server Actions

// lib/actions/users.ts
'use server'

import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createUser(formData: FormData) {
  const email = formData.get('email') as string
  
  await prisma.user.create({ data: { email } })
  
  revalidatePath('/users')
  redirect('/users')
}

Using in Forms

import { createUser } from '@/lib/actions/users'

export function CreateUserForm() {
  return (
    <form action={createUser}>
      <input name="email" type="email" required />
      <button type="submit">Create</button>
    </form>
  )
}

Data Fetching

Parallel Fetching

export default async function Dashboard() {
  const [user, posts] = await Promise.all([
    getUser(),
    getPosts()
  ])
  
  return <DashboardView user={user} posts={posts} />
}

Streaming with Suspense

import { Suspense } from 'react'

export default function Page() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<Loading />}>
        <SlowComponent />
      </Suspense>
    </div>
  )
}

Caching

// Revalidate every 60 seconds
fetch(url, { next: { revalidate: 60 } })

// No caching
fetch(url, { cache: 'no-store' })

// Static (default)
fetch(url)

Protected Routes

// app/(dashboard)/layout.tsx
import { redirect } from 'next/navigation'
import { auth } from '@/lib/auth'

export default async function DashboardLayout({ children }) {
  const session = await auth()
  if (!session) redirect('/login')
  
  return <div>{children}</div>
}
Files (antigravity-fullstack-hq)
  • SKILL.md 2.9 KB
    ---
    name: nextjs-app-router
    description: Next.js App Router best practices, Server Components, Server Actions, routing patterns, and data fetching strategies. Use when building Next.js applications with the App Router.
    ---
    
    # Next.js App Router Patterns
    
    ## Project Structure
    
    ```
    app/
    ├── (auth)/                 # Route Group
    │   ├── login/page.tsx
    │   ├── register/page.tsx
    │   └── layout.tsx
    ├── (dashboard)/
    │   ├── layout.tsx
    │   ├── page.tsx
    │   └── [projectId]/
    │       └── page.tsx
    ├── api/
    │   └── webhooks/route.ts
    ├── layout.tsx
    ├── page.tsx
    ├── loading.tsx
    ├── error.tsx
    └── not-found.tsx
    ```
    
    ## Server vs Client Components
    
    ### Decision Tree
    
    - Need interactivity (onClick, useState)? -> 'use client'
    - Need browser APIs? -> 'use client'
    - Otherwise -> Server Component (default)
    
    ### Server Component
    
    ```tsx
    // No directive needed - Server Component by default
    import { prisma } from '@/lib/db'
    
    export default async function UsersPage() {
      const users = await prisma.user.findMany()
      return <UserList users={users} />
    }
    ```
    
    ### Client Component
    
    ```tsx
    'use client'
    
    import { useState } from 'react'
    
    export function Counter() {
      const [count, setCount] = useState(0)
      return <button onClick={() => setCount(c => c + 1)}>{count}</button>
    }
    ```
    
    ## Server Actions
    
    ```tsx
    // lib/actions/users.ts
    'use server'
    
    import { revalidatePath } from 'next/cache'
    import { redirect } from 'next/navigation'
    
    export async function createUser(formData: FormData) {
      const email = formData.get('email') as string
      
      await prisma.user.create({ data: { email } })
      
      revalidatePath('/users')
      redirect('/users')
    }
    ```
    
    ### Using in Forms
    
    ```tsx
    import { createUser } from '@/lib/actions/users'
    
    export function CreateUserForm() {
      return (
        <form action={createUser}>
          <input name="email" type="email" required />
          <button type="submit">Create</button>
        </form>
      )
    }
    ```
    
    ## Data Fetching
    
    ### Parallel Fetching
    
    ```tsx
    export default async function Dashboard() {
      const [user, posts] = await Promise.all([
        getUser(),
        getPosts()
      ])
      
      return <DashboardView user={user} posts={posts} />
    }
    ```
    
    ### Streaming with Suspense
    
    ```tsx
    import { Suspense } from 'react'
    
    export default function Page() {
      return (
        <div>
          <h1>Dashboard</h1>
          <Suspense fallback={<Loading />}>
            <SlowComponent />
          </Suspense>
        </div>
      )
    }
    ```
    
    ## Caching
    
    ```tsx
    // Revalidate every 60 seconds
    fetch(url, { next: { revalidate: 60 } })
    
    // No caching
    fetch(url, { cache: 'no-store' })
    
    // Static (default)
    fetch(url)
    ```
    
    ## Protected Routes
    
    ```tsx
    // app/(dashboard)/layout.tsx
    import { redirect } from 'next/navigation'
    import { auth } from '@/lib/auth'
    
    export default async function DashboardLayout({ children }) {
      const session = await auth()
      if (!session) redirect('/login')
      
      return <div>{children}</div>
    }
    ```
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related