Intro
NextJs 13 was released with some game-changing features like server-side components, a router, improved TS support, and more. So this might require a few changes in how we write our code especially when using client libraries like Redux.
Here is a quick and simple guide to get started with Nextjs 13 and Redux.
Get Started
Create a NextJs 13 application:
batch
npx create-next-app@latest
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use src/
directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias? No / Yes
What import alias would you like configured? @/
Then we install the Redux toolkit and React Redux
batch
npm i @reduxjs/toolkit react-redux
Folder Structure
Normally Redux app would include an app folder with a sub-folder called features. Instead, we are going to create a redux folder and inside a features folder
Inside the redux folder create a store.ts.
typescript
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: {},
});
Should look like this:
Slices
Inside the features folder, we will be saving our redux slices.
Inside the features folder, we will add a todo-slice.ts file:
typescript
import { createSlice } from "@reduxjs/toolkit";
type Todo = {
id: number;
name: string;
done: boolean;
};
type TodoState = {
list: Todo[];
user: string;
};
const initialState: TodoState = {
list: [],
user: "todo",
};
export const todo = createSlice({
name: "todo",
initialState,
reducers: {
addTodo: (state, action) => {
state.list.push(action.payload);
},
removeTodo: (state, action) => {
state.list = state.list.filter((todo) => todo.id !== action.payload);
},
toggleTodo: (state, action) => {
const todo = state.list.find((todo) => todo.id === action.payload);
if (todo) {
todo.done = !todo.done;
}
},
},
});
export const { addTodo, removeTodo, toggleTodo } = todo.actions;
export default todo.reducer;
Store update
Now we need to update the store.ts file to add the reducer and add the RootState and our AddDispatch types to our TS types are always up to date with our slices.
import { configureStore } from "@reduxjs/toolkit";
import todoReducer from "./features/todo-slice";
export const store = configureStore({
reducer: {
todoReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Redux Provider
We need to create the redux provider now, so let's create a provider.tsx inside the redux folder. On top of the file make sure to add the 'use client'
string to tell NextJs this is a client component since all components are server components by default.
typescript
"use client";
import React from "react";
import { Provider } from "react-redux";
import { store } from "./store";
function ReduxProvider({ children }: { children: React.ReactNode }) {
return <Provider store={store}>{children}</Provider>;
}
export default ReduxProvider;
Now we need to wrap our provider around our whole app. To do so we will need to change our root layout.tsx file to this.
import ReduxProvider from "@/redux/provider";
import "./globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<ReduxProvider>{children}</ReduxProvider>
</body>
</html>
);
}
Use Redux
Now let's create a client component that read from the redux store and dispatch events:
"use client";
import { addTodo, removeTodo, toggleTodo } from "@/redux/features/todo-slice";
import { AppDispatch, RootState } from "@/redux/store";
import React from "react";
import { useDispatch, useSelector } from "react-redux";
function TodoList() {
const todoList = useSelector((state: RootState) => state.todoReducer.list);
const dispatch = useDispatch<AppDispatch>();
const [todo, setTodo] = React.useState("");
const handleSubmit = () => {
dispatch(
addTodo({
id: Date.now(),
name: todo,
done: false,
})
);
setTodo("");
};
const handleDelete = (id: number) => {
dispatch(removeTodo(id));
};
const handleToggle = (id: number) => {
dispatch(toggleTodo(id));
};
return (
<div>
<input
type="text"
onChange={(e) => setTodo(e.target.value)}
value={todo}
/>
<button onClick={handleSubmit}>Add</button>
{todoList.map((todo) => {
return (
<div key={todo.id} className="flex">
<input
type="checkbox"
checked={todo.done}
onChange={() => handleToggle(todo.id)}
/>
{todo.name}
<button onClick={() => handleDelete(todo.id)} className="ml-auto">
ποΈ
</button>
</div>
);
})}
</div>
);
}
export default TodoList;
Then let's add our component to our home page
import TodoList from "@/components/TodoList";
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
<TodoList />
</div>
</main>
);
}
Here is a simple and quick Nextjs 13 app with Redux and Typescript.
Note: you could create your custom dispatch and selector to include your type by default in the store file.
Please leave your suggestions to help others since this is a high-level and simple quick guide.
Junior The Dev here, keeping up the grind.
Top comments (6)
Thannks so much dear friend, l have never tried that combination however l will get started, your article is my strong introduction
I'm glad!
Good one! I was doing this last week and tried to figure out if redux has any server side support and apparently they donβt.
Yeah, may be one day.
can i fetch data by server side? not from browser as client.
nice write up, would like if there is any full-stack developer here who is interested in mentoring me