SCALE — Build Lab
開発パターン · NEXT.JS PATTERN

Next.js App Router ダッシュボード雛形

CATEGORY開発パターン TYPENext.js Pattern EFFORT120〜360分 DIFFICULTY
PRIMARY CODE
tsx
// ファイル構成:
//   app/
//     (dashboard)/         ← route group(URLには現れない)
//       layout.tsx         ← 全ダッシュボード共通レイアウト
//       home/page.tsx
//       tasks/page.tsx
//     login/page.tsx       ← (dashboard) 外なのでヘッダー不要
//   middleware.ts          ← 認証チェック

// === app/(dashboard)/layout.tsx ===
import { ReactNode } from 'react';
import { Header } from '@/components/layout/Header';
import { Sidebar } from '@/components/layout/Sidebar';

export default function DashboardLayout({ children }: { children: ReactNode }) {
  return (
    <div className="min-h-screen bg-zinc-950 text-zinc-100">
      <Header />
      <div className="flex">
        <Sidebar />
        <main className="flex-1 p-6 overflow-x-auto">{children}</main>
      </div>
    </div>
  );
}

// === middleware.ts ===
import { NextRequest, NextResponse } from 'next/server';

export function middleware(req: NextRequest) {
  const isAuthed = req.cookies.get('session')?.value;
  const isLoginPage = req.nextUrl.pathname === '/login';

  if (!isAuthed && !isLoginPage) {
    return NextResponse.redirect(new URL('/login', req.url));
  }
  if (isAuthed && isLoginPage) {
    return NextResponse.redirect(new URL('/home', req.url));
  }
  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|api).*)'],
};
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 全ダッシュボード系プロジェクト

Next.js App Router ダッシュボード雛形

:LiTarget: 用途

Next.js App Router でダッシュボードレイアウトを組む標準雛形。route group 活用。

:LiSparkle: 特徴

  • (dashboard) route group
  • 共通レイアウト
  • 認証ミドルウェア

:LiCode: コード(コピペ用)

// ファイル構成:
//   app/
//     (dashboard)/         ← route group(URLには現れない)
//       layout.tsx         ← 全ダッシュボード共通レイアウト
//       home/page.tsx
//       tasks/page.tsx
//     login/page.tsx       ← (dashboard) 外なのでヘッダー不要
//   middleware.ts          ← 認証チェック

// === app/(dashboard)/layout.tsx ===
import { ReactNode } from 'react';
import { Header } from '@/components/layout/Header';
import { Sidebar } from '@/components/layout/Sidebar';

export default function DashboardLayout({ children }: { children: ReactNode }) {
  return (
    <div className="min-h-screen bg-zinc-950 text-zinc-100">
      <Header />
      <div className="flex">
        <Sidebar />
        <main className="flex-1 p-6 overflow-x-auto">{children}</main>
      </div>
    </div>
  );
}

// === middleware.ts ===
import { NextRequest, NextResponse } from 'next/server';

export function middleware(req: NextRequest) {
  const isAuthed = req.cookies.get('session')?.value;
  const isLoginPage = req.nextUrl.pathname === '/login';

  if (!isAuthed && !isLoginPage) {
    return NextResponse.redirect(new URL('/login', req.url));
  }
  if (isAuthed && isLoginPage) {
    return NextResponse.redirect(new URL('/home', req.url));
  }
  return NextResponse.next();
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|api).*)'],
};

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加