【3章】Next.jsのチュートリアルをやってみた
これは Next.js の公式チュートリアルの3. Optimizing Fonts and Imagesをやってみたメモです
前章のメモ
Next.js の公式チュートリアルの該当ページ
学ぶこと
- next/font でカスタムフォントを追加する方法
- next/image で画像を追加する方法
- Next.js でのフォントや画像の最適化
なぜフォントを最適化するのか?
next/fontを使用すると、ビルド時にフォントファイルをダウンロードします。
ダウンロードされているフォントファイルを使うのでアプリのパフォーマンスが向上します
プライマリーフォントを追加する
/app/ui/fonts.tsファイルを作成し、アプリ全体で使うフォントを定義します。
今回はInterというフォントを使用し、latin 文字のみをサブセット化してみます
import { Inter } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
/app/ui/fonts.tsをアプリ全体に適用させるために /app/layout.tsxで読み込みます
インポートして body の className に追加します。
また、antialiasedはTailwind のクラスでフォントを滑らかにしてくれます
import '@/app/ui/global.css';
+import { inter } from '@/app/ui/fonts';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
+ <body className={`${inter.className} antialiased`}>{children}</body>
</html>
);
}
アプリにアクセスすると、フォントが変更されていると思います
セカンダリーフォントを追加する
Lusitanaというフォントを追加してみます。
先ほどと同じように /app/ui/fonts.ts でフォントをインポートし、サブセット化していきます。
Lusitanaを見てみると、font weight が 400 と 700なのでこれも指定します。
-import { Inter } from 'next/font/google';
+import { Inter, Lusitana } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
+export const lusitana = Lusitana({
+ weight: ['400', '700'],
+ subsets: ['latin'],
+});
これを /app/page.tsxの p タグに適用させます
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import styles from '@/app/ui/home.module.css';
+import { lusitana } from '@/app/ui/fonts';
export default function Page() {
return (
<main className="flex min-h-screen flex-col p-6">
<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
+ <AcmeLogo />
</div>
<div className="mt-4 flex grow flex-col gap-4 md:flex-row">
<div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
{/* <div className="h-0 w-0 border-b-[30px] border-l-[20px] border-r-[20px] border-b-black border-l-transparent border-r-transparent" /> */}
<div className={styles.shape} />
<p
+ className={`${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal`}
>
<strong>Welcome to Acme.</strong> This is the example for the{' '}
...
11 行目の <AcmeLogo /> のコメントアウトも併せて外しておきます。
このコンポーネントのコードを見るとlusitana をインポートしているのでエラーになっていました。
ただ、lusitana を定義したことでこのコンポーネントが使用可能になりました。
import { GlobeAltIcon } from '@heroicons/react/24/outline';
import { lusitana } from '@/app/ui/fonts';
export default function AcmeLogo() {
return (
<div
className={`${lusitana.className} flex flex-row items-center leading-none text-white`}
>
<GlobeAltIcon className="h-12 w-12 rotate-[15deg]" />
<p className="text-[44px]">Acme</p>
</div>
);
}
<Image>コンポーネント
<Image>コンポーネントは<img>タグの拡張で、下記の自動画像最適化機能を提供している
- 画像読み込み時のレイアウトずれを防ぐ
- デバイスごとに画像のサイズを変更する
- 画像の遅延読み込み
- ブラウザがサポートしている場合に、WebP や AVIF など最新の画像ファイルをサポート
デスクトップヒーロー画像の追加
<Image>コンポーネントを使って、デスクトップのヒーロー画像を追加します。
画像は /public/hero-desktop.pngを使います。
/app/page.tsxでnext/image よりインポートして、<Image>コンポーネントを追加します。
src に記載するパスは public をルートとして指定しています。
Next.js では、画像などの静的アセットを /public フォルダ で管理します。
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import styles from '@/app/ui/home.module.css';
import { lusitana } from '@/app/ui/fonts';
+import Image from 'next/image';
export default function Page() {
return (
....
{/* Add Hero Images Here */}
+ <Image
+ src="/hero-desktop.png"
+ width={1000}
+ height={760}
+ className="hidden md:block"
+ alt="Screenshots of the dashboard project showing desktop version"
+ />
</div>
</div>
</main>
);
}
- レイアウトずれを回避するために width と height を指定。
- hidden と md:block を組み合わせて、768px 以上の width のときのみ画像を表示させています
- hidden = display:none で画像を非表示
- md: block = 768px 以上の width のみ display: block を付与
アプリにアクセスすると ↓ のように右側に画像が表示されます
モバイル用のヒーローイメージを追加
- 要件
- /public/hero-mobile.pngを使う
- 画像はwidth:560px, height:620px
- モバイルでは表示されるが、デスクトップでは非表示
先ほどの /app/page.tsx <Image>コンポーネントの下に追加していきます
画像ファイルや width,、height を変更します。
className は、デスクトップ画像が出ないとき(=width が 768px 未満)の時に表示させたいので
基本block(display: block) で md:hidden(768px 以上の時 display: hidden)としておきます
...
export default function Page() {
return (
....
{/* Add Hero Images Here */}
<Image
src="/hero-desktop.png"
width={1000}
height={760}
className="hidden md:block"
alt="Screenshots of the dashboard project showing desktop version"
/>
+ <Image
+ src="/hero-mobile.png"
+ width={560}
+ height={620}
+ className="block md:hidden"
+ alt="Screenshots of the dashboard project showing mobile version"
+ />
</div>
</div>
</main>
);
}
ブラウザから確認します。普通に表示すると、デスクトップの方の画像が表示されます。
モバイルでの表示を確認したいときは F12⇒Ctrl+Shift+M を押します。(Chrome)
関連ドキュメント
- Image Optimization Docs
- Font Optimization Docs
- Improving Web Performance with Images (MDN)
- Web Fonts (MDN)
次章のメモ
Discussion