【Everyday Next.js】8. daisyUI

daisyUI

今回は、Next.js の公式ドキュメントを離れて、daisyui を導入する。

daisyui.com

daisyUI とは

daisyUI は、Tailwind CSS をベースにしたコンポーネントライブラリである。

GitHub の tailwind タグでスター数が3番目に多いライブラリになっている。

1番目は、Tailwind CSS 本家。

2番目は、UI コンポーネントライブラリである Radix UI と、Tailwind CSS をベースとした「shadcn-ui」。

つまり、純粋な Tailwind CSSコンポーネントライブラリの中では現状最も有名ということになる。

導入方法

  1. daisyUI をインストール
npm i -D daisyui
  1. tailwind.config.ts に追加
// tailwind.config.ts
module.exports = {
  //...
  plugins: [require('daisyui')],
};

以上。

使い方

例えば、ただの Tailwind CSS で一般的なボタンを実装しようとすると、以下のようにとても長い class を書く必要がある。

(色つけてパディングつけてテキストのサイズ指定してホバー時色が濃くなるようにして…)

<button
  className="inline-block cursor-pointer rounded-md bg-gray-800 px-4 py-3 text-center text-sm font-semibold text-white transition duration-200 ease-in-out hover:bg-gray-900"
>
  Button
</button>

これが daisyUI を使用すると以下の通り。

<button class="btn btn-neutral">Button</button>

daisyUI のコンポーネントをベースに、Tailwind CSS ユーティリティクラスで一部変更も可能

<button class="btn w-64 rounded-full">Button</button>

基本的には、daisyUI のコンポーネントページで使いたいコンポーネントを探してコピペすれば OK。

daisyui.com

Button や Card や Checkbox や Drawer など、よく使う UI は大体揃っている。

おわりに

今回は、Next.js に daisyUI を導入した。 次回は、ダイナミックルーティングなど。

【Everyday Next.js】7. Linking and Navigating

Linking and Navigating

画面遷移の方法

nextjs.org

Next.js には、<a>タグを拡張した組み込みコンポーネント<Link>がある。

基本的には<a>タグと同じように使ってあげれば良い。

import Link from 'next/link';

export default function Page() {
  return <Link href='/dashboard'>Dashboard</Link>;
}
useRouter()

また、useRouter()フックを使って、ルートを変更することもできる。こちらはクライアントコンポーネントでしか使えない。基本的には<Link>コンポーネントの使用を推奨。

'use client';

import {useRouter} from 'next/navigation';

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

  return (
    <button type='button' onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  );
}

<Link>useRouter()を使うメリット

nextjs.org

<Link>などを使うことで、プリフェッチやキャッシュや部分レンダリングあたりをよしなにしてくれるらしい。

まだ experimental の機能ではあるが、ルーティングに対して型をつけてくれるStatically Typed Linksという機能がある。

next.config.jsに設定を追加することで有効になる。

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    typedRoutes: true,
  },
};

module.exports = nextConfig;

例えば、/ と/parent と/parent/child ページが存在するとき、Link の href に["/", "/parent", "/parent/child"]以外のパスを与えようとすると、エラーが出る。

なんなら入力補完が効く。

experimental ではあるが、便利な機能なのでぜひ使っていきたい。

おわりに

今回は、Next.js の画面遷移について見ていった。

次回は、ダイナミックルーティングなど。

【Everyday Next.js】6. Routing Detail

Routing

ページごとに page.tsx が必要

例えば、以下のようなディレクトリ構造だと、

app
├ layout.tsx
└ page.tsx
  ├ about
  │ └ page.tsx
  └ user
    ├ page.tsx
    └ post
      └ page.tsx
  • app/page.tsx -> /
  • app/about/page.tsx -> /about
  • app/user/page.tsx -> /user
  • app/user/post/page.tsx -> /user/post

となる。

Pages Router のように、

のようなファイルを作っても対象にならないので注意。

Root Layout は必須

app ディレクトリ直下のlayout.tsxは必須。

そして、以下のファイルのように、<html>タグと<body>タグを含む必要がある。

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

この Root Layout は、Pages Router の\app.tsx\document.tsx にあたるもののようだ。

Rayout と Template

Rayout も Template も、どちらもページをラップするものである。

2つの違いは、「画面遷移したときに再レンダリングが行われるかどうか」。

Rayout は再レンダリングが行われず、Template は再レンダリングが行われる。

使い分け基準は以下の技術記事がわかりやすい。

zenn.dev

<head>の編集

タイトルやメタデータなどの<head>要素は、layout.tsx や page.tsx で metadata オブジェクトや generateMetadata 関数をエクスポートすることで定義できる。

// app/page.tsx
import {Metadata} from 'next';

export const metadata: Metadata = {
  title: 'Next.js',
};

export default function Page() {
  return '...';
}

逆に、自分で Root Layout に<head>タグを追加すべきではない。上記のような Metadata API を使うようにすること。

おわりに

今日は、App Router での Routing についてもう少し詳細な仕様を見た。 明日は、Linking や Navigating を見ていく。

【Everyday Next.js】5. Routing Fundamentals

nextjs.org

Next.js Routing の基本

Next.js 13 から App Router が導入された。

前までの Pages Router と大きく仕様が変わっているため、ググって出てくる情報にはバージョンの差異がないか注意が必要。

といっても基本原理は変わらない。

Next.js の Routing は、ファイルシステムベースルーティングになっている。

以下の図のように、原則、app フォルダ下のフォルダ構成によって、URL パスが決定する。

https://nextjs.org/_next/image?url=%2Fdocs%2Flight%2Froute-segments-to-path-segments.png&w=1920&q=75&dpl=dpl_3KvQ7chUpCwD5geTFxau9SMj51uW

そして、そのフォルダの中に特定のファイル名を持つファイル群を設置することでそのページの中身を決定する。

ファイル名 説明
layout セグメントとその子の共有 UI
page ルートのユニークな UI とルートの一般公開
loading セグメントとその子の UI をロードする
not-found セグメントとその子の UI が見つからない
error セグメントとその子のためのエラー UI
global-error グローバルエラー UI
route サーバーサイド API エンドポイント
template 特殊化された再レンダリングされたレイアウト UI
default 並列ルートのフォールバック UI

この特定の役割を持ったファイル群は、以下のような階層でレンダリングされる。ネストされた Route の場合は、入れ子レンダリングされる。

https://nextjs.org/_next/image?url=%2Fdocs%2Flight%2Ffile-conventions-component-hierarchy.png&w=1920&q=75&dpl=dpl_3KvQ7chUpCwD5geTFxau9SMj51uW

https://nextjs.org/_next/image?url=%2Fdocs%2Flight%2Fnested-file-conventions-component-hierarchy.png&w=1920&q=75&dpl=dpl_3KvQ7chUpCwD5geTFxau9SMj51uW

試してみる

parent フォルダ、child フォルダに layout.tsx、page.tsx を作って、どのようにレンダリングされるかを実際に試してみる。

// app/parent/layout.tsx
export default function ParentLayout({children}: {children: React.ReactNode}) {
  return (
    <div>
      親レイアウトbegin
      {children}
      親レイアウトend
    </div>
  );
}
// app/parent/page.tsx
export default function Parent() {
  return <div>これは親ページです。</div>;
}
// app/parent/child/layout.tsx
export default function ChildLayout({children}: {children: React.ReactNode}) {
  return (
    <div>
      子レイアウトbegin
      {children}
      子レイアウトend
    </div>
  );
}
// app/parent/child/page.tsx
export default function Child() {
  return <div>これは子ページです。</div>;
}

レイアウトの中にページ、そしてネストされている場合は入れ子のようにレンダリングされていることがわかる。

おわりに

今回は、Next.js App Router の基本原理を理解した。 次回は、App Router の扱い方をもう少し詳しく見ていく。

【Everyday Next.js】4. 脇道 (ANSI Escape Sequences)

前回の記事で、eslint エラーメッセージが文字化け?してると書いたが、

[33m[STARTED][39m Preparing lint-staged...

この、[33mの部分は、調べてみるとどうやらANSI Escape Sequencesらしい。

en.wikipedia.org

qiita.com

これをANSI Escape Sequencesをサポートしているターミナルなどでは、その範囲の文字色を変えられたりするらしいが、今回のダイアログではそのカラーコードがそのまま表示されてしまっているだけだったようだ。

【Everyday Next.js】3. husky / lint-staged

lint 自動実行の必要性

前回も説明した通り、nextjs で build するときは自動で lint も走るようである。

例えば以下のコード。

'use client';

import React, {useState} from 'react';

export default function Home() {
  const isTrue = Math.random() > 0.5;

  if (isTrue) {
    const [count, setCount] = useState(0);
  }

  return <div>Hello, world!</div>;
}

build 自体はできるはずなのだが、React フックを条件付きで呼び出すと ESLint エラーを起こす。

そのため、build しようとすると以下のように lint エラーで build も失敗する。

ということは、少なくとも push をするときに eslint エラーが起きてしまうようなコードだとまずい。

では、「push や commit をする前に毎回npm run lintを実行するようにしましょうね!」とすれば良いかというと、やはり人間なので忘れてしまうこともあるし、面倒である。

したがって、push や commit をする前に「自動で」npm run lintが実行されるようにしたいわけである。

husky / lint-staged

ここで、husky と lint-staged の登場。

husky と lint-staged を使えば、「コミット前にステージされているファイルに lint を自動で走らせる」ことができる。

nextjs.org

github.com

github.com

それぞれの公式ドキュメント通りに設定を進める。

パッケージインストール

npm i -D husky lint-staged

lint-staged 設定ファイル

公式ドキュメントに載っている通り、Next.js で lint-staged を動かしたい場合は以下の lint-staged 設定ファイルが必要。

// .lintstagedrc.js
const path = require('path')

const buildEslintCommand = (filenames) =>
  `next lint --fix --file ${filenames.map((f) => path.relative(process.cwd(), f)).join(' --file ')}`

module.exports = {
  '*.{js,jsx,ts,tsx}': [buildEslintCommand],
}

husky install

npm pkg set scripts.prepare="husky install"
npm run prepare

husky をセットアップするために、husky installの実行が必要である。

複数人で開発したりしないのであれば、単純husky installを実行するだけでも良いのだが、上記は npm scripts に"prepare": "husky install"を追加した上で、npm run prepareを実行してhusky installを走らせている。

npm run prepareは少々特殊なスクリプトで、npm installが実行されたときなどに自動実行される。

つまり、あなた以外がリポジトリを clone して開発するとなったときに、わざわざhusky installを実行してもらわなくても、勝手に実行してくれるのだ。

hook を追加

npx husky add .husky/pre-commit "npx lint-staged"

「コミット前にnpx lint-stagedを husky で自動実行するための設定を追加しなさい」というコマンド。

動作確認

本当に動作するか、わざと eslint エラーが起きるコードをコミットしようとしてみる。 コミットは VSCodeGUI で行ってみた。

狙い通り、コミットに失敗した。(なぜか文字化けしているが…)

おわりに

今回は、コミット前 lint 自動実行のために、husky と lint-staged を導入した。

次回は文字化け解消?

【Everyday Next.js】2. Linter / Formatter

ESLint

nextjs.org

create-next-app をした時点で ESLint がすでにセッティングされている。

以下のコマンドで lint を走らせることができる。

npm run lint

色々カスタマイズ方法が公式ドキュメントに載っているが、ESLint にそこまで拘りはないので多くは弄らず。

一点だけ。後述する prettier と共存させるには、eslint-config-prettier を入れた方が良いみたいなのでそれだけ追加。

npm i -D eslint-config-prettier
//  .eslintrc.json
{
  "extends": ["next/core-web-vitals", "prettier"]
}

あと、ESLint について気を付けなければいけない点が。

You can now run next lint every time you want to run ESLint to catch errors. Once ESLint has been set up, it will also automatically run during every build (next build). Errors will fail the build, while warnings will not.

build の度に自動で lint が走るとなっている。つまり、デプロイする際に lint error があるとビルドに失敗するということであり、事前に lint error が起きないかどうかのチェックが必要であるということである。

Prettier

formatter には、おなじみ prettier を使いたい。

先ほどの公式ドキュメントにも prettier の言及があり、eslint と競合しないための設定は前述の通りである。

VSCode で prettier を使いたい場合は、vscode の prettier 拡張機能を入れてあげれば OK。

github.com

.prettierrc に好みの設定を書くことでカスタマイズもできる。

私のお気に入りの設定は以下の通り。

// .prettierrc
{
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true,
  "jsxSingleQuote": true,
  "bracketSpacing": false
}

あとは、ファイル保存時に自動でフォーマットされるようにしてあげる。

// .vscode/settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
}

おわりに

今日は、Next.js に Linter と Formatter を導入した。

次回は、lint-staged あたりを導入する予定。