The Spaghetti Code We All Write
Managing interactive UI in React quickly devolves into a tangled mess. Developers routinely prop drill common isOpen states and onClose handlers deep into the component tree, passing them through layers of unrelated components. This forces parent components to manage child UI visibility, even when they have no direct concern, obscuring intent and complicating component reusability across the application.
This widespread pattern tightly couples UI state with core business logic, scattering control across numerous files. The result is classic spaghetti code, where a simple confirmation modal or multi-step form wizard becomes a web of interconnected useState calls and useEffect hooks. Such tightly bound logic is difficult to trace, debug, and refactor, turning minor UI adjustments into significant development efforts.
Traditional solutions like the Context API or React Portals offer only partial relief for these issues. Context requires verbose providers and consumers, adding significant boilerplate; Portals isolate DOM elements but provide no inherent solution for state management or control flow. Crucially, neither fundamentally solves the core architectural challenge: calling a UI component like an asynchronous function that returns a direct, actionable value, forcing developers into disconnected, callback-heavy approaches.
Call Components Like Async Functions
react-call redefines interactive UI management. It mirrors the native browser's window.confirm API, allowing components to pause execution, bringing an intuitive, blocking model directly into modern React.
Invoke components like async functions: await Confirm.call(). Execution pauses, awaiting user input—like an OK or Cancel click—before resuming. This keeps business logic linear and local to the calling component, simplifying complex workflows.
Architecture shifts dramatically. Eliminate isModalOpen state variables, removing prop drilling. Drop onClose or onSubmit callbacks. No longer require complex context providers or portals for interactive UI elements.
A callable component, wrapped with createCallable, acts as its own mounting point. Place it as a root tag anywhere visible; it sits as a listener. react-call manages multiple active instances via an internal stack, handling rendering and exit animations.
Under the hood, react-call abstracts promise management and handles typed requests/responses out-of-the-box. This lightweight utility is under 500B with zero dependencies. Full hot module replacement support maintains fast development cycles.
Inside the react-call Architecture
A single Root component forms react-call's foundation, acting as its own mounting point. Place this callable component as a root tag anywhere visible in your application, like App.js, where it sits as a passive listener. When multiple calls trigger, react-call manages them via an internal stack, handling rendering, clean exit animations, and ensuring instance isolation automatically.
Upsert provides a critical pattern for singleton UI elements, like global notification toasts or loading overlays. While call() pushes new instances, upsert() prevents duplication for components where only one instance should be active. If an instance is on screen, upsert smoothly updates the existing component with new data on the fly, avoiding redundant UI and ensuring controlled, single-instance rendering.
Connect UI directly to backend actions using the Mutation Flow concept and its useMutationFlow hook. This hook automatically manages pending states, ensuring your callable UI, like a confirmation modal, remains open with a loading spinner while an async function runs. The call resolves and closes only once the underlying promise successfully completes, effectively bridging UI state and data mutations. This explicit control, including manual call.end(), allows robust error handling and retry mechanisms without losing user context. Explore more advanced patterns on react-call.
Enjoying this? Get one like it in your inbox each morning.
one email a day · unsubscribe in two clicks · no third-party tracking
The Practical Payoff
Deploy react-call fast. Its footprint is minimal: <500B with zero dependencies, ensuring negligible bundle size impact. Wrap any React component with the createCallable higher-order component to instantly transform it into an awaitable UI primitive, ready for direct invocation.
Move beyond simple confirmations. react-call excels at orchestrating complex interactive flows. Implement:
- Multi-step wizards
- Dynamic item pickers
- Asynchronous form submissions with integrated loading states
Utilize the useMutationFlow hook to seamlessly tie callable UI to backend actions, managing pending states and ensuring the UI remains active until promises resolve.
This isn't merely another modal library. react-call represents a fundamental shift in how you manage component interaction. It eliminates state-heavy boilerplate, declutters your frontend toolkit, and delivers a cleaner, more readable architecture. Embrace the paradigm shift; rethink your UI.
Frequently Asked Questions
What is react-call?
It's a lightweight utility that allows developers to call React components, like modals or dialogs, as if they were asynchronous functions, returning a promise that resolves with user input.
How does react-call differ from using React Context for modals?
react-call eliminates the need for a global context provider to manage modal state. It localizes the business logic to the calling component, avoiding prop drilling and complex state management.
What is the 'Upsert' feature in react-call for?
Upsert is for singleton UI patterns like toasts or loading spinners. It updates an existing component instance with new data instead of creating a new one, ensuring only one is on screen at a time.
Is react-call suitable for server-side rendering (SSR)?
The createCallable and Root components support SSR. However, the call() method itself is client-only, as it depends on user interaction in the browser.

