Назад към всички

nextjs-app-router

// Deep expertise in Next.js App Router patterns including route groups, parallel routes, intercepting routes, layouts, and loading states.

$ git log --oneline --stat
stars:384
forks:73
updated:March 4, 2026
SKILL.mdreadonly
SKILL.md Frontmatter
namenextjs-app-router
descriptionDeep expertise in Next.js App Router patterns including route groups, parallel routes, intercepting routes, layouts, and loading states.
allowed-toolsRead, Write, Edit, Bash, Glob, Grep

Next.js App Router Skill

Expert assistance for implementing Next.js App Router with advanced routing patterns and conventions.

Capabilities

  • Design app directory structure with route groups
  • Implement parallel and intercepting routes
  • Configure layouts, templates, and loading states
  • Set up error boundaries and not-found pages
  • Implement streaming with Suspense
  • Configure route handlers for API endpoints

Usage

Invoke this skill when you need to:

  • Structure app directory for complex routing
  • Implement modal routes with interception
  • Create parallel route layouts
  • Configure streaming and loading states
  • Build API route handlers

Inputs

ParameterTypeRequiredDescription
routeTypestringYespage, layout, route, loading, error
routePathstringYesRoute path in app directory
featuresarrayNoparallel, intercept, group
streamingbooleanNoEnable streaming with Suspense

Configuration Example

{
  "routeType": "page",
  "routePath": "/dashboard/analytics",
  "features": ["parallel", "loading"],
  "streaming": true
}

Route Structure Patterns

Route Groups

app/
├── (auth)/
│   ├── login/
│   │   └── page.tsx
│   ├── register/
│   │   └── page.tsx
│   └── layout.tsx          # Auth-specific layout
├── (dashboard)/
│   ├── analytics/
│   │   └── page.tsx
│   ├── settings/
│   │   └── page.tsx
│   └── layout.tsx          # Dashboard layout with sidebar
├── (marketing)/
│   ├── about/
│   │   └── page.tsx
│   ├── pricing/
│   │   └── page.tsx
│   └── layout.tsx          # Marketing layout
└── layout.tsx              # Root layout

Parallel Routes

app/
├── @modal/
│   ├── (.)photos/[id]/
│   │   └── page.tsx        # Modal view
│   └── default.tsx
├── @sidebar/
│   ├── default.tsx
│   └── feed/
│       └── page.tsx
├── photos/
│   └── [id]/
│       └── page.tsx        # Full page view
├── layout.tsx
└── page.tsx
// app/layout.tsx
export default function Layout({
  children,
  modal,
  sidebar,
}: {
  children: React.ReactNode;
  modal: React.ReactNode;
  sidebar: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <div className="flex">
          <aside>{sidebar}</aside>
          <main>{children}</main>
        </div>
        {modal}
      </body>
    </html>
  );
}

Intercepting Routes

app/
├── @modal/
│   └── (.)photos/[id]/     # Intercept from same level
│       └── page.tsx
├── feed/
│   └── @modal/
│       └── (..)photos/[id]/ # Intercept from parent
│           └── page.tsx
└── photos/
    └── [id]/
        └── page.tsx
// app/@modal/(.)photos/[id]/page.tsx
import { Modal } from '@/components/modal';
import Photo from '@/components/photo';

export default function PhotoModal({
  params: { id },
}: {
  params: { id: string };
}) {
  return (
    <Modal>
      <Photo id={id} />
    </Modal>
  );
}

Loading and Error States

// app/dashboard/loading.tsx
export default function DashboardLoading() {
  return (
    <div className="animate-pulse">
      <div className="h-8 bg-gray-200 rounded w-1/4 mb-4" />
      <div className="h-64 bg-gray-200 rounded" />
    </div>
  );
}

// app/dashboard/error.tsx
'use client';

export default function DashboardError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div className="p-4 bg-red-50 rounded">
      <h2 className="text-red-800 font-bold">Something went wrong!</h2>
      <p className="text-red-600">{error.message}</p>
      <button
        onClick={reset}
        className="mt-2 px-4 py-2 bg-red-600 text-white rounded"
      >
        Try again
      </button>
    </div>
  );
}

Route Handlers

// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams;
  const query = searchParams.get('query');

  const users = await db.user.findMany({
    where: query ? { name: { contains: query } } : undefined,
  });

  return NextResponse.json(users);
}

export async function POST(request: NextRequest) {
  const body = await request.json();

  const user = await db.user.create({
    data: body,
  });

  return NextResponse.json(user, { status: 201 });
}

// app/api/users/[id]/route.ts
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const user = await db.user.findUnique({
    where: { id: params.id },
  });

  if (!user) {
    return NextResponse.json(
      { error: 'User not found' },
      { status: 404 }
    );
  }

  return NextResponse.json(user);
}

Streaming with Suspense

// app/dashboard/page.tsx
import { Suspense } from 'react';
import { Analytics, RecentActivity, Stats } from '@/components';
import { StatsSkeleton, ActivitySkeleton } from '@/components/skeletons';

export default function Dashboard() {
  return (
    <div className="grid gap-4">
      <Suspense fallback={<StatsSkeleton />}>
        <Stats />
      </Suspense>

      <div className="grid grid-cols-2 gap-4">
        <Suspense fallback={<ActivitySkeleton />}>
          <RecentActivity />
        </Suspense>

        <Suspense fallback={<div>Loading analytics...</div>}>
          <Analytics />
        </Suspense>
      </div>
    </div>
  );
}

Best Practices

  • Use route groups to organize without affecting URL
  • Implement loading.tsx for immediate feedback
  • Use error.tsx for graceful error handling
  • Leverage parallel routes for complex layouts
  • Use intercepting routes for modal patterns

Target Processes

  • nextjs-full-stack
  • frontend-architecture-design
  • app-router-migration
  • performance-optimization