` outside the previews. Call it once from your host's preview entry file (e.g. `.storybook/preview.tsx`); your story decorators don't need to render Callables at all.
```tsx
// .storybook/preview.tsx
import { mount } from 'react-call/host'
import { Confirm } from '../src/Confirm'
mount(
)
const preview = { /* normal Storybook config */ }
export default preview
```
That's it for the simple case. Your app's own `
` mount stays where it is — this helper only handles the preview environment. If you were previously rendering `
` from inside a story decorator, drop it from the decorator. The mount is idempotent under HMR — saving your `preview.tsx` doesn't double-mount, and an open `Confirm.call()` survives the edit.
## With static providers
The Confirm renders in its own React tree, separate from every story preview. It does not inherit context from your story decorators — if it needs a theme, locale, or router, pass them via `wrapper`:
```tsx
import { mount } from 'react-call/host'
import { ThemeProvider } from '@mui/material/styles'
import { lightTheme } from '../src/themes'
import { Confirm } from '../src/Confirm'
mount(
, {
wrapper: ({ children }) => (
{children}
),
})
```
## With reactive providers
A static wrapper captures its props once. If your providers depend on Storybook globals — toolbar toggles, args, parameters — subscribe to them inside the wrapper via `useGlobals` from `@storybook/preview-api`:
```tsx
import type { ReactNode } from 'react'
import { mount } from 'react-call/host'
import { useGlobals } from '@storybook/preview-api'
import { ThemeProvider } from '@mui/material/styles'
import { lightTheme, darkTheme } from '../src/themes'
import { Confirm } from '../src/Confirm'
function ReactiveTheme({ children }: { children: ReactNode }) {
const [{ theme = 'light' }] = useGlobals()
return (
{children}
)
}
mount(
, { wrapper: ReactiveTheme })
```
External stores (Zustand, Jotai, Redux, anything backed by `useSyncExternalStore`) work the same way — both trees subscribe to the same source of truth.
## Options
```tsx
mount(element, {
wrapper?: ComponentType<{ children: ReactNode }>,
container?: HTMLElement, // default:
in document.body
})
```
Works wherever React DOM does.
# FAQ
### What if more than one call is active?
`
` works as a call stack. Multiple calls will render one after another (newer below, which is one on top of the other if your CSS is position fixed/absolute).
### Can I place more than one Root?
No. There can only be one `` mounted per createCallable(). Avoid placing it in multiple locations of the React Tree loaded at once, an error will be thrown if so.
If you specifically need this in a sandbox host (Storybook autodocs, Ladle, …), see [Multi-preview hosts](#multi-preview-hosts-storybook-ladle-) for the supported pattern.
# TypeScript types
You won't need them most likely, but if you want to split the component declaration and such, the public types are available as named exports:
```tsx
import type { UserComponent, CallContext } from 'react-call'
```
Type | Description
--- | ---
CallFunction | The call() method
UpsertFunction | The upsert() method
CallContext | The call prop in UserComponent
PropsWithCall | Your props + the call prop
UserComponent | What is passed to createCallable
Callable | What createCallable returns
# Errors
Error | Solution
--- | ---
No \ found! | You forgot to place the Root, check [Rooting section](#2--rooting). If it's already in place but not present by the time you call(), you may want to place it higher in your React tree. If you're getting this error on the server see [SSR section](#ssr).
Multiple instances of \ found! | You placed more than one Root, check [Rooting section](#2--rooting) as there is a warning about this. If you're hitting this in Storybook autodocs or another multi-preview tool, see [Multi-preview hosts](#multi-preview-hosts-storybook-ladle-).
# Lazy loading
If your callable carries a heavy payload (rich-text editor, chart library, big form), wrap it with `React.lazy` so the chunk only ships when the call fires.
```tsx
import { createCallable } from 'react-call'
import { lazy, Suspense } from 'react'
const Confirm = createCallable(
lazy(() => import('./Confirm')),
)
}>
```
- The lazy module must default-export the user component (React.lazy requirement).
- The first call waits for the chunk to download — pick a `fallback` that signals "something's loading"; `null` works but the user sees nothing happen on click.
- Subsequent calls are instant; the chunk is cached by the browser.
# SSR
✅ The react-call setup supports [Server Side Rendering](https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering). This means both createCallable and Root component are fine if run or rendered on the server.
However, bear in mind that because the call() method is meant to be triggered by user interaction, it is designed as a client-only feature.
> [!CAUTION]
> If call() is run on the server a "No \ found!" error will be thrown. As long as you don't run the call() method on the server you'll be fine.
## Next.js / RSC
Mark the file where you call `createCallable(...)` as a Client Component (the lib uses `useSyncExternalStore`):
```diff
+ 'use client'
export const Confirm = createCallable(...)
```
Then `` mounts cleanly from any Server Component (e.g. `app/layout.tsx`).
# AI agent skill
Using an AI coding assistant (Claude Code, Cursor, …)? Install the official react-call skill so it writes correct Callables — the Declare→Root→Call model, `call` vs `upsert`, mutation flow, multi-preview hosts, SSR, the single-Root rule, and the canonical vocabulary:
```sh
npx skills add desko27/react-call --skill react-call
```
The `--skill react-call` flag pins exactly this skill (this repo also hosts the maintainers' internal workflow skills, which you don't want). Powered by [`skills`](https://github.com/vercel-labs/skills) — works with any agent it supports.
# Migrating from v1
If you're upgrading from 1.x, see the [full v2 changelog](packages/react-call/CHANGELOG.md). The breaking changes in short:
- **`` is the canonical Root.** The `` form is still exported as a backwards-compatible alias but is soft-deprecated (ADR-0013).
- **Public types are flat named exports**, not under the `ReactCall` namespace (ADR-0015). Migration is a mechanical find-and-replace:
| Before | After |
| --- | --- |
| `ReactCall.Function` | `CallFunction` |
| `ReactCall.UpsertFunction` | `UpsertFunction` |
| `ReactCall.Context` | `CallContext` |
| `ReactCall.Props` | `PropsWithCall` |
| `ReactCall.UserComponent` | `UserComponent` |
| `ReactCall.Callable` | `Callable` |
- **`CallContext` no longer leaks `promise`, `resolve`, or `isUpsert`.** Replace `call.resolve(value)` with `call.end(value)`. The other two were internal and had no public-API equivalent.
- **`"Multiple instances of found!"` now fires at `call()` time**, not at Root mount time (ADR-0001) — making it compatible with `React.lazy` inside ``, React StrictMode's double-invoke, and HMR re-mounts. Tests of the form `expect(() => render(<>>)).toThrow(...)` should now assert at the `call()` site instead.