16.

【Vue.js】ヘッダー/フッターの共通コンポーネントの作り方

編集
この記事の要点
  • Web アプリのヘッダー / フッターを共通コンポーネントとして再利用する方法
  • Laravel Blade: @include / @yield / @extends
  • Vue.js: コンポーネント import + <Header /> / <Footer />
  • React: コンポーネント import + JSX で配置
  • Next.js / Nuxt.js: Layout コンポーネントで自動適用

 

Laravel Blade のパターン

方法 1: @extends と @yield(推奨)

<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>@yield('title', 'デフォルトタイトル')</title>
    @yield('styles')
</head>
<body>

    @include('partials.header')

    <main>
        @yield('content')
    </main>

    @include('partials.footer')

    @yield('scripts')
</body>
</html>
<!-- resources/views/partials/header.blade.php -->
<header class="site-header">
    <h1><a href="{{ route('home') }}">My Site</a></h1>
    <nav>
        <ul>
            <li><a href="{{ route('home') }}">ホーム</a></li>
            <li><a href="{{ route('users.index') }}">ユーザー</a></li>
            @auth
                <li><a href="{{ route('profile') }}">マイページ</a></li>
                <li><a href="{{ route('logout') }}">ログアウト</a></li>
            @else
                <li><a href="{{ route('login') }}">ログイン</a></li>
            @endauth
        </ul>
    </nav>
</header>

<!-- resources/views/partials/footer.blade.php -->
<footer class="site-footer">
    <p>© {{ date('Y') }} My Site</p>
</footer>
<!-- resources/views/users/index.blade.php -->
@extends('layouts.app')

@section('title', 'ユーザー一覧')

@section('content')
    <h2>ユーザー一覧</h2>
    @foreach ($users as $user)
        <p>{{ $user->name }}</p>
    @endforeach
@endsection

@section('scripts')
    <script src="{{ asset('js/users.js') }}"></script>
@endsection

方法 2: コンポーネント(Laravel 7+)

<!-- resources/views/components/site-header.blade.php -->
<header class="site-header">
    <h1>{{ $title ?? config('app.name') }}</h1>
    <nav>{{ $slot }}</nav>
</header>

<!-- 使い方 -->
<x-site-header title="My Page">
    <ul>
        <li><a href="/">ホーム</a></li>
    </ul>
</x-site-header>

Vue.js のパターン

Vue 3 + Composition API

<!-- components/AppHeader.vue -->
<template>
    <header class="site-header">
        <h1>
            <router-link to="/">{{ title }}</router-link>
        </h1>
        <nav>
            <router-link to="/">ホーム</router-link>
            <router-link to="/users">ユーザー</router-link>
            <span v-if="user">
                {{ user.name }} さん
                <button @click="logout">ログアウト</button>
            </span>
            <router-link v-else to="/login">ログイン</router-link>
        </nav>
    </header>
</template>

<script setup>
defineProps({ title: { type: String, default: "My Site" } });

import { useAuthStore } from "@/stores/auth";
const authStore = useAuthStore();
const user = computed(() => authStore.user);

function logout() {
    authStore.logout();
}
</script>
<!-- App.vue -->
<template>
    <AppHeader title="My Site" />
    <main>
        <router-view />
    </main>
    <AppFooter />
</template>

<script setup>
import AppHeader from "./components/AppHeader.vue";
import AppFooter from "./components/AppFooter.vue";
</script>

React のパターン

// components/Header.tsx
import { Link } from "react-router-dom";
import { useAuth } from "../hooks/useAuth";

export default function Header({ title = "My Site" }) {
    const { user, logout } = useAuth();
    return (
        <header className="site-header">
            <h1><Link to="/">{title}</Link></h1>
            <nav>
                <Link to="/">ホーム</Link>
                <Link to="/users">ユーザー</Link>
                {user ? (
                    <>
                        <span>{user.name} さん</span>
                        <button onClick={logout}>ログアウト</button>
                    </>
                ) : (
                    <Link to="/login">ログイン</Link>
                )}
            </nav>
        </header>
    );
}
// App.tsx
import Header from "./components/Header";
import Footer from "./components/Footer";

function App() {
    return (
        <>
            <Header title="My Site" />
            <main>
                <Routes>
                    <Route path="/" element={<Home />} />
                    <Route path="/users" element={<Users />} />
                </Routes>
            </main>
            <Footer />
        </>
    );
}

Next.js の Layout

// app/layout.tsx (App Router)
import Header from "@/components/Header";
import Footer from "@/components/Footer";

export default function RootLayout({ children }) {
    return (
        <html lang="ja">
            <body>
                <Header />
                <main>{children}</main>
                <Footer />
            </body>
        </html>
    );
}

// → 全ページで自動的に Header / Footer が表示される

Nuxt.js の Layout

<!-- layouts/default.vue -->
<template>
    <div>
        <AppHeader />
        <main>
            <slot />
        </main>
        <AppFooter />
    </div>
</template>

<!-- pages/users.vue → 自動的に default レイアウトが適用 -->
<template>
    <div>
        <h2>ユーザー一覧</h2>
    </div>
</template>

<!-- 別レイアウトを指定 -->
<script setup>
definePageMeta({ layout: "auth" });  // layouts/auth.vue を使う
</script>

Web Components(純粋 HTML / JS)

// header.js
class SiteHeader extends HTMLElement {
    connectedCallback() {
        this.innerHTML = `
            <header>
                <h1><a href="/">My Site</a></h1>
                <nav>
                    <a href="/">ホーム</a>
                    <a href="/users">ユーザー</a>
                </nav>
            </header>
        `;
    }
}
customElements.define("site-header", SiteHeader);

// HTML
<site-header></site-header>

共通コンポーネント設計の Tips

  • props で柔軟性: タイトル・サブメニュー等を props で受け取って汎用化
  • slot を活用: ヘッダー内に任意の要素を差し込めるように
  • 認証状態に応じて: ログイン中はマイページリンク、未ログインはログインリンク
  • アクティブリンクのハイライト: 現在ページに応じてスタイル変更
  • SP/PC で別表示: メディアクエリ + display 切替、または別コンポーネント
  • SEO への配慮: <header>, <nav>, <footer> 等の意味的タグ

関連記事

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. インストール(ファイルのダウンロード)
  2. npmを使用したプロジェクトの作成
  3. for 繰り返し処理
  4. ifの条件分岐とtemplateを用いたグループ化
  5. クリック時のイベント処理(on:click)
  6. modelとdata フォーム入力値とDOMへの即時反映
  7. computed(算出プロパティ)と使い方とdataとの違い
  8. ライフサイクルフック(created / mounted / updated / destroyedの使い方)
  9. $nextTickの使い方(ライフサイクルフック)
  10. メソッドの定義方法
  11. エラー一覧
  12. ルーティング設定
  13. aリンクの貼り方と動的URLの作成
  14. Mixinを利用した共通処理の記述方法
  15. v-bindによるデータ連携
  16. ヘッダー/フッターの共通コンポーネント
  17. ナビゲーションの現在ページをハイライトする方法
  18. 画面サイズの取得方法