Redux Store Setup 2026: Production RTK Configuration 🚀
Redux Toolkit (RTK) provides zero-boilerplate store setup with TypeScript-first slices, RTK Query caching, DevTools time-travel, middleware extensibility, and automatic code-splitting. Modern stores combine client state (auth, UI) with server state (RTK Query) and support React 19 Server Components hydration.
🎯 Production Store Architecture
store/ ├── index.ts # Root store config ├── slices/ # Client state (auth, ui) │ ├── authSlice.ts │ └── uiSlice.ts ├── services/ # RTK Query APIs │ ├── authApi.ts │ └── todoApi.ts └── middleware/ # Custom middleware ├── authInterceptor.ts └── logger.ts
🚀 Step-by-Step Production Setup
Step 1: Install Production Dependencies
# Core RTK + React integration
npm install @reduxjs/toolkit react-redux
# RTK Query (server state)
npm install @reduxjs/toolkit
# DevTools + Persistence (optional)
npm install redux-persist @redux-devtools/cli
npm install -D @types/redux-persist
Step 2: Root Store Configuration
// store/index.ts - Production store
import { configureStore } from '@reduxjs/toolkit';
import { setupListeners } from '@reduxjs/toolkit/query';
import { persistReducer, persistStore } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import authSlice from './slices/authSlice';
import uiSlice from './slices/uiSlice';
import { authApi } from './services/authApi';
import { todoApi } from './services/todoApi';
import { authInterceptor } from './middleware/authInterceptor';
const persistConfig = {
key: 'root',
storage,
whitelist: ['auth'] // Only persist auth
};
const rootReducer = {
auth: persistReducer(persistConfig, authSlice),
ui: uiSlice,
[authApi.reducerPath]: authApi.reducer,
[todoApi.reducerPath]: todoApi.reducer
};
export const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST']
}
})
.concat(authApi.middleware, todoApi.middleware)
.concat(authInterceptor),
devTools: process.env.NODE_ENV !== 'production',
// Code-splitting support
preloadedState: (window as any).__PRELOADED_STATE__
});
setupListeners(store.dispatch); // Auto-refetch on reconnect
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Step 3: React Provider (With Persistence)
// providers/ReduxProvider.tsx
'use client';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from '@/store';
import { StrictMode } from 'react';
export function ReduxProvider({ children }: { children: React.ReactNode }) {
return (
<Provider store={store}>
<PersistGate loading={<Spinner />} persistor={persistor}>
<StrictMode>{children}</StrictMode>
</PersistGate>
</Provider>
);
}
Step 4: Typed Hooks
// hooks/useAppDispatch.ts
import { useDispatch } from 'react-redux';
import type { AppDispatch } from '@/store';
export const useAppDispatch = () => useDispatch<AppDispatch>();
// hooks/useAppSelector.ts
import { useSelector } from 'react-redux';
import type { RootState } from '@/store';
export const useAppSelector = <TSelected>(
selector: (state: RootState) => TSelected
) => useSelector<RootState, TSelected>(selector);
🛠️ Example Slices + RTK Query
Auth Slice (Client State)
// store/slices/authSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { logout } from '../authActions';
interface AuthState {
user: User | null;
token: string | null;
}
const initialState: AuthState = {
user: null,
token: null
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setCredentials: (state, action: PayloadAction<{ user: User; token: string }>) => {
state.user = action.payload.user;
state.token = action.payload.token;
}
},
extraReducers: (builder) => {
builder.addCase(logout, (state) => {
state.user = null;
state.token = null;
});
}
});
export const { setCredentials } = authSlice.actions;
export default authSlice.reducer;
RTK Query API
// store/services/todoApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import type { RootState } from '../index';
export const todoApi = createApi({
reducerPath: 'todoApi',
baseQuery: fetchBaseQuery({
baseUrl: '/api/',
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token;
if (token) {
headers.set('authorization', `Bearer ${token}`);
}
return headers;
}
}),
tagTypes: ['Todo'],
endpoints: (builder) => ({
getTodos: builder.query<Todo[], void>({
query: () => 'todos',
providesTags: ['Todo']
}),
addTodo: builder.mutation<Todo, Partial<Todo>>({
query: (todo) => ({
url: 'todos',
method: 'POST',
body: todo
}),
invalidatesTags: ['Todo']
})
})
});
export const { useGetTodosQuery, useAddTodoMutation } = todoApi;
🎯 Production Features
1. Code-Splitting (Server-Side Rendering)
// next.config.js
export default {
// Preload initial state for SSR
generateEtags: false
};
// pages/_app.tsx
if (typeof window !== 'undefined') {
(window as any).__PRELOADED_STATE__ = initialReduxState;
}
2. Auth Interceptor Middleware
// middleware/authInterceptor.ts
import { Middleware } from '@reduxjs/toolkit/query';
import type { RootState } from '../store';
export const authInterceptor: Middleware = (store) => (next) => (action) => {
if (todoApi.util.resetApiState.match(action)) return next(action);
const token = (store.getState() as RootState).auth.token;
if (token && 'meta' in action && action.meta.request) {
action.meta.request.headers = {
...action.meta.request.headers,
Authorization: `Bearer ${token}`
};
}
return next(action);
};
📊 Store Performance Metrics
| Feature | Vanilla Redux | Redux Toolkit |
|---|---|---|
| Bundle Size | 12KB | 2.1KB |
| Setup Time | 2 hours | 5 minutes |
| TypeScript | Manual | Built-in |
| DevTools | Plugin | Native |
| Caching | Manual | RTK Query |
🎯 Production Checklist
✅ [] configureStore with TypeScript generics ✅ [] RTK Query for server state (90% use cases) ✅ [] Persistence (redux-persist) for auth ✅ [] Auth interceptor middleware ✅ [] DevTools time-travel (non-production) ✅ [] Typed hooks (useAppDispatch/useAppSelector) ✅ [] Code-splitting support (PRELOADED_STATE) ✅ [] ESLint + Prettier configured
🚀 Complete App Integration
// app/layout.tsx (Next.js 15 App Router)
export default function RootLayout({
children
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<ReduxProvider>
{children}
</ReduxProvider>
</body>
</html>
);
}
// components/TodoApp.tsx
function TodoApp() {
const { data: todos = [] } = useGetTodosQuery();
const [addTodo] = useAddTodoMutation();
return (
<div className="p-8">
<TodoForm onSubmit={addTodo} />
<TodoList todos={todos} />
</div>
);
}
🎯 Final Thoughts
Redux Toolkit store setup = 5 minutes to production. RTK Query handles server state, createSlice eliminates boilerplate, middleware adds enterprise features, and TypeScript generics ensure type safety at scale.
2026 State Architecture: Server Components → Initial data (60%) RTK Query → Cached mutations (30%) Slices → Client state (10%)
Vanilla Redux = 2018. RTK Production Store = 2026. Build enterprise React apps with predictable state and automatic caching 🚀.