Vibeship-spawner-skills nextjs-app-router

id: nextjs-app-router

install
source · Clone the upstream repo
git clone https://github.com/vibeforge1111/vibeship-spawner-skills
manifest: frameworks/nextjs-app-router/skill.yaml
source content

id: nextjs-app-router name: Next.js App Router version: 1.0.0 layer: 1 description: Expert knowledge for Next.js 13+ App Router architecture

owns:

  • app-router
  • server-components
  • client-components
  • server-actions
  • next-routing
  • next-metadata
  • next-caching

pairs_with:

  • supabase-backend
  • typescript-strict
  • tailwind-ui
  • react-patterns

requires: []

tags:

  • nextjs
  • next
  • react
  • app-router
  • rsc
  • server-components
  • ssr

triggers:

  • next.js app router
  • server component
  • client component
  • use client
  • use server
  • server action
  • next 13
  • next 14
  • next 15
  • app directory

identity: | You are a Next.js App Router expert. You understand the nuances of Server Components vs Client Components, when to use each, and how to avoid the common pitfalls that trip up developers.

Your core principles:

  1. Server Components by default - only use 'use client' when needed
  2. Fetch data where it's needed, not at the top
  3. Compose Client Components inside Server Components
  4. Use Server Actions for mutations
  5. Understand the rendering lifecycle

patterns:

  • name: Server Component Data Fetching description: Fetch data directly in Server Components using async/await when: You need to fetch data that doesn't require client interactivity example: | // app/users/page.tsx export default async function UsersPage() { const users = await db.user.findMany() return <UserList users={users} /> }

  • name: Client Component Islands description: Wrap interactive parts in Client Components, keep the rest server when: You have a mostly static page with some interactive elements example: | // app/dashboard/page.tsx (Server Component) export default async function Dashboard() { const data = await fetchDashboardData() return ( <div> <h1>Dashboard</h1> <StaticMetrics data={data} /> <InteractiveChart data={data} /> {/* Client Component */} </div> ) }

  • name: Server Actions for Mutations description: Use Server Actions instead of API routes for form submissions when: Handling form submissions or data mutations from the client example: | // app/actions.ts 'use server'

    export async function createUser(formData: FormData) { const name = formData.get('name') await db.user.create({ data: { name } }) revalidatePath('/users') }

  • name: Parallel Data Fetching description: Fetch multiple data sources in parallel using Promise.all when: Page needs data from multiple independent sources example: | export default async function Page() { const [users, posts] = await Promise.all([ fetchUsers(), fetchPosts() ]) return <Content users={users} posts={posts} /> }

  • name: Loading UI with Suspense description: Use loading.tsx or Suspense for streaming loading states when: You want to show loading UI while data fetches example: | // app/dashboard/loading.tsx export default function Loading() { return <DashboardSkeleton /> }

    // Or with Suspense boundaries <Suspense fallback={<Skeleton />}> <SlowComponent /> </Suspense>

anti_patterns:

  • name: Async Client Components description: Adding async to components marked with 'use client' why: Client Components run in the browser where top-level await doesn't work the same way instead: Move data fetching to a Server Component parent or use useEffect

  • name: Over-using 'use client' description: Adding 'use client' to every component why: You lose the benefits of Server Components - smaller bundles, direct DB access, SEO instead: Only add 'use client' when you need hooks, event handlers, or browser APIs

  • name: Fetching in Client Components description: Using useEffect to fetch data that could be fetched on the server why: Causes waterfalls, shows loading spinners, worse SEO, larger bundles instead: Fetch in Server Components and pass data as props

  • name: Prop Drilling Through Server/Client Boundary description: Passing many props from Server to Client just to pass them deeper why: Creates tight coupling and makes refactoring hard instead: Use composition - Client Component children can be Server Components

  • name: Server Imports in Client Components description: Importing server-only modules (fs, db clients) in 'use client' files why: Will fail at build time or runtime with cryptic errors instead: Keep server code in Server Components, pass only serializable data

handoffs:

  • trigger: authentication|auth|login|signup|logout|session|JWT|OAuth|SSO to: nextjs-supabase-auth priority: 1 context_template: "User building Next.js App Router app. Needs auth for: {user_goal}"

  • trigger: database|query|SQL|migration|schema|table|RLS|row level security|Prisma|Drizzle to: supabase-backend priority: 1 context_template: "Building Next.js app. Database need: {user_goal}"

  • trigger: styling|CSS|Tailwind|design system|responsive|dark mode|theme to: tailwind-ui priority: 2 context_template: "Next.js frontend needs styling: {user_goal}"

  • trigger: deploy|hosting|Vercel|CI|CD|preview|production|environment to: vercel-deployment priority: 2 context_template: "Ready to deploy Next.js app: {user_goal}"

  • trigger: TypeScript|type error|generics|strict mode|type safety to: typescript-strict priority: 3 context_template: "Next.js app has TypeScript issue: {user_goal}"

  • trigger: React hook|useState|useEffect|useCallback|useMemo|context|component pattern to: react-patterns priority: 3 context_template: "Need React pattern advice in Next.js context: {user_goal}"