Basics of Jotai — Easy State Management in Next.js with Jotai

Raşit Çolakel
5 min readMar 25, 2024

--

What is Jotai?

Jotai is a simple and fast state management library for Next.js. It is based on the idea of atoms and selectors. Atoms are the smallest units of state that can be created using the atom function. Selectors are derived state that can be created using the selector function. Jotai is inspired by Recoil and Recoil is inspired by Jotai. Jotai is a simpler version of Recoil.

Why Jotai?

The main reason to use Jotai is its simplicity. It is very easy to use and understand. It is also very fast and has a small bundle size. It is a good alternative to Redux and MobX. It is also a good alternative to Recoil if you want a simpler and smaller library.

How to use Jotai?

To use Jotai, you need to install it using pnpm or npm or yarn. You can install it using the following command:

pnpm install jotai

After installing Jotai, you need to wrap your app with the Provider component. You can wrap your app with the Provider component like this:

// src/layouts/app-layout.tsx
import type { ReactNode } from "react";
import { Provider } from "jotai";

type LayoutProps = {
children: ReactNode,
};

function AppLayout({ children }: LayoutProps) {
return (
<Provider>
<main className="flex min-h-screen flex-col items-center justify-between p-24">
{children}
</main>
</Provider>
);
}

export default AppLayout;

Working with atoms

Atoms are the smallest units of state in Jotai. They can be created using the atom function. Atoms can be read and written using the useAtom hook.

Creating an atom

To create an atom, you need to use the atom function. You can create an atom like this:

// src/atoms/counter-atom.ts
import { atom } from "jotai";

export const counterAtom = atom(0);

Using an atom in a component

To read an atom, you need to use the useAtom hook. You can read an atom like this:

// src/components/counter/index.tsx
"use client";

import { counterAtom } from "@/atoms/counter-atom";
import { useAtom } from "jotai";

function CounterPage() {
const [count, setCount] = useAtom(counterAtom);
return (
<div className="flex flex-col items-center space-y-4">
<h1>Counter Example</h1>
<div>{count}</div>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setCount((c) => c + 1)}
>
Increment
</button>
<button
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setCount((c) => c - 1)}
>
Decrement
</button>
</div>
);
}

export default CounterPage;

Reading and writing to an atom

To write to an atom, you need to use the useSetAtom hook. Also, you can read an atom using the useAtomValue hook. You can read and write to an atom like this:

// src/components/counter/child.tsx
"use client";
import { counterAtom } from "@/atoms/counter-atom";
import { useAtomValue, useSetAtom } from "jotai";

function CounterChild() {
const count = useAtomValue(counterAtom);
const setCount = useSetAtom(counterAtom);

return (
<div className="flex flex-col items-center space-y-4">
<h1>Counter Child Example</h1>
<div>{count}</div>
<button
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setCount(0)}
>
Reset
</button>
</div>
);
}

Creating a complex atom

We will create a complex atom that will store the user’s information. We will create a complex atom like this:

// src/atoms/user-atom.ts
import { atom } from "jotai";

export type Post = {
userId: number,
id: number,
title: string,
body: string,
};

export type User = {
name: string,
email: string,
posts: Post[],
};

export const userAtom = atom<User>({
name: "John Doe",
email: "johndoe@example.com",
posts: [],
});

Before using the complex atom, we need to look at the selectAtom utility function. The selectAtom utility function is used to create a selector that depends on an atom. You can create an atom like this:

// src/atoms/user-atom.ts
import { selectAtom } from "jotai/utils";

export const postsAtom = selectAtom(userAtom, (user) => user.posts);

Using the complex atom in a component

To read a complex atom, you need to use the useAtom hook. You can read a complex atom like this:

// src/components/user/index.tsx
"use client";

import { Post, userAtom } from "@/atoms/user-atom";
import { useAtom } from "jotai";
import Posts from "./posts";

function UserPage() {
const [user, setUser] = useAtom(userAtom);
const fetchPosts = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts?userId=1"
);
const posts = (await response.json()) as Post[];
setUser((prev) => ({ ...prev, posts }));
};

return (
<div className="flex flex-col items-center space-y-4">
<h1>User Example</h1>
<div>{user.name}</div>
<div>{user.email}</div>
<button
className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
onClick={fetchPosts}
>
Fetch Posts
</button>
<Posts />
</div>
);
}
export default UserPage;

Using the postsAtom in a component

To read a selector, you need to use the useAtomValue hook. You can read a selector like this:

// src/components/user/posts.tsx
"use client";
import { postsAtom } from "@/atoms/user-atom";
import { useAtomValue } from "jotai";

function Posts() {
const posts = useAtomValue(postsAtom); return (
<div className="flex flex-col items-center space-y-4">
<h1>Posts</h1>
{posts.map((post) => (
<div key={post.id} className="flex flex-col items-center space-y-4">
<div>{post.title}</div>
<div>{post.body}</div>
</div>
))}
</div>
);
}

export default Posts;

Source code and demo

Demo: https://jotai-example.rasit.me/

Github Repository: https://github.com/rasitcolakel/next-js-jotai

Conclusion

In this article, we learned how to use Jotai in Next.js to manage the state of our application. We learned how to create atoms and selectors. We also learned how to read and write to atoms. We also learned how to create a complex atom and use it in a component. We also learned how to use the selectAtom utility function to create a selector. We also learned how to use the useAtomValue and useSetAtom hooks to read and write to an atom. We also learned how to use the useAtomValue hook to read a selector. We also learned how to use the useAtom hook to read a complex atom. We also learned how to use the Provider component to wrap our app with Jotai

References

--

--

No responses yet