📑

Nextjs (App Router) と Bootstrap で固定レイアウトの Application Bar を作る

2023/07/30に公開

概要

タイトルの通りです。

具体的には / のページが下の図のようになっていたとき、

root

/dashboard とか他のページに遷移したときでも、画面の上にある Application Bar (NavBar) と下にある footer のレイアウトは固定されたままにするということです。

root

この記事では react-bootstrap を使っていませんが、使う場合でも基本的な部分は多分同じです。

環境とかバージョンなど

JavaScript でやります。TypeScript でも手順自体はほぼ同じでいけます。

$ node -v
16.16.0
$ yarn -v
1.22.19
  • bootstrap: 5.3.1
  • create-next-app: 13.4.12
  • next: 13.4.12
  • react: 18.2.0
  • react-dom: 18.2.0

準備

アプリケーションの作成

$ yarn create-next-app@latest

✔ What is your project named? … next-bootstrap-appbar
✔ Would you like to use TypeScript? … No
✔ Would you like to use ESLint? … No
✔ Would you like to use Tailwind CSS? … No
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias? … No

$ cd next-bootstrap-appbar
$ yarn add bootstrap

動作テスト

$ yarn dev

ブラウザで https://localhost:3000 を開いてページが表示されたらとりあえずOKです。

改造

ファイル構造

dashboard ディレクトリを作成して page.js を作っておきます。この /app/dashboard/page.jsというファイルは、ブラウザ上では /dashboard というパスでアクセスできます。

$ tree app
app/
├── dashboard   # 追加
│   └── page.js # 追加
├── favicon.ico
├── globals.css
├── layout.js
└── page.js    

/app/layout.js の中でアプリケーションバー (NavBar) を作成しておき、<main> 要素の中に /app/page.js の中身を展開する感じにします。`

ブラウザで /dashboard に遷移したときも、/app/layout.js/app/dashboard/page.js の両方を使って画面がレンダリングされるので、/app/layout.js に作成してあるアプリケーションバーが表示されます。

各ファイルの中身

layout.js

HTML のコード自体は下記ページのほぼコピペです。

import 'bootstrap/dist/css/bootstrap.min.css';
import styles from './globals.css';

export default function RootLayout({children}){ 
  return <>
    <html lang="en" className='h-100'>
      <body className="d-flex flex-column h-100">
        <header>
          <nav className="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
            <div className="container-fluid">
              <a className="navbar-brand" href="#">Fixed navbar</a>
              <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
                <span className="navbar-toggler-icon"></span>
              </button>
              <div className="collapse navbar-collapse" id="navbarCollapse">
                <ul className="navbar-nav me-auto mb-2 mb-md-0">
                  <li className="nav-item">
                    <a className="nav-link active" aria-current="page" href="#">Home</a>
                  </li>
                  <li className="nav-item">
                    <a className="nav-link" href="#">Link</a>
                  </li>
                  <li className="nav-item">
                    <a className="nav-link disabled" href="#" tabIndex="-1" aria-disabled="true">Disabled</a>
                  </li>
                </ul>
                <form className="d-flex">
                  <input className="form-control me-2" type="search" placeholder="Search" aria-label="Search"/>
                  <button className="btn btn-outline-success" type="submit">Search</button>
                </form>
              </div>
            </div>
          </nav>
        </header>
        <main className={styles.main}>
          <div className="container">
            {children}
          </div>
        </main>
        <footer className="footer mt-auto py-3 bg-dark">
          <div className="container">
            <span className="text-light">Powered by BBLED 2023.</span>
          </div>
        </footer>
      </body>
    </html>
  </>
}

global.css

padding を設定して、<main> の中身が NavBar の領域と重複することを防ぎます。これも、下記コードからほぼそのままコピペしたものです。

main {
  padding: 60px 15px 0;
}

page.js

/dashboard へ遷移するためのボタンを表示します。

/app/page.js/app/layout.js<main> の中にある {children} の部分に展開されるため、結果的に NavBar の下にボタンが表示されることになります。

useRouter() はページ遷移のために使っています。useRouter() を使うときは "use client" の指定が必要になります。

'use client'
import { useRouter } from 'next/navigation'

export default function Home() {
  const router = useRouter()
  return <>
    <div className="my-3">
      <div type="button" className='btn btn-primary' onClick={() => router.push('/dashboard')}>
        to Dashboard
      </div>
    </div>
  </>
}

dashboard/page.js

/ へ遷移するためのボタンを表示します。基本、/app/page.js と同じですが、このコードでは例示のために遷移部分を関数として定義しています。

"use client"
import { useRouter } from 'next/navigation'

export default function Dashboard() {
  const router = useRouter();

  const clickButton = (href) => {
    router.push(href);
  }

  return <>
    <div className="my-3">
      <div type="button" className='btn btn-secondary'  onClick={() => clickButton('/')}>
        Top Page
      </div>
    </div>
  </>
}

実行

$ yarn dev

ブラウザで https://localhost:3000 を開きます。

routing

上の図のように、ボタンを押して画面が切り替われば成功です。

Discussion