jotai-recoil-compat
v0.1.2
Published
A Recoil-compatible API wrapper over Jotai for seamless migration from Recoil
Maintainers
Readme
jotai-recoil-compat
A Recoil-compatible API wrapper over Jotai for seamless migration from Recoil to Jotai.
Why jotai-recoil-compat?
Recoil is no longer actively maintained, but many projects rely on it for state management. This library provides a drop-in replacement that uses Jotai under the hood while maintaining Recoil's familiar API, making migration straightforward and less risky.
Features
- Recoil-compatible API: Minimal code changes required
- Powered by Jotai: Leverages Jotai's modern, lightweight state management
- TypeScript support: Full type safety out of the box
- Tree-shakeable: Only bundle what you use
- Easy migration: Gradually migrate your codebase
Installation
npm install jotai-recoil-compat jotai
# or
yarn add jotai-recoil-compat jotai
# or
pnpm add jotai-recoil-compat jotaiQuick Start
Before (Recoil)
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue } from 'recoil';
const countState = atom({
key: 'countState',
default: 0,
});
const doubleCountState = selector({
key: 'doubleCountState',
get: ({ get }) => get(countState) * 2,
});
function App() {
return (
<RecoilRoot>
<Counter />
</RecoilRoot>
);
}
function Counter() {
const [count, setCount] = useRecoilState(countState);
const doubleCount = useRecoilValue(doubleCountState);
return (
<div>
<p>Count: {count}</p>
<p>Double: {doubleCount}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}After (jotai-recoil-compat)
Simply change the import statement:
// Change this:
// import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue } from 'recoil';
// To this:
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue } from 'jotai-recoil-compat';
// Everything else stays the same!API Reference
Core Functions
atom(options)
Creates an atom with Recoil's API.
import { atom } from 'jotai-recoil-compat';
const textState = atom({
key: 'textState',
default: 'Hello',
});selector(options)
Creates a derived state (selector) with Recoil's API.
import { atom, selector } from 'jotai-recoil-compat';
const countState = atom({
key: 'countState',
default: 0,
});
const doubleCountState = selector({
key: 'doubleCountState',
get: ({ get }) => get(countState) * 2,
});
// Writable selector
const incrementState = selector({
key: 'incrementState',
get: ({ get }) => get(countState),
set: ({ get, set }, newValue) => {
set(countState, newValue);
},
});Hooks
useRecoilState(state)
Returns a tuple with the current value and a setter function.
import { useRecoilState } from 'jotai-recoil-compat';
function Component() {
const [count, setCount] = useRecoilState(countState);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}useRecoilValue(state)
Returns the current value of an atom or selector (read-only).
import { useRecoilValue } from 'jotai-recoil-compat';
function Component() {
const count = useRecoilValue(countState);
return <div>Count: {count}</div>;
}useSetRecoilState(state)
Returns a setter function without subscribing to value changes.
import { useSetRecoilState } from 'jotai-recoil-compat';
function Component() {
const setCount = useSetRecoilState(countState);
return (
<button onClick={() => setCount((prev) => prev + 1)}>
Increment
</button>
);
}useResetRecoilState(state)
Returns a function to reset the atom to its default value.
import { useResetRecoilState } from 'jotai-recoil-compat';
function Component() {
const resetCount = useResetRecoilState(countState);
return <button onClick={resetCount}>Reset</button>;
}Components
<RecoilRoot>
Provides the state context for your application.
import { RecoilRoot } from 'jotai-recoil-compat';
function App() {
return (
<RecoilRoot>
<YourApp />
</RecoilRoot>
);
}Migration Guide
Step 1: Install jotai-recoil-compat
npm install jotai-recoil-compat jotaiStep 2: Update imports
Find and replace all Recoil imports:
// Before
import { ... } from 'recoil';
// After
import { ... } from 'jotai-recoil-compat';Step 3: Test your application
Run your tests and verify everything works as expected.
Step 4: (Optional) Gradually migrate to native Jotai
Once stable, you can gradually refactor to use Jotai's native API for new features while keeping existing code unchanged.
Known Limitations
- Atom Effects: Not fully implemented yet. Consider using Jotai's built-in features or creating custom hooks.
- initializeState: The
RecoilRootinitializeStateprop shows a warning. Use atom default values instead. - Loadable API: Simplified implementation. Async atoms are supported but with Jotai's behavior.
- Snapshots: Not implemented. Use Jotai's store API if needed.
Comparison with Recoil
| Feature | Recoil | jotai-recoil-compat | |---------|--------|--------------| | Basic atoms | ✅ | ✅ | | Selectors | ✅ | ✅ | | Async selectors | ✅ | ✅ (Jotai behavior) | | Atom effects | ✅ | ⚠️ Planned | | Snapshots | ✅ | ❌ Use Jotai store | | DevTools | ✅ | ✅ (Jotai DevTools) |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
Acknowledgments
- Jotai - The amazing state management library powering this wrapper
- Recoil - The inspiration for this API design
Support
If you encounter any issues or have questions, please open an issue.
