Basics of Jotai — Easy State Management in Next.js with Jotai
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