Open4

NextTurotial

Junpei KatayamaJunpei Katayama

2.CSS Styling

https://nextjs.org/learn/dashboard-app/css-styling

Tailwind

https://tailwindcss.com/
css に以下のような感じで tailwind ディレクティブを書き込むと各スタイリングが import される

@tailwind base;
@tailwind components;
@tailwind utilities;

以下のようにクラス名を tailwind のルールに則って命名するといい感じのスタイルが当たる

<h1 className="text-blue-500">I'm blue!</h1>

CSS Modules

home.module.css のように modules を含めた名前で命名すると、css をモジュールとして利用できる。

import して別名を付けて、クラス名で呼び出せるよう。

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';
 
export default function Page() {
  return (
    <main className="flex min-h-screen flex-col p-6">
      <div className={styles.shape} />
    // ...
  )
}

Tailwind を使うか CSS Modules のどちらを使うかは好みによるので、どっちでも良いし、なんなら両立しても良い。

clsx

動的にスタイルを変えたい場合に用いる。
例えばボタンの活性/非活性をコントロールしたい場合など。

以下のように記述する。

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';
 
export default function Page() {
  return (
    <main className="flex min-h-screen flex-col p-6">
      <div className={styles.shape} />
    // ...
  )
}
Junpei KatayamaJunpei Katayama

3. Optimize Fonts and Images(フォントと画像の最適化)

https://nextjs.org/learn/dashboard-app/optimizing-fonts-images

なぜフォントを最適化する必要があるのか

カスタムフォントはデザインに必須だが、追加でダウンロードが発生するためパフォーマンスに影響する可能性があるため。
また、Google のウェブサイト評価には累積レイアウトシフトなるものがある。
https://vercel.com/blog/how-core-web-vitals-affect-seo
Webページの表示の際、まずはデフォルトのフォントがロードされ、その後追加でカスタムフォントがダウンロードされて置き換わる。
その際にレイアウトがズレることがある。これがレイアウトシフト。

Next.js では next/font を使えばモジュールの利用するフォントを最適化してくれる。
ビルド時にフォントをダウンロードし、静的アセットとしてホストするため、追加のリクエストも発生しない。

なぜ画像を最適化する必要があるのか

/public 配下に静的アセットを配置するとアプリケーションから参照できる。

従来の html では普通にやると以下のようになる。

<img
  src="/hero.png"
  alt="Screenshots of the dashboard project showing desktop version"
/>

画像によるレイアウト崩れや遅延ロードなどを考えたり色々手動でやるとしんどい。
next/image を使えば簡単に実装できる。

<Image> コンポーネントは以下の機能を持つ。

  • 画像サイズの自動調整
  • 遅延ロード
  • ブラウザが対応していれば WEBP などに変換

画像は以下のような感じでデスクトップ/モバイルで出し分けられる。

        <div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
          {/* 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="Screenshot of the dashboard project showing mobile version"
          />
        </div>

className がポイント。
blockdisplay: block を表す。
https://tailwindcss.com/docs/display
md:hidden はレスポンシブ用のやつ。
https://tailwindcss.com/docs/responsive-design
md 部分は画面サイズを表す。
md サイズ以上の画面 = PC や ipad でのブラウザ視聴と考えて良さそう。

また、一般的に画像の with と height を決めておくのはデザイン崩れの予防になるので忘れないようにしよう。

Junpei KatayamaJunpei Katayama

4. Creating Layouts and Pages

https://nextjs.org/learn/dashboard-app/creating-layouts-and-pages

ルーティング

ディレクトリ構造がそのままルーティングとなる。
page.tsx という名前のファイルがあると、ページとして認識される。
例えば app/page.tsx -> /app/dashboard/page.tsx -> /dashboard といった具合。

共通レイアウト

layout.tsx を配置して、複数のページで共有される UI を定義できる。
<Layout /> コンポーネントは children props を受け取り、これが子ページとなる。
layout.tsx より下にあるページは , Layout コンポーネントの中に自動的にネストされる。

import SideNav from '@/app/ui/dashboard/sidenav';
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
      <div className="w-full flex-none md:w-64">
        <SideNav />
      </div>
      <div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
    </div>
  );
}

Next.js ではレイアウトを使うとページコンポーネントだけが更新され、レイアウトは再レンダリングされないという利点がある。
これを partial rendering と呼ぶ。

ちなみに 3 章で inter フォントを設定した app/layout.tsxRootLayout は必須。
メタデータはここで設定する。

Junpei KatayamaJunpei Katayama

5. Navigating Between Pages

https://nextjs.org/learn/dashboard-app/navigating-between-pages

<Link /> コンポーネントを使うことでクライアントサイドナビゲーションを実現できる。
使い方はほとんど a タグと同じ。
Link を使うだけでページ更新を挟まず、差分だけが更新される。

import {
  UserGroupIcon,
  HomeIcon,
  DocumentDuplicateIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
 
// ...
 
export default function NavLinks() {
  return (
    <>
      {links.map((link) => {
        const LinkIcon = link.icon;
        return (
          <Link
            key={link.name}
            href={link.href}
            className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
          >
            <LinkIcon className="w-6" />
            <p className="hidden md:block">{link.name}</p>
          </Link>
        );
      })}
    </>
  );
}

実は Next.js はナビゲーション体験向上のため、自動的にルートセグメント毎にコードを分割している。
これによって、どこかのページがエラーを起こしても他は動作するという副作用もある。

Link コンポーネントが表示されるたび、そのリンク先をプリフェッチするため、リンク先をクリックするころにはすべて読み込まれているということになる。

パターン: アクティブリンクの表示

ユーザの現在いる場所へのリンクを表示するためにはパスを取得する必要がある。
そういった場合usePathName() hook を使えば良い。
これは hook なので React の use client ディレクティブが必要なことに注意。
https://ja.react.dev/reference/rsc/use-client
use client はサーバサイドの React を併用する際に、このコンポーネントはクライアントサイドで動作するものであることを示すために使う。

'use client';
 
import {
  UserGroupIcon,
  HomeIcon,
  InboxIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
 
// ...

clsx を利用すれば、usePathName で取得したリンクと現在地が同じ時に、リンクに特定のスタイルを当てるということが実現できる。

          <Link
            key={link.name}
            href={link.href}
            className={clsx(
              'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
              {
                'bg-sky-100 text-blue-600': pathname === link.href,
              },
            )}
          >