Ditch Redux Already?! Building Scalable State with Recoil & React that Doesn’t Drive You Insane
If you’ve ever wrestled with Redux in a large-scale React app and found yourself knee-deep in boilerplate hell and repetitive actions/reducers/selectors—this post is for you. We're about to investigate a state management solution that might finally let you sleep well at night: Recoil.
This isn’t just another “use Recoil instead of Redux” post—it's a guided breakdown of real-world patterns using Recoil to manage global and derived state, async data, and how all of it scales beautifully. We'll look at how Recoil fixes pain points Redux doesn’t even acknowledge exist, and why this could be the future of React state management.
React brought us components. Then came Context for global state. Then came Redux for even more complex global state. But all of these solutions were either too local, too global, or too verbose.
Here's the pain:
Enter: Recoil. The React team kind of quietly introduced it. Yet it solves 90% of these scenarios with barely any learning curve.
Recoil is a state management library for React created by folks at Facebook. It offers:
Conceptually, Recoil feels like having a shared version of React’s useState, but with data-driven dependency graphs.
Let's jump into some code to show how it really works.
npm install recoil
Then wrap your app in the <RecoilRoot> provider:
// index.tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import { RecoilRoot } from 'recoil'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <RecoilRoot> <App /> </RecoilRoot> );
// atoms/todoListAtom.ts import { atom } from 'recoil'; export const todoListState = atom({ key: 'todoListState', default: [], });
Consume and update it in any component:
import { useRecoilState } from 'recoil'; import { todoListState } from './atoms/todoListAtom'; function AddTodo() { const [todos, setTodos] = useRecoilState(todoListState); const addTodo = () => { setTodos((old) => [...old, { id: Date.now(), text: 'New todo' }]); }; return <button onClick={addTodo}>Add Todo</button>; }
// selectors/todoStats.ts import { selector } from 'recoil'; import { todoListState } from '../atoms/todoListAtom'; export const todoStatsState = selector({ key: 'todoStatsState', get: ({ get }) => { const todos = get(todoListState); const total = todos.length; const completed = todos.filter((t) => t.completed).length; return { total, completed, percentCompleted: total === 0 ? 0 : (completed / total) * 100, }; }, });
Use it anywhere, without triggering render on unrelated parts:
const stats = useRecoilValue(todoStatsState); return <p>{stats.percentCompleted}% done</p>;
// selectors/userProfile.ts import { selector } from 'recoil'; export const userProfileQuery = selector({ key: 'userProfileQuery', get: async () => { const response = await fetch('/api/me'); const data = await response.json(); return data; }, });
And then:
const profile = useRecoilValueLoadable(userProfileQuery); if (profile.state === 'loading') return <Spinner />; if (profile.state === 'hasError') return <ErrorBoundary />; return <h1>Hello, {profile.contents.name}</h1>;
Recoil feels like it belongs inside React rather than bolted onto it. Key benefits:
Let’s say you want your app to support light/dark themes, and share that across multiple components.
// atoms/themeAtom.ts export const themeState = atom({ key: 'themeState', default: 'light', });
Hook it up to a toggle:
function ThemeToggle() { const [theme, setTheme] = useRecoilState(themeState); return <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode </button>; }
And theme your components accordingly:
function ThemedHeader() { const theme = useRecoilValue(themeState); return <header className={theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-white text-black'}>Welcome</header>; }
No context, no custom providers. Just atomic state.
While Recoil is great, it's not a universal hammer. Don’t use Recoil when:
But when state shape grows, and async logic gets tied to global UI—you'll love Recoil.
Cut the boilerplate, drop the middleware, and stop treating state like a second job. Recoil brings sanity to modern React.
You’ll:
Next time you reach for Redux in your React project, ask yourself: Do I want shipping speed and joy? Or do I want boilerplate, ceremony, and chaos?
Recoil isn’t experimental anymore—it's the spiritual successor to the state we were promised when React began.
Feel the Recoil 🔫
If you're still unsure—clone the example and play for 10 minutes. You’ll never look back.
💻 If you need help building scalable, modern frontend UIs with clean state management, we offer frontend development services.
Information