Utilizing localStorage
to manage initial state in a Redux Toolkit slice within a Next.js TypeScript application can enhance user experience by retaining state across sessions. However, managing this involves ensuring access to localStorage
strictly on the client side and integrating it with Redux Toolkit’s setup.
Below I will provide a step-by-step example of how to create a Redux slice with initial state sourced from localStorage
, which is especially useful for settings, user preferences, or session data.
Step 1: Set Up Redux Toolkit in Next.js
First, install Redux Toolkit and React Redux if you haven’t already:
npm install @reduxjs/toolkit react-redux
Step 2: Create the Store
Create a simple Redux store in your Next.js app. Here’s a basic setup:
// store.ts
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './slices/userSlice';
const store = configureStore({
reducer: {
user: userReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
Step 3: Create a Redux Slice with localStorage
for Initial State
We will use a user authentication state as an example. We store the user’s authentication state in localStorage
so that it persists across sessions.
// slices/userSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface UserState {
isAuthenticated: boolean;
}
// Attempt to retrieve the state from localStorage
const loadState = (): UserState => {
try {
const serializedState = localStorage.getItem('userState');
if (serializedState === null) {
return { isAuthenticated: false };
}
return JSON.parse(serializedState);
} catch (err) {
return { isAuthenticated: false };
}
};
const initialState: UserState = loadState();
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
logIn(state) {
state.isAuthenticated = true;
localStorage.setItem('userState', JSON.stringify(state));
},
logOut(state) {
state.isAuthenticated = false;
localStorage.setItem('userState', JSON.stringify(state));
},
},
});
export const { logIn, logOut } = userSlice.actions;
export default userSlice.reducer;
This slice contains two actions: logIn
and logOut
. Notice also that the initial state is set by attempting to load the state from localStorage
using the loadState
function. The state is updated in localStorage
whenever actions are dispatched.
Step 4: Handling Server-Side Usage
Since localStorage
is not accessible during server-side rendering, care must be taken only to interact with localStorage
during client-side execution.
The given example already abstracts the localStorage
logic safely by keeping it in action creators and default state initialization (which will only run client-side during hydration).
Step 5: Integrate Store with Next.js
Make sure to wrap your application in the Provider
from react-redux
to allow React components to access the Redux store:
// pages/_app.tsx
import { AppProps } from 'next/app';
import { Provider } from 'react-redux';
import store from '../store';
const MyApp = ({ Component, pageProps }: AppProps) => (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
export default MyApp;
Conclusion
This setup ensures that sensitive operations involving localStorage
are handled client-side, integrating neatly into Redux Toolkit’s framework and providing a persistence layer for your application’s state. Always test thoroughly to ensure no server-side code attempts to access localStorage
, as it will cause errors during rendering.