🛠️

[2025年] フロントエンド環境構築(React, TypeScript, Vite, Biome, lefthook)

に公開

概要

フロントエンドの環境構築を行ったのでそのメモ。自分の知識のアップデートも兼ねて。

ChatGPTには適宜相談しているが、最新の方法を確実に採用したいので、公式ドキュメントを参考にして、1つずつ手動で設定している。

コンセプト

シンプルなアプリケーションなので、Next.js のようなフレームワークは導入しない。状態管理も React 自体の機能で行う。
また、筆者はそれなりに CSS を書けるので、なるべく素の CSS を書ける環境にする。つまり TailwindCSS のようなクラスベースのスタイリングは選択しない。

環境構築

ここから環境構築を開始。

前提

まず、Github で空のレポジトリを作成。レポジトリ内には一切ファイルもディレクトリもない状態。ここからスタート。
Node.js(および npm)はインストール済み。

Vite

https://vite.dev/guide/

Vite を起点に環境を作っていく。

npm create vite@latest APP_NAME -- --template react-ts

無難に React と TypeScript を選択。APP_NAME には実際のアプリケーション名を入れる。

公式ドキュメントの通りに、依存パッケージのインストールと起動確認を行う。

npm install
npm run dev

無事に localhost で起動することを確認できた。

React Router v7

https://reactrouter.com/start/declarative/installation

Next.js を利用しないので、ルーティングのために React Router を利用する。

npm i react-router

main.tsx に以下を追加。

import { BrowserRouter } from "react-router";

  <BrowserRouter>
  </BrowserRouter>

react-router-dom は不要になった?

React Hook Form & Valibot

フォーム機能を提供するので React Hook Form を入れる。
React v19 でフォームの機能が強化されたが、それでも依然として、複雑なフォームを提供するには React Hook Form の方が良さそう。

バリデーションライブラリには割りと新しめな Valibot を採用。

npm i react-hook-form valibot @hookform/resolvers

App.tsx を以下のように書き換え、最低限のフォームが動いていることを確認。

import { useForm } from 'react-hook-form'
import { object, string, type InferOutput } from 'valibot';
import { valibotResolver } from "@hookform/resolvers/valibot";
import './App.css'

const schema = object({
  email: string(),
  password: string(),
})

type FormSchema = InferOutput<typeof schema>

function App() {
  const form = useForm<FormSchema>({
    resolver: valibotResolver(schema),
  })

  const onSubmit = form.handleSubmit((data: FormSchema) => {
    console.log(data)
  })

  return (
      <form onSubmit={onSubmit}>
        <input {...form.register('email')} />
        <input {...form.register('password')} />
        <button type="submit">Submit</button>
      </form>
  )
}

export default App

Lucide React

アイコンライブラリは入れておきたい。気に入ったものなら割りと何でも良いかなと思う。

npm install lucide-react

簡単に利用できて良い感じ。

import { Smile } from 'lucide-react';

      <Smile size={100} />

Biome

リンターとフォーマッターには Biome を使う。ESLint と Prettier がテッパンだと思いつつ、少し冒険してみる。

npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init

biome.json が自動生成されるが、少し手直しして以下のように。

{
  "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
  "vcs": {
    "enabled": false,
    "clientKind": "git",
    "useIgnoreFile": false
  },
  "files": {
    "ignoreUnknown": false,
    "ignore": []
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 120,
    "lineEnding": "lf",
    "attributePosition": "auto"
  },
  "organizeImports": {
    "enabled": true
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "javascript": {
    "formatter": {
      "jsxQuoteStyle": "double",
      "quoteProperties": "asNeeded",
      "trailingCommas": "all",
      "semicolons": "asNeeded",
      "arrowParentheses": "always",
      "bracketSpacing": true,
      "bracketSameLine": false,
      "quoteStyle": "single",
      "attributePosition": "auto"
    }
  }
}

以下のページを参考にした。
https://biomejs.dev/guides/migrate-eslint-prettier/

.vscode/settings.json がまだなかったので作成。

{
  "editor.formatOnSave": true,
  "[javascript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[css]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "editor.codeActionsOnSave": {
    "source.fixAll.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

settings.json が .gitignore されていたので、git 管理されるよう修正。

VSCode 拡張を入れる。
https://biomejs.dev/guides/editors/first-party-extensions/

この時点で、ファイル保存するとフォーマッターが動くようになっているはず。

vite が入れたっぽい eslint 系は消してしまう。だいぶ依存性がシンプルになった。

npm uninstall eslint eslint-plugin-react-hooks eslint-plugin-react-refresh typescript-eslint @eslint/js

Git Hooks

Git Hooks の設定をしてしまう。

https://biomejs.dev/recipes/git-hooks/

npm install lefthook --save-dev

lefthook.yml は以下の通り。git commit 時にフォーマットさせる。

pre-commit:
  commands:
    check:
      glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,css}"
      run: npx @biomejs/biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
      stage_fixed: true

Husky/lint-staged より簡単で良い。

CSS Module

スタイリングは CSS Module で行う。vite なら特別な設定なしにそのまま使える。
Theme 管理やダークモード実装は、CSS 変数などを駆使して自前でつくる。

Vitest

https://vitest.dev/guide/

テストには visest を使う。実行速度が速いので。
ちょっと複雑なロジックなどは、しっかりとユニットテストを書いておきたい。

npm install -D vitest

package.json にテストのコマンドを追加。

    "test": "vitest",

コンポーネントテストの予定はなし。

まとめ

ここまでで、それなりにモダンな SPA 開発はできるようになったと思う。あとはサービスの特性に応じて、追加でパッケージを入れればOK。

最終的な package.json 内の依存は以下のようになっている。

  "dependencies": {
    "@hookform/resolvers": "^5.0.1",
    "lucide-react": "^0.513.0",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "react-hook-form": "^7.57.0",
    "react-router": "^7.6.2",
    "valibot": "^1.1.0"
  },
  "devDependencies": {
    "@biomejs/biome": "1.9.4",
    "@types/react": "^19.1.2",
    "@types/react-dom": "^19.1.2",
    "@vitejs/plugin-react": "^4.4.1",
    "globals": "^16.0.0",
    "lefthook": "^1.11.13",
    "typescript": "~5.8.3",
    "vite": "^6.3.5",
    "vitest": "^3.2.1"
  }

Discussion