Nuxt のページ遷移を極める:ファイルベースルーティング攻略法

Nuxt.js の最大の魅力の一つが、ファイルベースルーティングです。従来の Vue Router のように手動でルートを定義する必要がなく、ファイルシステムの構造がそのまま URL 構造になるという革命的な仕組みです。
この記事では、Nuxt のページ遷移を完全に理解し、実践的なプロジェクトで活用できるようになることを目指します。初心者の方でも安心して読み進められるよう、基礎から応用まで段階的に解説していきます。
ファイルベースルーティングをマスターすれば、開発効率が劇的に向上し、保守性の高いアプリケーションを構築できるようになります。一緒に Nuxt の世界を深く探求していきましょう。
ファイルベースルーティングの基本概念
Nuxt のディレクトリ構造とルーティングの関係
Nuxt では、pagesディレクトリ内のファイル構造が自動的にルーティング構造に変換されます。これがファイルベースルーティングの核心です。
まず、基本的なディレクトリ構造を確認してみましょう:
bashpages/
├── index.vue          → /
├── about.vue          → /about
├── users/
│   ├── index.vue      → /users
│   └── [id].vue       → /users/123
└── blog/
    ├── index.vue      → /blog
    └── [slug].vue     → /blog/my-first-post
この構造を見ると、ファイル名がそのまま URL パスになることがわかります。index.vueは特別な意味を持ち、ディレクトリのデフォルトページとして機能します。
実際にプロジェクトを作成して確認してみましょう:
bash# Nuxtプロジェクトの作成
$ yarn create nuxt-app my-nuxt-app
$ cd my-nuxt-app
$ yarn dev
pages ディレクトリの役割と命名規則
pagesディレクトリは、Nuxt アプリケーションの心臓部です。ここに配置されたファイルは自動的にルートとして認識され、Vue コンポーネントとしてレンダリングされます。
基本的な命名規則を理解しましょう:
静的ルート
- about.vue→- /about
- contact.vue→- /contact
- products.vue→- /products
動的ルート
- [id].vue→- /123/456` など
- [slug].vue→- /my-post,- /another-postなど
ネストしたルート
- users/index.vue→- /users
- users/[id].vue→- /users/123
実際のコード例を見てみましょう:
vue<!-- pages/index.vue -->
<template>
  <div>
    <h1>ホームページ</h1>
    <p>ようこそ、Nuxtの世界へ!</p>
  </div>
</template>
<script setup>
// ページのメタデータを設定
useHead([object Object] title:ホームページ,meta: [
    { name: 'description, content:Nuxtアプリケーションのホームページです' }
  ]
})
</script>
基本的なページ遷移の実装
静的ルートの作成方法
静的ルートは、最もシンプルなページ遷移の実装方法です。固定の URL でアクセスできるページを作成します。
まず、基本的な静的ページを作成してみましょう:
vue<!-- pages/about.vue -->
<template>
  <div class="about-page>
    <h1私たちについて</h1>
    <p>このページは静的ルートの例です。</p>
    <NuxtLink to="/>ホームに戻る</NuxtLink>
  </div>
</template>
<script setup>
// ページ固有のロジック
const pageTitle = ref(私たちについて')
// SEO対策
useHead({
  title: pageTitle.value,
  meta: [
    { name: 'description', content: '私たちの会社について詳しく紹介します' }
  ]
})
</script>
よくあるエラーと解決策
bash# エラー: Cannot find module ./pages/about.vue# 原因: ファイル名の大文字小文字が間違っている
# 解決策: ファイル名を正確に確認する
# エラー: [nuxt] [request error] Cannot read properties of undefined
# 原因: script setupでrefをimportしていない
# 解決策: import [object Object] ref } from vueを追加
動的ルート(パラメータ付き)の実装
動的ルートは、URL パラメータを受け取って動的にコンテンツを表示する仕組みです。ブログ記事や商品詳細ページなどで活用されます。
vue<!-- pages/users/[id].vue -->
<template>
  <div class=user-detail>    <h1>ユーザー詳細: [object Object][object Object] user?.name }}</h1<div v-if="pending">読み込み中...</div>
    <div v-else-if=error">エラーが発生しました</div>
    <div v-else>
      <p>ID: {{ $route.params.id }}</p>
      <p>名前: [object Object][object Object]user?.name }}</p>
      <p>メール: {[object Object] user?.email }}</p>
    </div>
  </div>
</template>
<script setup>
// ルートパラメータを取得
const route = useRoute()
const userId = route.params.id
// ユーザーデータを取得
const { data: user, pending, error } = await useFetch(`/api/users/${userId}`)
// エラーハンドリング
if (error.value) {
  throw createError([object Object]   statusCode: 404,
    statusMessage: 'ユーザーが見つかりません'
  })
}
</script>
動的ルートでよくあるエラー
bash# エラー: [nuxt] [request error] Cannot read properties of undefined (reading 'params')
# 原因: useRoute()をscript setupの外で呼び出している
# 解決策: script setup内でuseRoute()を呼び出す
# エラー: [nuxt] [request error] Cannot read properties of undefined (readingid# 原因: route.paramsがundefined
# 解決策: route.paramsの存在確認を追加
ネストしたルートの構築
ネストしたルートは、階層構造を持つページを作成する際に使用します。親子関係のあるコンテンツを整理できます。
vue<!-- pages/blog/index.vue -->
<template>
  <div class=blog-list>
    <h1ログ一覧</h1>
    <div class="posts">
      <article v-for="post in posts" :key="post.id" class=post">
        <h2>[object Object][object Object] post.title }}</h2>
        <p>{{ post.excerpt }}</p>
        <NuxtLink :to="`/blog/$[object Object]post.slug}`">続きを読む</NuxtLink>
      </article>
    </div>
  </div>
</template>
<script setup>
// ブログ記事一覧を取得
const { data: posts } = await useFetch('/api/posts)</script>
vue<!-- pages/blog/[slug].vue -->
<template>
  <div class=blog-post>
    <h1>{[object Object] post?.title }}</h1>
    <div class="meta>
      <span>投稿日: {{ formatDate(post?.createdAt) }}</span>
    </div>
    <div class=content" v-html=post?.content></div>
    <NuxtLink to=/blog>←ブログ一覧に戻る</NuxtLink>
  </div>
</template>
<script setup>
const route = useRoute()
const slug = route.params.slug
// 記事データを取得
const { data: post } = await useFetch(`/api/posts/${slug}`)
// 日付フォーマット関数
const formatDate = (date) => {
  return new Date(date).toLocaleDateString('ja-JP')
}
</script>
高度なページ遷移テクニック
ミドルウェアを使った遷移制御
ミドルウェアは、ページ遷移の前後に実行される処理を定義できます。認証チェックやログ出力などに活用されます。
javascript// middleware/auth.js
export default defineNuxtRouteMiddleware((to, from) =>[object Object]
  // ユーザーの認証状態をチェック
  const user = useUser()
  if (!user.value && to.path.startsWith('/admin')) [object Object]   // 未認証の場合はログインページにリダイレクト
    return navigateTo(/login')
  }
  // ログ出力
  console.log(`ページ遷移: ${from.path} → ${to.path}`)
})
ページレベルでミドルウェアを適用する場合:
vue<!-- pages/admin/dashboard.vue -->
<template>
  <div class="admin-dashboard>
    <h1理者ダッシュボード</h1>
    <p>認証済みユーザーのみアクセス可能</p>
  </div>
</template>
<script setup>
// このページにミドルウェアを適用
definePageMeta([object Object]middleware: ['auth']
})
</script>
ミドルウェアでよくあるエラー
bash# エラー: [nuxt] [request error] Cannot read properties of undefined (reading value)
# 原因: useUser()が未定義
# 解決策: composableを正しく定義する
# エラー: [nuxt] [request error] navigateTo is not defined
# 原因: navigateToをimportしていない
# 解決策: import { navigateTo } from #app を追加
ページ遷移のアニメーション実装
Nuxt では、ページ遷移時にアニメーションを追加できます。ユーザー体験を向上させる重要な要素です。
vue<!-- app.vue -->
<template>
  <div>
    <NuxtPage />
  </div>
</template>
<style>
/* ページ遷移アニメーション */
.page-enter-active,
.page-leave-active {
  transition: all 0.3se;
}
.page-enter-from {
  opacity: 0;
  transform: translateX(20x);
}
.page-leave-to {
  opacity: 0;
  transform: translateX(-20;
}
</style>
特定のページでアニメーションをカスタマイズする場合:
vue<!-- pages/about.vue -->
<template>
  <div class="about-page>
    <h1私たちについて</h1>
    <p>カスタムアニメーション付きページ</p>
  </div>
</template>
<script setup>
// ページ固有のアニメーション設定
definePageMeta([object Object]
  pageTransition: [object Object]    name:slide-fade',
    mode: out-in'
  }
})
</script>
<style scoped>
.slide-fade-enter-active,
.slide-fade-leave-active {
  transition: all 0.3 ease;
}
.slide-fade-enter-from {
  opacity: 0;
  transform: translateY(30x);
}
.slide-fade-leave-to {
  opacity: 0;
  transform: translateY(-30;
}
</style>
プログラムによる遷移制御
JavaScript からプログラム的にページ遷移を制御する方法を学びましょう。
vue<!-- pages/contact.vue -->
<template>
  <div class="contact-form">
    <h1>お問い合わせ</h1>
    <form @submit.prevent="handleSubmit">
      <input v-model=form.name" type=text placeholder="お名前" required>
      <input v-model="form.email" type="emailplaceholder=メールアドレス" required>
      <textarea v-model="form.messageplaceholder="メッセージ" required></textarea>
      <button type=submit :disabled="isSubmitting">
       [object Object][object Object] isSubmitting ?送信中...' : 送信する }}
      </button>
    </form>
  </div>
</template>
<script setup>
const router = useRouter()
const form = ref({
  name: ail:
  message:const isSubmitting = ref(false)
const handleSubmit = async () => {
  isSubmitting.value = true
  try [object Object]   // フォーム送信処理
    await $fetch(/api/contact,[object Object]     method: POST',
      body: form.value
    })
    // 成功時の遷移
    await navigateTo('/thank-you, {
      replace: true
    })
  } catch (error) {
    console.error('送信エラー:, error)
    // エラー時の処理
  } finally [object Object]isSubmitting.value = false
  }
}
</script>
プログラム遷移でよくあるエラー
bash# エラー: [nuxt] [request error] navigateTo is not a function
# 原因: navigateToを正しくimportしていない
# 解決策: import { navigateTo } from #app' を追加
# エラー: [nuxt] [request error] Cannot read properties of undefined (readingpush)
# 原因: useRouter()の使用方法が間違っている
# 解決策: const router = useRouter() で正しく取得
ファイルベースルーティングの応用パターン
レイアウトを使った共通 UI の実装
Nuxt のレイアウト機能を使って、共通の UI 要素を効率的に管理できます。
vue<!-- layouts/default.vue -->
<template>
  <div class="layout">
    <header class="header">
      <nav>
        <NuxtLink to="/">ホーム</NuxtLink>
        <NuxtLink to=/about">私たちについて</NuxtLink>
        <NuxtLink to="/blog">ブログ</NuxtLink>
        <NuxtLink to="/contact>お問い合わせ</NuxtLink>
      </nav>
    </header>
    <main class="main>      <slot />
    </main>
    <footer class=footer>
      <p>©2024xt App. All rights reserved.</p>
    </footer>
  </div>
</template>
<style scoped>
.layout {
  min-height:100h;
  display: flex;
  flex-direction: column;
}
.header [object Object]
  background: #f8f9fa;
  padding: 1rem;
  border-bottom:1px solid #e9ecef;
}
.main {
  flex: 1
  padding: 2rem;
}
.footer[object Object]  background: #343a40;
  color: white;
  padding: 1rem;
  text-align: center;
}
</style>
特定のページでカスタムレイアウトを使用する場合:
vue<!-- layouts/admin.vue -->
<template>
  <div class="admin-layout">
    <aside class=sidebar">
      <nav>
        <NuxtLink to="/admin/dashboard">ダッシュボード</NuxtLink>
        <NuxtLink to=/admin/users>ユーザー管理</NuxtLink>
        <NuxtLink to="/admin/posts>記事管理</NuxtLink>
      </nav>
    </aside>
    <main class=content>      <slot />
    </main>
  </div>
</template>
vue<!-- pages/admin/dashboard.vue -->
<template>
  <div class=dashboard>
    <h1理者ダッシュボード</h1>
    <p>管理画面専用レイアウトを使用しています</p>
  </div>
</template>
<script setup>
// カスタムレイアウトを指定
definePageMeta([object Object]  layout: 'admin'
})
</script>
エラーページのカスタマイズ
Nuxt では、エラーページをカスタマイズして、ユーザー体験を向上させることができます。
vue<!-- error.vue -->
<template>
  <div class="error-page">
    <div class="error-content">
      <h1>{{ error.statusCode }}</h1>
      <h2>{{ error.statusMessage }}</h2>
      <p>{{ error.message }}</p>
      <div class=actions>
        <button @click=handleError>ホームに戻る</button>
        <button @click="goBack>前のページに戻る</button>
      </div>
    </div>
  </div>
</template>
<script setup>
const error = useError()
const handleError = () =>[object Object]
  // エラーをクリアしてホームページに遷移
  clearError({ redirect: /})
}
const goBack = () =>[object Object]  // ブラウザの戻るボタンと同じ動作
  if (process.client) {
    window.history.back()
  }
}
</script>
<style scoped>
.error-page[object Object]
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100text-align: center;
}
.error-content h1
  font-size: 6m;
  color: #dc3545;
  margin: 0actions[object Object]  margin-top:2}
.actions button[object Object]  margin: 0 0.5rem;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>
SEO 対応のためのメタデータ設定
SEO 対策は、Web アプリケーションの成功に不可欠です。Nuxt では、ページごとにメタデータを設定できます。
vue<!-- pages/blog/[slug].vue -->
<template>
  <div class=blog-post>
    <h1>{[object Object] post?.title }}</h1>
    <div class=content" v-html=post?.content></div>
  </div>
</template>
<script setup>
const route = useRoute()
const slug = route.params.slug
// 記事データを取得
const { data: post } = await useFetch(`/api/posts/${slug}`)
// 動的なメタデータ設定
useHead({
  title: post.value?.title || '記事が見つかりません,meta: [
    { name: 'description', content: post.value?.excerpt || '' },
    { property: 'og:title', content: post.value?.title || '' },
    { property:og:description', content: post.value?.excerpt || '' },
    { property: 'og:image', content: post.value?.featuredImage || },  { name: 'twitter:card', content: 'summary_large_image' }
  ],
  link:
   [object Object] rel: canonical, href: `https://example.com/blog/${slug}` }
  ]
})
</script>
パフォーマンス最適化
遅延読み込み(Lazy Loading)の実装
Nuxt では、ページを遅延読み込みすることで、初期ロード時間を短縮できます。
vue<!-- pages/heavy-page.vue -->
<template>
  <div class="heavy-page>
    <h1>重いコンテンツのページ</h1>
    <div class="content">
      <!-- 大量のコンテンツ -->
    </div>
  </div>
</template>
<script setup>
// 遅延読み込みを有効にする
definePageMeta({
  lazy: true
})
// 重い処理を遅延実行
const heavyData = await $fetch(/api/heavy-data,[object Object]  lazy: true
})
</script>
プリフェッチの活用
プリフェッチ機能を使って、ユーザーがリンクにホバーした時に次のページを事前に読み込みます。
vue<!-- pages/index.vue -->
<template>
  <div class="home">
    <h1>ホームページ</h1>
    <div class="navigation>
      <!-- プリフェッチを有効にする -->
      <NuxtLink to=/aboutprefetch>私たちについて</NuxtLink>
      <NuxtLink to="/blogprefetch>ブログ</NuxtLink>
      <NuxtLink to="/contact" prefetch>お問い合わせ</NuxtLink>
    </div>
  </div>
</template>
特定の条件下でのみプリフェッチを有効にする場合:
vue<!-- pages/blog/index.vue -->
<template>
  <div class=blog-list>
    <h1ログ一覧</h1>
    <div class="posts">
      <article v-for="post in posts" :key="post.id" class=post">
        <h2>[object Object][object Object] post.title }}</h2>
        <p>{{ post.excerpt }}</p>
        <!-- 条件付きプリフェッチ -->
        <NuxtLink
          :to="`/blog/${post.slug}`"
          :prefetch="post.isPopular"
        >
          続きを読む
        </NuxtLink>
      </article>
    </div>
  </div>
</template>
<script setup>
const { data: posts } = await useFetch('/api/posts)</script>
キャッシュ戦略
Nuxt のキャッシュ機能を活用して、パフォーマンスを向上させましょう。
vue<!-- pages/products/[id].vue -->
<template>
  <div class=product-detail">
    <h1product?.name }}</h1>
    <p>{{ product?.description }}</p>
    <p>価格: ¥{{ product?.price }}</p>
  </div>
</template>
<script setup>
const route = useRoute()
const productId = route.params.id
// キャッシュ付きでデータを取得
const[object Object] data: product } = await useFetch(`/api/products/${productId}`, [object Object]  key: `product-${productId}`,
  default: () => null,
  // 5シュ
  server: false,
  lazy: true
})
// ページレベルでのキャッシュ設定
definePageMeta({
  keepalive: true
})
</script>
パフォーマンス最適化でよくあるエラー
error# エラー: [nuxt] [request error] Cannot read properties of undefined (reading value)
# 原因: キャッシュキーが正しく設定されていない
# 解決策: ユニークなキーを設定する
# エラー: [nuxt] [request error] prefetch is not a function
# 原因: prefetchの使用方法が間違っている
# 解決策: NuxtLinkコンポーネントでprefetchプロパティを使用
まとめ
Nuxt のファイルベースルーティングは、開発効率を劇的に向上させる革新的な仕組みです。この記事で学んだ内容を実践することで、保守性が高く、ユーザー体験の優れた Web アプリケーションを構築できるようになります。
重要なポイントを振り返ると:1. ファイル構造がそのまま URL 構造になる - 直感的で理解しやすい 2. 動的ルートで柔軟なページ作成 - パラメータを活用した動的コンテンツ 3 ミドルウェアで遷移制御 - 認証やログ出力などの処理を自動化 4. アニメーションで UX 向上 - スムーズなページ遷移でユーザー体験を向上 5. SEO 対策とパフォーマンス最適化 - 検索エンジン対応と高速化
実際のプロジェクトでは、これらの機能を組み合わせることで、より高度なアプリケーションを構築できます。エラーが発生した場合は、この記事で紹介した解決策を参考に、一つずつ問題を解決していきましょう。
Nuxt のファイルベースルーティングをマスターすれば、開発の世界が大きく広がります。ぜひ実践的なプロジェクトで活用してみてください。
関連リンク
 article article- Nuxt Nitro のしくみを図解で理解:サーバーレス実行とアダプタの舞台裏
 article article- Nuxt 本番運用チェックリスト:セキュリティヘッダー・CSP・Cookie 設定を総点検
 article article- Nuxt クリーンアーキテクチャ実践:UI・ドメイン・インフラを composable で分離
 article article- Nuxt nuxi コマンド速見表:プロジェクト作成からモジュール公開まで
 article article- Nuxt を macOS + yarn で最短構築:ESLint/Prettier/TS 設定まで一気通貫
 article article- Nuxt と Next.js を徹底比較:開発体験・レンダリング・エコシステムの違い
 article article- MySQL ERROR 1449 対策:DEFINER 不明でビューやトリガーが壊れた時の復旧手順
 article article- Cursor で差分が崩れる/意図しない大量変更が入るときの復旧プレイブック
 article article- Motion(旧 Framer Motion)で exit が発火しない/遅延する問題の原因切り分けガイド
 article article- JavaScript 時刻の落とし穴大全:タイムゾーン/DST/うるう秒の実務対策
 article article- Cline が差分を誤適用する時:改行コード・Prettier・改フォーマット問題の解決
 article article- htmx で二重送信が起きる/起きない問題の完全対処:trigger と disable パターン
 blog blog- iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
 blog blog- Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
 blog blog- 【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
 blog blog- Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
 blog blog- Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
 blog blog- フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
 review review- 今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
 review review- ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
 review review- 愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
 review review- 週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
 review review- 新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
 review review- 科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来