※本記事には広告(楽天アフィリエイト)リンクが含まれる場合があります。
フロントエンド・バックエンド・共通ライブラリをそれぞれ別リポジトリで管理していると、「型定義を変えたらすべてのリポジトリでPRを出し直し」「共通コンポーネントのバージョン合わせが地獄」といった問題に直面します。モノレポで1つにまとめると解決しますが、今度はビルドが遅くなるのでは?というのが次の不安です。
その答えが Turborepo です。Vercel が開発するビルドシステムで、「変更していないものは再ビルドしない」というキャッシュ戦略で、大規模なモノレポでも爆速ビルドを実現します。2026年現在、Next.js・Svelte・Honoといった主要フレームワークの公式テンプレートでも採用されており、個人開発から企業規模まで幅広く使われています。
この記事では、Turborepoをゼロから学んで使いこなすための知識を、実際のコードとともに解説します。
モノレポとは?ポリレポとの違い
まず用語を整理しておきます。
| 方式 | 特徴 | 代表例 |
|---|---|---|
| ポリレポ | アプリ・ライブラリごとに独立したリポジトリ | 従来の多くのプロジェクト |
| モノレポ | 複数のパッケージを1リポジトリで管理 | Next.js, Vercel, Googleなど |
モノレポのメリットは大きいです。
- コード共有が楽 — 共通の型定義・ユーティリティを即座に参照できる
- 一貫したCI/CD — 1つのパイプラインで全体のテスト・デプロイを管理
- 一発リファクタリング — 関数名変更が関連パッケージすべてに波及するかすぐ確認できる
- 依存バージョンの統一 — Reactを2つのアプリで別バージョン使うミスがなくなる
デメリットは「リポジトリが肥大化するとビルドが遅くなる」こと。これをTurborepoが解決します。
Turborepoの仕組み:なぜ速いのか
Turborepoの速さの源泉は2つです。
1. タスクの並列実行
パッケージ間の依存グラフを解析し、依存関係のないタスクを並列実行します。たとえば apps/web と apps/admin が独立していれば、両方のビルドを同時に走らせます。
2. コンテンツハッシュベースのキャッシュ
ソースコード・依存パッケージ・環境変数をもとにハッシュを計算し、前回と同じハッシュならビルドをスキップしてキャッシュから結果を返します。「変えていないものは絶対に再ビルドしない」という原則です。
さらに Remote Cache(Vercel提供)を使えば、ハッシュをクラウドで共有できます。あなたのPCでビルドした結果をチームメンバーのPCやCIが再利用できるため、CIの初回ビルドでも「すでにビルド済み」が多発して劇的に速くなります。
セットアップ:新規プロジェクトの作成
最も簡単な始め方は公式CLIを使うことです。
npx create-turbo@latest my-monorepo
cd my-monorepo
パッケージマネージャーを選ぶプロンプトが出ます。2026年の個人開発では pnpm がデファクトなのでpnpmを選ぶのがおすすめです。
生成されるディレクトリ構造はこうなります。
my-monorepo/
├── apps/
│ ├── web/ # Next.jsアプリ
│ └── docs/ # Next.jsドキュメントサイト
├── packages/
│ ├── ui/ # 共通UIコンポーネント
│ ├── eslint-config/
│ └── typescript-config/
├── turbo.json # Turborepo設定
├── package.json # ルートのpackage.json
└── pnpm-workspace.yaml
turbo.json:タスクパイプラインの設定
Turborepoの核心は turbo.json です。どのタスクをどの順番で実行するかを定義します。
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"dev": {
"cache": false,
"persistent": true
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
}
}
}
ポイントを解説します。
"dependsOn": ["^build"]—^は「依存パッケージのbuildを先に実行」という意味。packages/uiのビルドが完了してからapps/webのビルドを開始する。"outputs"— キャッシュする対象ファイル。ここに指定したファイルがキャッシュに保存・復元される。"cache": false— devサーバーはキャッシュを使わない(常に起動する)。"persistent": true— 長時間実行のプロセス(devサーバーなど)を示すフラグ。
pnpm workspacesとの連携
Turborepoはパッケージ管理そのものはせず、npmやpnpmのworkspacesに乗っかります。pnpmの場合は pnpm-workspace.yaml でパッケージの場所を定義します。
packages:
- "apps/*"
- "packages/*"
各パッケージの package.json でワークスペース内パッケージを参照するときは workspace:* を使います。
// apps/web/package.json
{
"name": "web",
"dependencies": {
"@my-monorepo/ui": "workspace:*"
}
}
これで packages/ui の変更がビルド済みパッケージを経由せずにそのまま apps/web に反映されます。
よく使うコマンド
# 全パッケージをビルド
pnpm turbo build
# 全パッケージのdevサーバーを起動
pnpm turbo dev
# 特定パッケージだけ実行(--filterフラグ)
pnpm turbo build --filter=web
# 依存パッケージも含めてフィルタ
pnpm turbo build --filter=web...
# キャッシュを無視して強制再ビルド
pnpm turbo build --force
# 実行するタスクのグラフを確認
pnpm turbo build --graph
--filter フラグは非常に強力で、変更されたパッケージだけを実行する --filter=[HEAD^1] のような使い方もできます。
共通パッケージの作り方:packages/uiの例
実際に共通UIコンポーネントを作ってみましょう。
// packages/ui/src/button.tsx
import type { ButtonHTMLAttributes, ReactNode } from "react";
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: ReactNode;
variant?: "primary" | "secondary";
}
export function Button({ children, variant = "primary", ...props }: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
{...props}
>
{children}
</button>
);
}
// packages/ui/package.json
{
"name": "@my-monorepo/ui",
"version": "0.0.1",
"exports": {
".": {
"import": "./src/index.tsx",
"types": "./src/index.tsx"
}
},
"devDependencies": {
"typescript": "^5.x",
"@types/react": "^18.x"
},
"peerDependencies": {
"react": "^18.x || ^19.x"
}
}
TypeScriptでのモノレポでは、packages/typescript-config にベースの tsconfig.json を置いて、各パッケージから extends する形がスタンダードです。
// packages/typescript-config/base.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"moduleResolution": "bundler",
"module": "ESNext",
"target": "ES2022",
"jsx": "react-jsx"
}
}
// packages/ui/tsconfig.json
{
"extends": "@my-monorepo/typescript-config/base.json",
"include": ["src"]
}
Remote Cache:チームとCIでキャッシュを共有する
Turborepoの最大の目玉機能がRemote Cacheです。ローカルのキャッシュをクラウドにアップロードすることで、チームメンバーやCIサーバーとキャッシュを共有できます。
Vercel Remote Cacheのセットアップ
# Vercelにログイン
npx turbo login
# リモートキャッシュを有効化(Vercelチームと連携)
npx turbo link
これだけです。以降、turbo build を実行するとVercelのキャッシュサーバーに結果がアップロードされ、チームメンバーのPCやGitHub ActionsのCIがそのキャッシュを再利用します。
セルフホストのRemote Cache
Vercel以外のリモートキャッシュサーバーも使えます。ducktape や turborepo-remote-cache といったOSSを使って自前でホストする方法です。Cloudflare R2 + Workersで構築することもできます。
// turbo.json(セルフホストの場合)
{
"remoteCache": {
"apiUrl": "https://your-cache-server.example.com"
}
}
GitHub Actions CI/CDとの統合
TurborepoはCI/CDとの相性が特に良いです。以下はNext.jsモノレポの典型的なGitHub Actionsワークフローです。
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- run: pnpm install --frozen-lockfile
# Remote Cacheを使うためのトークン設定
- name: Build
run: pnpm turbo build lint test
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TOKEN と TURBO_TEAM はVercelのダッシュボードで取得してGitHub Secretsに設定します。これによりCIがローカルのビルドキャッシュを再利用し、変更のなかったパッケージのビルドをまるごとスキップします。
実践的なTips
1. package.jsonのscriptsでturboを呼ぶ
ルートの package.json にショートカットを作っておくと便利です。
{
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"test": "turbo test",
"format": "prettier --write \"**/*.{ts,tsx,md}\""
}
}
2. 環境変数のハッシュ化
ビルドに影響する環境変数は turbo.json の env に明示します。これを忘れると「環境変数を変えたのにキャッシュが使われて古い成果物が出る」という罠にはまります。
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"env": ["NEXT_PUBLIC_API_URL", "NODE_ENV"],
"outputs": [".next/**"]
}
}
}
3. パッケージを追加するときのチェックリスト
packages/またはapps/配下にディレクトリを作るpackage.jsonに名前(@my-monorepo/xxx形式推奨)を設定tsconfig.jsonで共通configを extends する- 他パッケージから使うなら
exportsを設定 - ルートから
pnpm installして依存を解決
4. コードジェネレーターで雛形作成
Turborepoにはパッケージの雛形を生成するgeneratorが組み込まれています(turbo gen)。カスタムgeneratorを作ってチームの標準構成を自動生成することもできます。
# 公式のgeneratorを使って新パッケージを作成
pnpm turbo gen workspace --name @my-monorepo/new-package
モノレポ移行のステップ
既存のポリレポをモノレポに移行する場合のステップを紹介します。
- ルートリポジトリを作成 — 新しいGitHubリポジトリを用意し、
pnpm-workspace.yamlを置く - 既存リポジトリを移植 —
git subtree addや単純なコピーでファイルを移動 - turbo.jsonを設定 — まず既存の
npm run buildが動くことを確認してから並列化・キャッシュを設定 - 共通コードをpackagesに抽出 — まず動くことを優先し、共通化は後回しでよい
- CIを移行 — Remote Cacheのトークンを設定し、CIでも速度改善を確認
一気にやろうとせず、まず「ビルドが通ること」を目標にするのが成功のコツです。
NxやRush、Lerna(v7)との比較
| ツール | 設定の簡単さ | Remote Cache | 学習コスト | 向いている規模 |
|---|---|---|---|---|
| Turborepo | ◎ シンプル | ◎ Vercel統合 | 低 | 個人〜中規模 |
| Nx | ○ 豊富な設定 | ○ Nx Cloud | 中〜高 | 中〜大規模 |
| Rush | △ 複雑 | ○ | 高 | 大規模企業 |
| Lerna v7 | ○ | △(Nx統合) | 低〜中 | パッケージ公開メイン |
個人開発や小〜中規模チームには Turborepo一択 と言えます。設定がシンプルで、Vercelへのデプロイとの親和性が高いのが最大の強みです。大規模なエンタープライズ開発でタスクグラフの細かい制御が必要ならNxも検討に値します。
まとめ
Turborepoを使うと、モノレポの複雑さを抑えたまま、高速ビルドと優れた開発体験を両立できます。
- コンテンツハッシュキャッシュで変更のないパッケージはスキップ
- 並列実行で独立タスクを同時に走らせる
- Remote CacheでCIとローカルのキャッシュを共有
turbo.jsonはシンプルで学習コストが低い
「複数アプリを1リポジトリで管理したい」「CIのビルドが遅い」「共通コードを分離したい」——このどれかに当てはまるなら、今すぐTurborepoを試してみてください。npx create-turbo@latest の1コマンドから始まります。