Convert Figma logo to code with AI

pmndrs logojotai

👻 Primitive and flexible state management for React

18,501
596
18,501
7

Top Related Projects

19,629

Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.

60,936

A JS library for predictable global state management

27,618

Simple, scalable state management.

27,295

Actor-based state management & orchestration for complex app logic.

3,764

Manage state with style in every framework

Quick Overview

Jotai is a primitive and flexible state management library for React. It provides a minimalistic API for managing global state in React applications, focusing on simplicity and composability. Jotai uses the concept of atoms to represent pieces of state, which can be easily shared and updated across components.

Pros

  • Lightweight and has a small bundle size
  • Simple API with a low learning curve
  • Supports TypeScript out of the box
  • Integrates well with React's concurrent features

Cons

  • Less suitable for complex state management scenarios compared to more robust solutions like Redux
  • Limited ecosystem and tooling compared to more established state management libraries
  • May require additional setup for advanced features like persistence or debugging

Code Examples

  1. Creating and using a basic atom:
import { atom, useAtom } from 'jotai'

const countAtom = atom(0)

function Counter() {
  const [count, setCount] = useAtom(countAtom)
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  )
}
  1. Creating a derived atom:
import { atom, useAtom } from 'jotai'

const countAtom = atom(0)
const doubleCountAtom = atom(get => get(countAtom) * 2)

function DoubleCounter() {
  const [count] = useAtom(countAtom)
  const [doubleCount] = useAtom(doubleCountAtom)
  return <div>Count: {count}, Double: {doubleCount}</div>
}
  1. Async atom:
import { atom, useAtom } from 'jotai'

const userAtom = atom(async () => {
  const response = await fetch('https://api.example.com/user')
  return response.json()
})

function User() {
  const [user] = useAtom(userAtom)
  return <div>{user.name}</div>
}

Getting Started

To start using Jotai in your React project:

  1. Install Jotai:
npm install jotai
  1. Create and use an atom in your component:
import { atom, useAtom } from 'jotai'

const textAtom = atom('Hello')

function MyComponent() {
  const [text, setText] = useAtom(textAtom)
  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <p>Text: {text}</p>
    </div>
  )
}

Competitor Comparisons

19,629

Recoil is an experimental state management library for React apps. It provides several capabilities that are difficult to achieve with React alone, while being compatible with the newest features of React.

Pros of Recoil

  • More mature and battle-tested in large-scale applications
  • Offers advanced features like atom effects and selectors
  • Backed by Facebook, potentially ensuring long-term support

Cons of Recoil

  • Larger bundle size and more complex API
  • Steeper learning curve for beginners
  • Requires wrapping the entire app in a RecoilRoot component

Code Comparison

Recoil:

import { atom, useRecoilState } from 'recoil';

const countState = atom({
  key: 'countState',
  default: 0,
});

function Counter() {
  const [count, setCount] = useRecoilState(countState);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Jotai:

import { atom, useAtom } from 'jotai';

const countAtom = atom(0);

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Both Recoil and Jotai are state management libraries for React applications, focusing on atomic state management. Recoil offers more features and is suitable for complex applications, while Jotai provides a simpler API and smaller bundle size, making it ideal for smaller projects or those prioritizing performance.

60,936

A JS library for predictable global state management

Pros of Redux

  • Well-established ecosystem with extensive tooling and middleware
  • Centralized state management for complex applications
  • Time-travel debugging and predictable state updates

Cons of Redux

  • Boilerplate code required for setup and actions
  • Steep learning curve for beginners
  • Can be overkill for small to medium-sized applications

Code Comparison

Redux:

const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    default:
      return state
  }
}

Jotai:

import { atom } from 'jotai'

const counterAtom = atom(0)
const incrementAtom = atom(
  (get) => get(counterAtom),
  (get, set) => set(counterAtom, get(counterAtom) + 1)
)

Redux requires defining reducers and action creators, while Jotai uses atoms for state management. Jotai's approach is more concise and requires less boilerplate. Redux offers a more structured approach to state management, which can be beneficial for larger applications. Jotai provides a simpler API that may be easier for beginners to grasp and quicker to implement in smaller projects.

27,618

Simple, scalable state management.

Pros of MobX

  • More mature and established library with a larger ecosystem
  • Supports complex state management scenarios with computed values and reactions
  • Offers better performance for large-scale applications with many observers

Cons of MobX

  • Steeper learning curve due to more concepts and decorators
  • Requires more boilerplate code for setting up stores and actions
  • Less aligned with React's functional programming paradigm

Code Comparison

MobX:

import { makeAutoObservable } from "mobx";

class TodoStore {
  todos = [];
  constructor() {
    makeAutoObservable(this);
  }
  addTodo(text) {
    this.todos.push({ text, completed: false });
  }
}

Jotai:

import { atom } from "jotai";

const todosAtom = atom([]);
const addTodoAtom = atom(
  null,
  (get, set, text) => set(todosAtom, [...get(todosAtom), { text, completed: false }])
);

Jotai offers a more lightweight and React-friendly approach to state management, with a focus on simplicity and composability. It uses atoms as the basic unit of state, which can be easily combined and shared across components. MobX, on the other hand, provides a more robust solution for complex state management scenarios, with features like computed values and reactions. The choice between the two depends on the specific needs of your project and your preferred programming style.

27,295

Actor-based state management & orchestration for complex app logic.

Pros of XState

  • More powerful for complex state management with hierarchical and parallel states
  • Visual editor (XState Viz) for designing and debugging state machines
  • Extensive documentation and community support

Cons of XState

  • Steeper learning curve due to its more complex API and concepts
  • Potentially overkill for simple state management needs
  • Larger bundle size compared to Jotai's lightweight approach

Code Comparison

XState:

import { createMachine, interpret } from 'xstate';

const toggleMachine = createMachine({
  id: 'toggle',
  initial: 'inactive',
  states: {
    inactive: { on: { TOGGLE: 'active' } },
    active: { on: { TOGGLE: 'inactive' } }
  }
});

Jotai:

import { atom, useAtom } from 'jotai';

const toggleAtom = atom(false);
const Toggle = () => {
  const [isActive, setIsActive] = useAtom(toggleAtom);
  return <button onClick={() => setIsActive(!isActive)}>Toggle</button>;
};

XState offers a more structured approach to state management, suitable for complex applications with many states and transitions. Jotai, on the other hand, provides a simpler, atom-based state management solution that's easier to grasp and implement for basic use cases.

3,764

Manage state with style in every framework

Pros of Signals

  • Lightweight and fast, with a smaller bundle size
  • Designed specifically for Preact, offering tight integration
  • Simple API with a focus on ease of use

Cons of Signals

  • Limited to Preact ecosystem, less versatile across frameworks
  • Fewer advanced features compared to Jotai's rich ecosystem
  • Less community adoption and third-party integrations

Code Comparison

Signals:

import { signal } from "@preact/signals";

const count = signal(0);
const increment = () => count.value++;

return <button onClick={increment}>{count}</button>;

Jotai:

import { atom, useAtom } from "jotai";

const countAtom = atom(0);
const Counter = () => {
  const [count, setCount] = useAtom(countAtom);
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
};

Both Signals and Jotai provide state management solutions, but they cater to different needs. Signals is tailored for Preact and offers a simpler API, while Jotai is more versatile and feature-rich, supporting various React-based frameworks. The choice between them depends on the specific project requirements, framework preferences, and desired level of complexity in state management.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README


Jotai (light mode) Jotai (dark mode)


visit jotai.org or npm i jotai

Build Status Build Size Version Downloads Discord Shield Open Collective

Jotai scales from a simple useState replacement to an enterprise TypeScript application.

  • Minimal core API (2kb)
  • Many utilities and extensions
  • No string keys (compared to Recoil)

Examples: Demo 1 | Demo 2

First, create a primitive atom

An atom represents a piece of state. All you need is to specify an initial value, which can be primitive values like strings and numbers, objects, and arrays. You can create as many primitive atoms as you want.

import { atom } from 'jotai'

const countAtom = atom(0)
const countryAtom = atom('Japan')
const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])
const mangaAtom = atom({ 'Dragon Ball': 1984, 'One Piece': 1997, Naruto: 1999 })

Use the atom in your components

It can be used like React.useState:

import { useAtom } from 'jotai'

function Counter() {
  const [count, setCount] = useAtom(countAtom)
  return (
    <h1>
      {count}
      <button onClick={() => setCount((c) => c + 1)}>one up</button>
      ...

Create derived atoms with computed values

A new read-only atom can be created from existing atoms by passing a read function as the first argument. get allows you to fetch the contextual value of any atom.

const doubledCountAtom = atom((get) => get(countAtom) * 2)

function DoubleCounter() {
  const [doubledCount] = useAtom(doubledCountAtom)
  return <h2>{doubledCount}</h2>
}

Creating an atom from multiple atoms

You can combine multiple atoms to create a derived atom.

const count1 = atom(1)
const count2 = atom(2)
const count3 = atom(3)

const sum = atom((get) => get(count1) + get(count2) + get(count3))

Or if you like fp patterns ...

const atoms = [count1, count2, count3, ...otherAtoms]
const sum = atom((get) => atoms.map(get).reduce((acc, count) => acc + count))

Derived async atoms needs suspense

You can make the read function an async function too.

const urlAtom = atom('https://json.host.com')
const fetchUrlAtom = atom(async (get) => {
  const response = await fetch(get(urlAtom))
  return await response.json()
})

function Status() {
  // Re-renders the component after urlAtom is changed and the async function above concludes
  const [json] = useAtom(fetchUrlAtom)
  ...

You can create a writable derived atom

Specify a write function at the second argument. get will return the current value of an atom. set will update the value of an atom.

const decrementCountAtom = atom(
  (get) => get(countAtom),
  (get, set, _arg) => set(countAtom, get(countAtom) - 1)
)

function Counter() {
  const [count, decrement] = useAtom(decrementCountAtom)
  return (
    <h1>
      {count}
      <button onClick={decrement}>Decrease</button>
      ...

Write only derived atoms

Just do not define a read function.

const multiplyCountAtom = atom(null, (get, set, by) =>
  set(countAtom, get(countAtom) * by),
)

function Controls() {
  const [, multiply] = useAtom(multiplyCountAtom)
  return <button onClick={() => multiply(3)}>triple</button>
}

Async actions

Just make the write function an async function and call set when you're ready.

const fetchCountAtom = atom(
  (get) => get(countAtom),
  async (_get, set, url) => {
    const response = await fetch(url)
    set(countAtom, (await response.json()).count)
  }
)

function Controls() {
  const [count, compute] = useAtom(fetchCountAtom)
  return (
    <button onClick={() => compute('http://count.host.com')}>compute</button>
    ...

Note about functional programming

Jotai's fluid interface is no accident — atoms are monads, just like promises! Monads are an established pattern for modular, pure, robust and understandable code which is optimized for change. Read more about Jotai and monads.

Links

NPM DownloadsLast 30 Days