DEV Community

Cover image for How to use NextJs 13 with Redux Toolkit and Typescript
Junior Batista
Junior Batista

Posted on

How to use NextJs 13 with Redux Toolkit and Typescript

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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: {},
});


Enter fullscreen mode Exit fullscreen mode

Should look like this:

Image description

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;



Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

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>
  );
}




Enter fullscreen mode Exit fullscreen mode

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;




Enter fullscreen mode Exit fullscreen mode

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>
  );
}



Enter fullscreen mode Exit fullscreen mode

Here is a simple and quick Nextjs 13 app with Redux and Typescript.

Image description

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)

Collapse
 
respect17 profile image
Kudzai Murimi

Thannks so much dear friend, l have never tried that combination however l will get started, your article is my strong introduction

Collapse
 
juniorbatistadev profile image
Junior Batista

I'm glad!

Collapse
 
thereis profile image
Lucas Reis

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.

Collapse
 
juniorbatistadev profile image
Junior Batista

Yeah, may be one day.

Collapse
 
romanown profile image
roman

can i fetch data by server side? not from browser as client.

Collapse
 
durotimi profile image
Ibrahim Oluwadurotimi

nice write up, would like if there is any full-stack developer here who is interested in mentoring me