---
url: https://canary.warp-drive.io/api/@warp-drive/react.md
description: >-
  React bindings for WarpDrive, providing `StoreProvider`, `useStore` and the
  `<Request />` component for rendering request states with React reactivity.
---

This package provides a React-specific reactivity integration, components and hooks for
***Warp*Drive**. The [Guides](#guides) at the end of this page cover installing and using it.

## The Basics

1. Provide your app with a store.

```tsx [src/app.tsx]
import { StoreProvider } from '@warp-drive/react';
import { Store } from './store.ts';
import { UserList } from './user-list.tsx';

export function App(props) {
  return (
    <div className='App'>
      <StoreProvider Store={Store}>
        <h1>Hello React!</h1>
        <UserList />
      </StoreProvider>
    </div>
  );
}
```

2. Make your First Request.

```tsx [src/users.tsx]
import { Request } from '@warp-drive/react';
import { listUsers } from './api.ts';

export function UserList() {
  return (
    <Request
      query={listUsers()}
      states={{
        loading: ({ state }) => <div>Loading Users...</div>,
        error: ({ error, features }) => (
          <div>
            <p>Error: {error.message}</p>
            <p><button onClick={features.retry}>Try Again?</button></p>
          </div>
        ),
        content: ({ result, features }) => (
          <ul>
            {result.data.map(user => <li>{user.name}</li>)}
          </ul>
        ),
      }}
    />
  );
}
```

## How It Works

Unlike reactive frameworks, React does not natively have the ability to support
fine-grained reactivity. However, we can approximate it to "good enough"
granularity by keeping track of signals used within a specific [ReactiveContext](functions/ReactiveContext.md).

React also does not have a built-in way to memoize arbitrary functions based on
what signals they consume the way that reactive frameworks do, but by building
overtop of other Signal libraries we can provide this.

Due to the above limitations, the React Signals integration is built
overtop [`@warp-drive/alien-signals`](/api/@warp-drive/alien-signals/primitives/), a signals graph
built on [alien-signals](https://github.com/stackblitz/alien-signals).

Its `Watcher` especially is valuable here, as it allows us to subscribe to changes
to the dependency graph of a memo and not just a signal.

Every [\<ReactiveContext />](functions/ReactiveContext.md) provides a `Watcher` and subscribes to it as
an [External Store](https://react.dev/reference/react/useSyncExternalStore). When reactive state is
accessed, if there is a current context then the signal is added to that context's Watcher.

The Watcher then intelligently batches updates to the signals it is observing and then notifies React
that the external store has changed. The triggers React to rerender the components in the context's
sub-tree.

## Optimizing Rerenders

The [\<Request />](functions/Request.md) component also functions as a `<ReactiveContext/>` (you do not need to wrap the component
in one yourself). For most applications, relying only on the `<Request />` component may be good enough. But when performance
counts you might find that wrapping additional sub-trees in their own `<ReactiveContext/>` is better.

For instance, imagine a request that loads a list of users. We might wrap each individual user in its own `<ReactiveContext/>`.

```tsx
function UserPreview({ user }) {
  return (
    <ReactiveContext>
      <h3>{user.name} | {user.jobTitle}</h3>
    </ReactiveContext>
  );
}

function UserList() {
  return (
    <Request
      query={getFirstPageOfUsers()}
      states={{
        loading: ({ state }) => <div>Loading user data...</div>,
        error: ({ error, features }) => (
          <div>
            <p>Error: {error.message}</p>
            <p><button onClick={features.retry}>Try Again?</button></p>
          </div>
        ),
        content: ({ result, features }) => (
          <ul>
          {result.data.map(user => <li><UserPreview user={user} /></li>)}
          </ul>
        </div>
      ),
      }}
    />
  );
}
```

## Using React Embedded In Another Reactive Framework

When React components share a page and a store with another framework, such as an Ember app that
renders some React components, import `@warp-drive/alien-signals/install` before either
framework's `install` import:

```ts
import '@warp-drive/alien-signals/install';
import '@warp-drive/ember/install';
import '@warp-drive/react/install';
```

The other framework then registers its signals with the same graph React watches, and memoized
values come from that graph, so components in both frameworks re-render when the data they read
changes. See
[Using Ember and React on the Same Page](/guides/the-manual/cookbook/multiple-frameworks-on-one-page.md)
for the full setup.

::: tip Derivations only track WarpDrive's signals
With `@warp-drive/alien-signals/install`, memoized values such as
[derived fields](/guides/the-manual/schemas/derivations.md) only recompute when a signal
***Warp*Drive** manages changes. If a derivation reads state that only a framework tracks, such
as an Ember `@tracked` property or a React `useState` value, it keeps returning its cached value
when that state changes. Keep the state a derivation reads in ***Warp*Drive**, for instance as a
field on the resource it derives from.
:::

## Tips & Tricks aka "The Rule of WarpDrive"

1. only things accessed during a render are subscribed to

Values accessed asynchronously inside of effects or callbacks will not become dependencies unless the reactive
property is explicitly a dependency of the effect or callback. Which brings us to rule #2.

2. useEffect/useMemo/useCallback etc will only re-run if the reactive property is one of their dependencies

This bit is easy enough. If you want the effect or callback to re-run anytime a rerender is due to reactive state
having changed, you can consume the context value itself as a dependency.

```ts
import { WatcherContext } from '@warp-drive/react';

function OnAnyReactiveUpdate() {
  useEffect(() => console.log('updated'), [WatcherContext]);
}
```

3. if signals are passed from an external source, their consumption by the app needs to occur within render.

This means that top-level consumption by the app must be done within a component, see below.

::: code-group

```ts [❌-BAD]
// the reactive state is created external to the React application
const blogPost = await store.request(getBlogPost());

// We wrapped the usage in a ReactiveContext, and yet 🤔... strangely
// it does not reactively update
const root = createRoot(element);
root.render(
   <ReactiveContext><div>{blogPost.content.title}</div></ReactiveContext>
)
```

```ts [✅-GOOD]
// the reactive state is created external to the React application
const blogPost = await store.request(getBlogPost());

// wrap the consumption in a component
function MyApp() {
  return (
    <div>{blogPost.content.title}</div>
  }
}

// render the react app wrapped in a <ReactiveContext />
const root = createRoot(element);
root.render(
  <ReactiveContext><MyApp /></ReactiveContext>
)
```

:::

The reason this is so is because when react compiles the jsx in the BAD example, it will treat `blogPost.content.title` as
an external dependency of the app, and thus the access of it will only occur once and cannot be subscribed to by React's render.

[Here is a babel repl](https://babeljs.io/repl#?config_lz=N4IgZglgNgpgdgQwLYxALhAJxgBygOgCsBnADxABoQdtiYAXY9AbWZHgDdLR6FMBzBkwxQExegAIAjBIDCAC0wB7FBI4xMxCErhMqSJQBMArrGFgEUOgF8AuhTbYEAY3rcsxuPQgp0IBMb0KgjeziB29iDESsaYzjAAKgCeOKgYBiaw4UA\&code_lz=JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wG4AoUSWRYmAEQHkBZObXAo9GAWgBNcAejQAbYEgB2MMuXJoIEgM7wQATwDKMFDCRwAvAQAWSESIhwA7tBF8Z5ZNybMAdGi46EECDAAUAtACuIJIwzgDmSDAAoiJIwVIAQqoAknw-hF7SAJRZzkQSfEhQPuRwcAA8DhjOmlDAGMwQhQB8pWUVfMAAbs0A3mqa2kgAvuWCnT1tY1WhtfUwjS3kWRRAA\&lineWrap=true\&version=7.28.3) to see what this looks like in compiled output.

By moving the access inside of `MyApp`, our reactive state is now a dependency of the component's render, and will work as expected.

## Guides

* [Installation](/guides/installation/#react): install `@warp-drive/react` and add the
  `@warp-drive/react/install` import to your app.
* [Configuration](/guides/configuration/): configure the build plugin and create a Store.
* [Setup - React](/guides/configuration/react): provide the store to your components with
  `<StoreProvider />` and read it with `useStore`.
* [Reactivity](/guides/the-manual/reactivity/): how ***Warp*Drive** uses signals to notify your UI.
* [Making Requests](/guides/the-manual/requests/): make a request with `<Request />`, or with
  `getRequestState` inside a `<ReactiveContext />`.
* [Reactive Control Flow](/guides/the-manual/reactivity/control-flow.md): render a request's
  loading, error and content states.

## Components

* [ReactiveContext](functions/ReactiveContext.md)
* [Request](functions/Request.md)
* [StoreProvider](functions/StoreProvider.md)

## Contexts

* [WatcherContext](variables/WatcherContext.md)

## Hooks

* [useStore](functions/useStore.md)

## Other

* [install](install/index.md)
* [ChromeComponentProps](types/ChromeComponentProps.md)
* [RequestProps](types/RequestProps.md)
* [RequestStates](types/RequestStates.md)
* [StoreProviderProps](types/StoreProviderProps.md)
