N.N. LLC. ロゴ - 千葉県船橋市のIT企業N.N. LLC.
技術ブログ

Next.js 15でコーポレートサイトを構築する実践ガイド

17分で読めます
2回閲覧
Next.js 15でコーポレートサイトを構築する実践ガイド

はじめに

N.N. LLC.のコーポレートサイト(n-n.tokyo)は、Next.js 15.5とTypeScriptで構築されています。React 19のServer Componentsをフル活用し、SEOとパフォーマンスを高いレベルで両立させています。

本記事では、実際のプロジェクトで得た知見をもとに、Next.js 15を使ったコーポレートサイト構築のベストプラクティスを包括的に解説します。これから新規でコーポレートサイトを作る方にも、既存サイトをNext.jsへ移行する方にも役立つ内容です。

Next.js 15を選ぶ理由

コーポレートサイトに求められる要件は多岐にわたります。SEO、表示速度、保守性、アクセシビリティ、そして将来の拡張性。Next.js 15はこれらすべてを高いレベルで満たすフレームワークです。

App Routerの成熟

Next.js 13で導入されたApp Routerは、15系で安定版となりました。ファイルベースのルーティングにより、ディレクトリ構造がそのままURLパスに対応するため、サイトマップの設計が直感的に行えます。

  • レイアウトの共有: layout.tsxでヘッダー・フッターを一度定義するだけで、配下のすべてのページに適用される
  • ルートグループ: (marketing)のようにカッコで囲むことで、URLに影響を与えずにレイアウトをグループ化できる
  • ローディングUI: loading.tsxでページ遷移時のローディング状態を宣言的に定義
  • エラーハンドリング: error.tsxでエラー発生時のフォールバックUIを設定

React Server Components(RSC)

App Routerの最大の特徴は、デフォルトですべてのコンポーネントがServer Componentとして動作する点です。これにより、以下のメリットが得られます。

  • クライアントに送信されるJavaScriptバンドルサイズの大幅削減
  • サーバーサイドでのデータフェッチにより、APIキーなどの機密情報がクライアントに露出しない
  • SEOに最適なHTMLをサーバー側で生成
// Server Component(デフォルト)
// このコンポーネントはサーバーでのみ実行される
import { createClient } from "@/lib/supabase/server"

export default async function ServicesPage() {
  // サーバーサイドでデータを直接取得
  const supabase = await createClient()
  const { data: services } = await supabase
    .from("services")
    .select("*")
    .eq("is_active", true)
    .order("sort_order")

  return (
    <section>
      {services?.map((service) => (
        <ServiceCard key={service.id} service={service} />
      ))}
    </section>
  )
}

Partial Prerendering(PPR)

Next.js 15で実験的に導入されたPartial Prerenderingは、1つのページ内で静的な部分と動的な部分を混在させる機能です。コーポレートサイトでは、ヘッダー・フッター・サービス紹介などの静的部分はビルド時に生成し、お問い合わせフォームの送信状況やブログの最新記事などの動的部分はリクエスト時に生成する、というハイブリッドなアプローチが可能になります。

プロジェクトの技術構成

主要な技術スタック

  • フレームワーク: Next.js 15.5.9
  • 言語: TypeScript 5.x(strict mode有効)
  • スタイリング: Tailwind CSS 4.x
  • UIコンポーネント: shadcn/ui(Radix UIベース)
  • バックエンド: Supabase(PostgreSQL + RLS)
  • ホスティング: Vercel(東京リージョン)
  • アイコン: Lucide React

ディレクトリ構成

実際のプロジェクトのディレクトリ構成です。App Routerの規約に沿いつつ、コンポーネントやユーティリティを整理しています。

web/
├── app/
│   ├── (marketing)/        # マーケティングページ群
│   │   ├── page.tsx        # トップページ
│   │   ├── about/          # 会社概要
│   │   ├── services/       # サービス紹介
│   │   │   └── [slug]/     # 各サービスの詳細
│   │   ├── works/          # 実績
│   │   └── pricing/        # 料金プラン
│   ├── blog/               # ブログ
│   │   └── [slug]/         # 記事詳細(動的ルート)
│   ├── contact/            # お問い合わせ
│   ├── admin/              # 管理画面
│   │   └── (dashboard)/    # ダッシュボード
│   ├── layout.tsx          # ルートレイアウト
│   └── globals.css         # グローバルCSS
├── components/
│   ├── ui/                 # shadcn/uiコンポーネント
│   ├── adsense.tsx         # AdSense広告コンポーネント
│   └── ...                 # その他の共通コンポーネント
├── lib/
│   ├── supabase/           # Supabaseクライアント設定
│   │   ├── client.ts       # ブラウザ用クライアント
│   │   └── server.ts       # サーバー用クライアント
│   └── utils.ts            # ユーティリティ関数
└── public/                 # 静的ファイル

Metadata APIによるSEO最適化

静的メタデータ

変更頻度の低いページでは、metadataオブジェクトをexportする静的な方法でメタデータを定義します。

// app/(marketing)/about/page.tsx
import type { Metadata } from "next"

export const metadata: Metadata = {
  title: "会社概要",
  description: "合同会社N.N.の会社概要。千葉県船橋市を拠点に...",
  openGraph: {
    title: "会社概要 | N.N. LLC.",
    description: "合同会社N.N.の会社概要。千葉県船橋市を拠点に...",
    type: "website",
  },
}

動的メタデータ

ブログ記事のように動的なコンテンツのページでは、generateMetadata関数を使ってDBから取得したデータに基づくメタデータを生成します。

// app/blog/[slug]/page.tsx
export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>
}): Promise<Metadata> {
  const { slug } = await params
  const supabase = await createClient()
  const { data: post } = await supabase
    .from("blog_posts")
    .select("title, excerpt, meta_title, meta_description")
    .eq("slug", slug)
    .eq("status", "published")
    .single()

  return {
    title: post?.meta_title || post?.title,
    description: post?.meta_description || post?.excerpt,
  }
}

JSON-LD構造化データ

Googleのリッチスニペット表示を狙い、Organization、WebSite、BreadcrumbList、BlogPostingなどのJSON-LD構造化データを実装しています。Next.jsの<Script>コンポーネントでhead内に出力します。

// Organization構造化データの例
const organizationJsonLd = {
  "@context": "https://schema.org",
  "@type": "Organization",
  name: "合同会社N.N.",
  url: "https://n-n.tokyo",
  logo: "https://n-n.tokyo/og-image.png",
  address: {
    "@type": "PostalAddress",
    addressRegion: "千葉県",
    addressLocality: "船橋市",
    addressCountry: "JP",
  },
  sameAs: [],
}

Vercelへのデプロイと東京リージョン配信

デプロイ先にはVercelを選択しました。Next.jsの開発元であるVercel社のプラットフォームであるため、最新機能への対応が最速で、設定の手間もほぼゼロです。

東京リージョン(hnd1)の活用

VercelのServerless Functionsは東京リージョン(hnd1)にデプロイ可能です。SSRやAPIルートの処理が日本国内のサーバーで実行されるため、日本のユーザーに対するレスポンスタイムが大幅に短縮されます。

// vercel.json
{
  "regions": ["hnd1"]
}

Edge Networkによるグローバル配信

静的アセット(CSS、JavaScript、画像)はVercelのEdge Networkを通じてグローバルに配信されます。CDNのキャッシュにより、世界中のどこからアクセスしても高速な読み込みを実現しています。

ISR(Incremental Static Regeneration)の活用

ブログ一覧ページではISRを活用しています。初回アクセス時に静的ページを生成し、一定時間(revalidate)経過後のアクセスでバックグラウンドでページを再生成する仕組みです。

// ブログ一覧ページでのISR設定(60秒間隔で再生成)
export const revalidate = 60

これにより、新しい記事を公開しても、最大60秒後にはブログ一覧に反映されます。ユーザーには常に静的HTMLが返却されるため、表示速度は常に高速です。

パフォーマンス最適化のベストプラクティス

画像最適化

Next.jsのnext/imageコンポーネントは、画像の自動最適化機能を提供します。WebP/AVIFフォーマットへの自動変換、レスポンシブサイズの生成、遅延読み込みが標準で有効です。

import Image from "next/image"

// 最適化された画像コンポーネント
<Image
  src="/hero-image.png"
  alt="N.N. LLC.のサービス"
  width={1200}
  height={630}
  priority  // LCPに影響するファーストビュー画像
  className="rounded-xl"
/>

フォント最適化

next/fontでGoogleフォントを最適化して読み込みます。サブセット化とフォントファイルのセルフホスティングにより、外部リクエストを削減し、CLSを防止します。

import { Noto_Sans_JP } from "next/font/google"

const notoSansJP = Noto_Sans_JP({
  subsets: ["latin"],
  weight: ["400", "500", "700"],
  display: "swap",   // フォント読み込み中もテキストを表示
  preload: true,
})

バンドルサイズの管理

Client Componentの使用を必要最小限に抑え、JavaScriptバンドルサイズを管理します。next build時に出力されるバンドル分析レポートを定期的にチェックし、不要な依存関係を削除しています。

  • インタラクション不要なページはすべてServer Component
  • "use client"ディレクティブは、フォーム操作やクリックイベントが必要なコンポーネントにのみ付与
  • 動的インポート(next/dynamic)で、ファーストビューに不要なコンポーネントを遅延読み込み

Tailwind CSS + shadcn/uiによるUI設計

UIコンポーネントにはshadcn/uiを採用しました。shadcn/uiはRadix UIをベースとしたアクセシブルなコンポーネントライブラリで、ソースコードがプロジェクトに直接コピーされるため、完全なカスタマイズが可能です。

  • Button, Card, Badge, Dialog: 頻繁に使用するUIパーツは統一されたデザインシステムで管理
  • ダークテーマ: bg-gradient-to-br from-gray-900 to-blackをベースにしたダークテーマで統一
  • アニメーション: hover-liftanimate-fade-inなどのカスタムCSSクラスで、控えめなインタラクションを実現

実際のLighthouseスコア

これらの最適化の結果、n-n.tokyoのLighthouseスコアは以下の通りです。

  • Performance: 95+
  • Accessibility: 92+
  • Best Practices: 95+
  • SEO: 100

特にSEOスコアは満点を達成しており、メタデータ、構造化データ、サイトマップ、robots.txtの設定が適切に機能していることを示しています。

まとめ

Next.js 15は、コーポレートサイト構築に必要な機能を網羅的に提供してくれるフレームワークです。特にApp RouterとReact Server Componentsの組み合わせにより、SEOとパフォーマンスを高いレベルで両立できる点が最大の強みです。

TypeScript + Tailwind CSS + shadcn/uiによるUI設計、Supabaseとの連携、Vercel東京リージョンでの配信と合わせることで、日本のユーザーに最適なコーポレートサイトを構築できます。Next.jsでのサイト構築を検討されている方は、ぜひ参考にしてください。

Next.js
TypeScript
SEO
Vercel
コーポレートサイト

関連記事