Gathering detailed insights and metrics for jotai-x
Gathering detailed insights and metrics for jotai-x
Gathering detailed insights and metrics for jotai-x
Gathering detailed insights and metrics for jotai-x
Jotai store factory for a best-in-class developer experience.
npm install jotai-x
Typescript
Module System
Node Version
NPM Version
96.8
Supply Chain
96.2
Quality
86.4
Maintenance
100
Vulnerability
100
License
TypeScript (81.09%)
JavaScript (18.91%)
Total Downloads
4,252,408
Last Day
4,976
Last Week
98,905
Last Month
453,044
Last Year
3,891,652
MIT License
26 Stars
146 Commits
4 Forks
2 Watchers
11 Branches
3 Contributors
Updated on Jun 26, 2025
Minified
Minified + Gzipped
Latest Version
2.3.3
Package Id
jotai-x@2.3.3
Unpacked Size
152.04 kB
Size
30.83 kB
File Count
8
NPM Version
10.8.2
Node Version
20.19.2
Published on
Jun 26, 2025
Cumulative downloads
Total Downloads
Last Day
5.1%
4,976
Compared to previous day
Last Week
-12.6%
98,905
Compared to previous week
Last Month
8.4%
453,044
Compared to previous month
Last Year
978.7%
3,891,652
Compared to previous year
3
An extension for Jotai that auto-generates type-safe hooks and utilities for your state. Built with TypeScript and React in mind.
use<StoreName>Value('name')
and use<StoreName>Set('name', value)
extend
Built on top of jotai
, jotai-x
offers a better developer experience with less boilerplate. Create and interact with stores faster using a more intuitive API.
Looking for global state management instead of React Context-based state? Check out Zustand X - same API, different state model.
1pnpm add jotai jotai-x
Here's how to create a simple store:
1import { createAtomStore } from 'jotai-x'; 2 3// Create a store with an initial state 4// Store name is used as prefix for all returned hooks (e.g., `useAppStore`, `useAppValue` for `name: 'app'`) 5const { useAppStore, useAppValue, useAppSet, useAppState, AppProvider } = 6 createAtomStore( 7 { 8 name: 'JotaiX', 9 stars: 0, 10 }, 11 { 12 name: 'app', 13 } 14 ); 15 16// Use it in your components 17function RepoInfo() { 18 const name = useAppValue('name'); 19 const stars = useAppValue('stars'); 20 21 return ( 22 <div> 23 <h1>{name}</h1> 24 <p>{stars} stars</p> 25 </div> 26 ); 27} 28 29function AddStarButton() { 30 const setStars = useAppSet('stars'); 31 32 return <button onClick={() => setStars((s) => s + 1)}>Add star</button>; 33}
The store is where everything begins. Configure it with type-safe options:
1import { createAtomStore } from 'jotai-x'; 2 3// Types are inferred, including options 4const { useUserValue, useUserSet, useUserState, UserProvider } = 5 createAtomStore( 6 { 7 name: 'Alice', 8 loggedIn: false, 9 }, 10 { 11 name: 'user', 12 delay: 100, // Optional delay for state updates 13 effect: EffectComponent, // Optional effect component 14 extend: (atoms) => ({ 15 // Optional derived atoms 16 intro: atom((get) => `My name is ${get(atoms.name)}`), 17 }), 18 } 19 );
Available options:
1{ 2 name: string; 3 delay?: number; 4 effect?: React.ComponentType; 5 extend?: (atoms: Atoms) => DerivedAtoms; 6}
The createAtomStore
function returns an object with the following:
1const { 2 // Store name used as prefix 3 name: string, 4 5 // Store hook returning all utilities 6 useAppStore: () => StoreApi, 7 8 // Direct hooks for state management 9 useAppValue: (key: string, options?) => Value, 10 useAppSet: (key: string) => SetterFn, 11 useAppState: (key: string) => [Value, SetterFn], 12 13 // Provider component 14 AppProvider: React.FC<ProviderProps>, 15 16 // Record of all atoms in the store 17 appStore: { 18 atom: Record<string, Atom> 19 } 20} = createAtomStore({ ... }, { name: 'app' });
There are three ways to interact with the store state:
The most straightforward way using hooks returned by createAtomStore
:
1// Get value 2const name = useAppValue('name'); 3const stars = useAppValue('stars'); 4 5// Set value 6const setName = useAppSet('name'); 7const setStars = useAppSet('stars'); 8 9// Get both value and setter 10const [name, setName] = useAppState('name'); 11const [stars, setStars] = useAppState('stars'); 12 13// With selector and deps 14const upperName = useAppValue('name', { 15 selector: (name) => name.toUpperCase(), 16}, []);
Using the store instance from useAppStore()
:
1const store = useAppStore(); 2 3// By key 4store.get('name'); // Get value 5store.set('name', 'value'); // Set value 6store.subscribe('name', (value) => console.log(value)); // Subscribe to changes 7 8// Direct access 9store.getName(); // Get value 10store.setName('value'); // Set value 11store.subscribeName((value) => console.log(value)); // Subscribe to changes
For advanced use cases, you can work directly with atoms:
1const store = useAppStore();
2
3// Access atoms
4store.getAtom(someAtom); // Get atom value
5store.setAtom(someAtom, 'value'); // Set atom value
6store.subscribeAtom(someAtom, (value) => {}); // Subscribe to atom
7
8// Access underlying Jotai store
9const jotaiStore = store.store;
use<Name>Value(key, options?)
Subscribe to a single value with optional selector and deps:
1// Basic usage
2const name = useAppValue('name');
3
4// With selector
5const upperName = useAppValue('name', {
6 selector: (name) => name.toUpperCase(),
7}, [] // if selector is not memoized, provide deps array
8);
9
10// With equality function
11const name = useAppValue('name', {
12 selector: (name) => name,
13 equalityFn: (prev, next) => prev.length === next.length
14}, []);
use<Name>Set(key)
Get a setter function for a value:
1const setName = useAppSet('name');
2setName('new value');
3setName((prev) => prev.toUpperCase());
use<Name>State(key)
Get both value and setter, like React's useState
:
1function UserForm() { 2 const [name, setName] = useAppState('name'); 3 const [email, setEmail] = useAppState('email'); 4 5 return ( 6 <form> 7 <input value={name} onChange={(e) => setName(e.target.value)} /> 8 <input value={email} onChange={(e) => setEmail(e.target.value)} /> 9 </form> 10 ); 11}
The provider component handles store initialization and state synchronization:
1type ProviderProps<T> = { 2 // Initial values for atoms, hydrated once on mount 3 initialValues?: Partial<T>; 4 5 // Dynamic values for controlled state 6 ...Partial<T>; 7 8 // Optional custom store instance 9 store?: JotaiStore; 10 11 // Optional scope for nested providers 12 scope?: string; 13 14 // Optional key to reset the store 15 resetKey?: any; 16 17 children: React.ReactNode; 18}; 19 20function App() { 21 return ( 22 <UserProvider 23 // Initial values hydrated on mount 24 initialValues={{ 25 name: 'Alice', 26 email: 'alice@example.com' 27 }} 28 29 // Controlled values that sync with the store 30 name="Bob" 31 32 // Optional scope for nested providers 33 scope="user1" 34 35 // Optional key to reset store state 36 resetKey={version} 37 > 38 <UserProfile /> 39 </UserProvider> 40 ); 41}
Create multiple instances of the same store with different scopes:
1function App() { 2 return ( 3 <UserProvider scope="parent" name="Parent User"> 4 <UserProvider scope="child" name="Child User"> 5 <UserProfile /> 6 </UserProvider> 7 </UserProvider> 8 ); 9} 10 11function UserProfile() { 12 // Get parent scope 13 const parentName = useUserValue('name', { scope: 'parent' }); 14 // Get closest scope 15 const name = useUserValue('name'); 16}
Two ways to create derived atoms:
1// 1. Using extend 2const { useUserValue } = createAtomStore( 3 { 4 name: 'Alice', 5 }, 6 { 7 name: 'user', 8 extend: (atoms) => ({ 9 intro: atom((get) => `My name is ${get(atoms.name)}`), 10 }), 11 } 12); 13 14// Access the derived value using the store name 15const intro = useUserValue('intro'); 16 17// 2. External atoms 18const { userStore, useUserStore } = createAtomStore( 19 { 20 name: 'Alice', 21 }, 22 { 23 name: 'user', 24 } 25); 26 27// Create an external atom 28const introAtom = atom((get) => `My name is ${get(userStore.atom.name)}`); 29 30// Create a writable external atom 31const countAtom = atom( 32 (get) => get(userStore.atom.name).length, 33 (get, set, newCount: number) => { 34 set(userStore.atom.name, 'A'.repeat(newCount)); 35 } 36); 37 38// Get the store instance 39const store = useUserStore(); 40 41// Access external atoms using store-based atom hooks 42const intro = useAtomValue(store, introAtom); // Read-only atom 43const [count, setCount] = useAtomState(store, countAtom); // Read-write atom 44const setCount2 = useSetAtom(store, countAtom); // Write-only 45 46// With selector and deps 47const upperIntro = useAtomValue( 48 store, 49 introAtom, 50 (intro) => intro.toUpperCase(), 51 [] // Optional deps array for selector 52); 53 54// With selector and equality function 55const intro2 = useAtomValue( 56 store, 57 introAtom, 58 (intro) => intro, 59 (prev, next) => prev.length === next.length // Optional equality function 60);
The store-based atom hooks provide more flexibility when working with external atoms:
useAtomValue(store, atom, selector?, equalityFnOrDeps?, deps?)
: Subscribe to a read-only atom value
selector
: Transform the atom value (must be memoized or use deps)equalityFnOrDeps
: Custom comparison function or deps arraydeps
: Dependencies array when using both selector and equalityFnuseSetAtom(store, atom)
: Get a setter function for a writable atomuseAtomState(store, atom)
: Get both value and setter for a writable atom, like React's useState
When using value hooks with selectors, ensure they are memoized:
1// ❌ Wrong - will cause infinite renders
2useUserValue('name', { selector: (name) => name.toUpperCase() });
3
4// ✅ Correct - memoize with useCallback
5const selector = useCallback((name) => name.toUpperCase(), []);
6useUserValue('name', { selector });
7
8// ✅ Correct - provide deps array
9useUserValue('name', { selector: (name) => name.toUpperCase() }, []);
10
11// ✅ Correct - no selector
12useUserValue('name');
1// Before 2const { useAppStore } = createAtomStore({ name: 'Alice' }, { name: 'app' }); 3const name = useAppStore().get.name(); 4const setName = useAppStore().set.name(); 5const [name, setName] = useAppStore().use.name(); 6 7// Now 8const { useAppStore, useAppValue, useAppSet, useAppState } = createAtomStore({ name: 'Alice' }, { name: 'app' }); 9const name = useAppValue('name'); 10const setName = useAppSet('name'); 11const [name, setName] = useAppState('name');
No vulnerabilities found.
No security vulnerabilities found.