コンポーネントとレイアウト
各ページのレイアウトを以下のように統一したい。
(Contentsをページごとに切り替え)
そのためHeader、Footer、Layoutを決めるComponentを作成する。
- まずは各コンポーネントを格納するフォルダを作成する。
フォルダ構成(好み)は「アプリのルート>src>components」とし、componentsフォルダの配下にfooter.tsx、header.tsx、pageLayout.tsxのファイルを作成する。
まずはHeaderコンポーネントを以下の内容で設定する。
- Headタグ内にWebアプリのメタ情報を設定
- ヘッダーの表示要素を作成(以下の例ではアイコンとアプリ名を中央揃え表示)
- publicにimagesフォルダを作成し、アイコン用の画像を配置
import type { NextPage } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import Link from 'next/link';
import styles from '/styles/Home.module.css';
import {
APP_NAME,
SITE_TITLE,
HEADER_ICON_HEIGHT,
HEADER_ICON_WIDTH,
} from '../app.const';
const Header: NextPage = () => {
return (
<>
<Head>
<title>{APP_NAME}</title>
<link rel="icon" href="/favicon.ico" />
<meta name="description" content="Vtuber Info App" />
<meta
property="og:image"
content={`https://og-image.vercel.app/${encodeURI(
SITE_TITLE
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/>
<meta name="og:title" content={SITE_TITLE} />
<meta
name="viewport"
content="width=device-width, initial-scale=1"
></meta>
</Head>
<div className={styles.header}>
<Image
priority
src="/images/media-small-icon.svg"
height={HEADER_ICON_HEIGHT}
width={HEADER_ICON_WIDTH}
alt={APP_NAME}
/>
<h1 className={styles.headerText}>{APP_NAME}</h1>
</div>
</>
);
};
export default Header;
Imageタグの使い方はリファレンスを参照。
使用している定数はsrcフォルダ配下にapp.const.tsを作成、以下のように定義を行う。
export const APP_NAME = 'Vtuber App';
export const SITE_TITLE = 'Vtuber Web Site';
export const HEADER_ICON_HEIGHT: number = 72;
export const HEADER_ICON_WIDTH: number = HEADER_ICON_HEIGHT;
続いてFooterを定義する。
こちらは一旦テンプレートのフッターをそのまま使用している。
import type { NextPage } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import Link from 'next/link';
import styles from '/styles/Home.module.css';
import { APP_NAME, SITE_TITLE } from '../app.const';
const Footer: NextPage = () => {
return (
<>
<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</>
);
};
export default Footer;
最後に画面全体のレイアウトを規定するコンポーネントクラスを作成する。
先ほど作成したHeaderコンポーネントとFooterコンポーネントを使用してレイアウトを規定する。各ページではこのコンポーネントを呼び出して各画面の要素を引数(children:子要素)で渡すことでレイアウトに埋め込んでいる。
import type { NextPage } from 'next';
import Footer from './footer';
import Header from './header';
import styles from '/styles/Home.module.css';
type Props = {
children?: React.ReactNode;
};
const PageLayout: NextPage = ({ children }: Props) => {
return (
<>
<Header />
<div className={styles.container}>
<main className={styles.main}>{children}</main>
</div>
<Footer />
</>
);
};
export default PageLayout;
ページでの使用例は以下の通り、PageLayoutコンポーネントで挟んだ内容が画面のヘッダーとフッターの間に埋め込まれる。
import type { NextPage } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import Link from 'next/link';
import PageLayout from '../src/components/pageLayout';
import styles from '../styles/Home.module.css';
const Home: NextPage = () => {
return (
<PageLayout>
<h1 className="title">Home</h1>
<h2>
<Link href="/list/vtuber">
<a>Go List Page</a>
</Link>
</h2>
</PageLayout>
);
};
export default Home;
スタイル
cssのファイルはテンプレート作成時にstylesフォルダが作成されるのでその中に格納していく。テンプレートでは全体適用のcssファイル(globals.css
)とCSSモジュールファイル(Home.modules.css
)が作成されている。レイアウト調整のため以下モジュールファイルに設定している。
.container {
max-width: auto;
padding: 0 1rem;
margin: 1rem 2rem;
word-break: break-all;
}
.main {
min-height: 60vh;
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
@media (max-width: 480px) {
.contents {
overflow-wrap: normal;
}
}
.header {
display: -webkit-flex;
display: flex;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center;
min-height: 10vh;
}
.headerText {
margin-left: 30px;
}
NextはデフォルトでCSS in JSも使えるものの現状一旦スキップ。個別のページやコンポーネントでcss作りこみたくなった場合に使用する。