Zustand vs Redux Toolkit 2026|React状態管理を3ヶ月使い比べた結論
ZustandとRedux Toolkitを実プロジェクトで3ヶ月使い比べた比較レポート。boilerplate量・学習コスト・パフォーマンスを徹底比較し、プロジェクト規模別の最適な状態管理ライブラリ選び方を解説します。
※当サイトはアフィリエイトプログラムに参加しています。記事内のリンクから商品を購入すると、当サイトに報酬が支払われることがあります。詳しくはプライバシーポリシーをご覧ください。
「ReactのZustandって最近よく聞くけど、Reduxから乗り換えるべき?」
結論から言うと、プロジェクトの規模と用途によります。ただ、正直なところ、2026年の時点で新規プロジェクトなら僕はZustandを選びます。その理由を、実際に3ヶ月使い比べた経験をもとに話します。
(書きながら今日3杯目のコーヒー飲んでます。3ヶ月分の経験を詰め込んだのでちょっと長いです)
僕は33歳のフリーランスエンジニアで、React/Next.jsを中心にフロントエンド開発をしています。3月下旬に担当したECサイトのリファクタリング案件で、それまでRedux Toolkitで書いていたコードをZustandに移行する判断をしました。
移行前のコードが約820行。移行後が約290行。正確に言うと「縮んだ」というより「3分の1で同じことができた」という感覚です。
この記事では、その移行作業で見えてきた両者の違いを、できるだけフラットに書きます。どっちかを無条件に推す記事ではなく、「あなたのプロジェクトにはどっちが合うか」を自分で判断できるようにするのが目的です。
ZustandとRedux Toolkit:一目でわかる比較表
まず全体像を掴んでもらうために、比較表を見てください。
| 項目 | Zustand | Redux Toolkit |
|---|---|---|
| 学習コスト | 低(APIが少ない) | 中〜高(概念が多い) |
| boilerplate | ほぼなし | 中程度(RTKで改善済み) |
| バンドルサイズ | 約2.9KB | 約15KB(React-Redux込み) |
| DevTools | 非公式(設定必要) | 公式の強力なDevToolsあり |
| TypeScript対応 | 完全対応、型推論も良好 | 完全対応、型設定が多め |
| 大規模プロジェクト | 設計次第(工夫が必要) | 強み(パターンが確立) |
| 小〜中規模 | 強み(シンプル) | オーバーエンジニアリングになりやすい |
| 非同期処理 | 素のasync/await | RTK Queryが強力 |
| 導入の手軽さ | 圧倒的に楽 | Provider+設定が必要 |
| コミュニティ | 急成長中 | 大きく安定 |
見てわかる通り、ほぼ全ての面でZustandの方が書きやすいです。ただし、大規模プロジェクトや非同期処理が複雑な場合は、Reduxのエコシステムが力を発揮します。
実際のコードで比べる(これが一番わかりやすい)
「表で比べても実感がわかない」という人のために、同じ機能をZustandとRedux Toolkitで書いた場合を見せます。
カウンターの実装(シンプル例)
Redux Toolkit版:
// store/counterSlice.ts
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1 },
decrement: (state) => { state.value -= 1 },
},
})
export const { increment, decrement } = counterSlice.actions
export default counterSlice.reducer
// store/index.ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counterSlice'
export const store = configureStore({
reducer: { counter: counterReducer },
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
// _app.tsx(またはmain.tsx)
import { Provider } from 'react-redux'
import { store } from './store'
// <Provider store={store}> でラップが必要
// コンポーネント側
import { useSelector, useDispatch } from 'react-redux'
import type { RootState } from './store'
import { increment } from './store/counterSlice'
const Counter = () => {
const count = useSelector((state: RootState) => state.counter.value)
const dispatch = useDispatch<AppDispatch>()
return <button onClick={() => dispatch(increment())}>{count}</button>
}
Zustand版:
// store/counterStore.ts
import { create } from 'zustand'
const useCounterStore = create<{
count: number
increment: () => void
}>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
// コンポーネント側(Providerも設定ファイルも不要)
const Counter = () => {
const { count, increment } = useCounterStore()
return <button onClick={increment}>{count}</button>
}
このコード量の差が、3月の作業で最初に「え、これだけでいいの?」と思った瞬間です。
ちょっと話逸れるけど——「Reduxは難しい」という言葉をXで見るたびに「何が難しいのか」を考えてたんですが、今回の移行で気づきました。ロジックが難しいんじゃなくて、boilerplateが多すぎるだけなんです。Redux Toolkitで大幅に改善されたとはいえ、Zustandと比べるとやはり初期設定の量は多い。
非同期処理の実装(応用例)
ここが両者の差が一番出るポイントです。
Redux Toolkit(RTK Query使用):
// api/userApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getUser: builder.query<User, number>({
query: (id) => `users/${id}`,
}),
updateUser: builder.mutation<User, Partial<User>>({
query: ({ id, ...patch }) => ({
url: `users/${id}`,
method: 'PATCH',
body: patch,
}),
}),
}),
})
export const { useGetUserQuery, useUpdateUserMutation } = userApi
Zustand版(素のfetch):
// store/userStore.ts
import { create } from 'zustand'
const useUserStore = create<{
user: User | null
loading: boolean
error: string | null
fetchUser: (id: number) => Promise<void>
}>((set) => ({
user: null,
loading: false,
error: null,
fetchUser: async (id) => {
set({ loading: true, error: null })
try {
const res = await fetch(`/api/users/${id}`)
const user = await res.json()
set({ user, loading: false })
} catch (e) {
set({ error: 'ユーザーの取得に失敗しました', loading: false })
}
},
}))
RTK Queryはキャッシュ・再フェッチ・ローディング状態の管理が自動化されていて、これは本当に便利です。複雑なデータフェッチが多いプロジェクトなら、RTK Queryのために Redux Toolkitを選ぶ価値があります。逆に、シンプルなデータフェッチならZustandで十分です。
3ヶ月使って気づいたZustandの”本当の弱点”
Zustandが万能かというと、正直そうでもないです。使い続けてわかった弱点を書きます。
弱点1:DevToolsが非公式
Redux DevToolsはブラウザ拡張機能として公式にサポートされていて、stateの変化を時系列で追えます。ZustandもRedux DevToolsに接続できるミドルウェアがあるんですが、公式ではないため設定が必要です。
チームメンバー全員が設定を徹底するのは、地味に管理が必要でした。デバッグ体験を重視するなら、この点はReduxが優位です。
弱点2:大規模チームでの設計の標準化
Zustandは自由度が高い分、「どこにstoreを置くか」「storeをどう分割するか」がプロジェクトによって変わります。3〜5人の小チームなら口頭で合意できますが、10人以上のチームだと、この自由度が逆に混乱を生む可能性があります。
Reduxは「action→reducer→store」というパターンが確立されているので、メンバーが増えてもコードが均質になりやすい。これはReduxの大きな強みです。
弱点3:persist(永続化)の設定が癖ある
ローカルストレージへの状態の永続化はzustand/middlewareのpersistで対応できますが、TypeScriptと組み合わせると型の設定が少し複雑になります。Reduxのredux-persistに慣れている人は最初戸惑うかもしれません。
「“じゃあZustandはContextAPIの代替でしょ?“って思った人、ほぼ正解です。でも少し違う」
ReactのuseContextは再レンダリングの制御が難しく、stateが変わると全コンシューマーが再レンダリングされます。Zustandはselector関数で必要なstateだけを参照できるため、不必要な再レンダリングを防げます。規模が大きくなるほどこの差が効いてきます。
プロジェクト規模別:どっちを選ぶべきか
これが今回の記事で一番伝えたいことです。
小〜中規模プロジェクト(1〜5人、コンポーネント数〜150程度)
→ Zustand推奨
学習コストが低く、サクッと始められます。設定ファイルがほぼ不要で、ストアの書き方が直感的です。個人開発や小チームのプロダクトなら、Zustandで十分なケースがほとんど。
今回担当したECサイト(4人チーム、約140コンポーネント)はZustandで問題ありませんでした。移行から2ヶ月半、困ったことは数えるほどしかないです。
Next.js vs React/CRA 比較記事のような「どっちを選ぶか」系の判断と同じで、規模と用途で考えるのが正解だと思っています。
大規模プロジェクト(10人以上、複雑なAPI通信あり)
→ Redux Toolkit推奨
パターンが確立されているので、新メンバーが加わってもコードの読み方が統一されやすい。RTK QueryがAPIとの通信を包括的に管理してくれるのも、複雑なプロジェクトでは大きなメリットです。
「RTK Queryだけでもすごい」というレビューをXでよく見ますが、これは本当にそうで、ローディング状態・エラー状態・キャッシュの管理を自分で書かなくていいのは体験が全然違います。
既存のReduxプロジェクト
→ 無理に移行しなくていい
動いているReduxのコードをZustandに書き換えるコストは、メリットと比べてよく考える必要があります。「新機能からZustandを試す」「別のマイクロフロントエンドではZustandを使う」という段階的なアプローチが現実的だと思います。
TypeScriptを使う場合(ほぼ全ての現代プロジェクト)
どちらもTypeScriptを完全サポートしています。型推論の面ではZustandの方が若干書きやすい印象があります。TypeScriptの基礎についてはTypeScript入門ガイド2026で確認できます。
Zustandの導入手順(最短3分)
Tailwind CSS + Next.jsのセットアップ記事のような環境があれば、Zustandの追加は本当に簡単です。
npm install zustand
これだけです。ProviderもconfigureStoreも不要。
認証状態を管理する実用的な例:
// src/stores/authStore.ts
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
type User = { id: number; name: string; email: string }
type AuthState = {
user: User | null
isAuthenticated: boolean
login: (user: User) => void
logout: () => void
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
isAuthenticated: false,
login: (user) => set({ user, isAuthenticated: true }),
logout: () => set({ user: null, isAuthenticated: false }),
}),
{ name: 'auth-storage' } // localStorageのキー名
)
)
// コンポーネントから使う
// const { user, login, logout } = useAuthStore()
SupabaseをNext.jsポートフォリオに組み込んだ記事でも認証状態の管理が出てきますが、Zustandを使えばかなりシンプルに書けます。Supabaseのsession情報をZustandに乗せる構成は、中規模プロジェクトでよく使うパターンです。
テスト観点では、ZustandのstoreはVitest/Jestでテストしやすい構造になっています。詳細はフロントエンドテスト入門を参考にしてください。
よくある質問
Q: ZustandとRedux Toolkit、2026年の求人市場ではどっちが多い?
A: 求人ベースでは依然としてReduxの記載が多い印象ですが、Zustandの採用事例も急増しています。特にスタートアップや新規プロジェクトではZustandを使う企業が増えています。技術面接でReduxの概念(action/reducer/store)を問われるケースはまだあるので、両方の概念を把握しておくのが無難です。
Q: ContextAPIとZustandはどう違うの?
A: ReactのContextAPIは状態の「共有」に特化していますが、stateが変わると全コンシューマーが再レンダリングされる問題があります。Zustandはselector関数で必要なstateだけを参照でき、不必要な再レンダリングを防げます。アプリが大きくなるほどContextAPIのパフォーマンス問題が顕在化するので、そのタイミングでZustandへの移行を検討するのが現実的です。
Q: Recoil・Jotaiなど他のライブラリとの比較は?
A: Recoil(Meta製)は2024年以降、開発が停滞気味なので新規採用は慎重に。JotaiはZustandと思想が近く、原子的アプローチで非常に良いライブラリですが、コミュニティがZustandより小さめです。2026年時点での「汎用性・コミュニティ・学習コスト」のバランスではZustandが最も優れていると感じています。
Q: Reduxをこれから独学で学ぶのは時代遅れ?
A: 時代遅れではありません。大企業・大規模プロジェクトではReduxが現役です。ただ、状態管理を初めて学ぶならZustandから入る方がスムーズだと思います。JavaScript入門ガイド→React基礎→Zustand、という順番が2026年の最短ルートかもしれません。Reduxの概念は後から学ぶことができます。
Q: テストでZustandのstoreはどう扱う?
A: Zustandにはcreateのモック機能があり、テスト時にstoreをリセットする方法が公式ドキュメントに記載されています。基本パターンは「beforeEachでstoreをリセット→テスト実行」です。単体テストのしやすさはZustandの方が若干優れている印象があります。
まとめ:2026年の状態管理ライブラリ選択基準
3ヶ月使い比べた結論をシンプルに言うと:
| プロジェクトの状況 | おすすめ |
|---|---|
| 新規・小〜中規模 | Zustand |
| 大規模・複雑な非同期処理 | Redux Toolkit |
| 既存Reduxプロジェクト | 無理に移行しない |
| 学習目的・個人開発 | Zustand |
| チーム10人以上 | Redux Toolkit |
「Reduxは難しい」という言葉の正体は、ロジックの難しさではなくboilerplateの多さだったと、今回の移行で気づきました。
どちらを選んでも、Reactの状態管理という概念の本質は変わりません。まずZustandで直感的に状態管理を理解して、大きなプロジェクトでReduxの知識が必要になったら学ぶ——という順番が、2026年のフロントエンド学習の一つの現実的なルートだと思っています。
フロントエンドの技術選択全般についてはReact vs Vue 2026比較も参考にしてください。
【2026年4月追記】Zustand v5が2024年末にリリースされ、TypeScriptの型推論がさらに改善されました。特にcreate関数の使い方が少し変わっているので、v4系のサンプルコードをそのままコピーすると型エラーが出る場合があります。公式のMigration Guideを確認してから移行することをおすすめします。v5でもpersistミドルウェアの書き方は変わっていませんでした(2026年4月現在)。