Skip to Content
API

React hooks

useObservable()

A React hook that returns the current/latest value from an observable. Store updates are deferred by default via useDeferredValue: urgent renders keep the previous value while a background render catches up. That makes it safe to suspend on the returned value without replacing already-revealed UI with a Suspense fallback.

The deferral is identity-coherent: unlike a bare useDeferredValue(useObservable(...)), the observable identity and its value are deferred as one snapshot, and when the observable identity changes (e.g. it is memoized on a document id that just changed) the hook falls back to the live value — the initialValue, or the new observable’s last emission when it is already live elsewhere — so the previous identity’s value never renders under the new one.

initialValue is required: it is what renders until the observable emits. Every value is a valid initial value — undefined included, pass it explicitly — and omitting the argument throws during render. Functions act as initializers, exactly like useState: pass () => value to compute the initial value lazily, and an initializer returning the function when the initial value should be a function itself. When there is no meaningful initial value, use useObservablePromise instead.

Mounts, remounts, and <Activity> reveals still render the current snapshot synchronously (no initial-value flash once a value has been emitted). The observable is never subscribed during render — the initialValue paints first and the live subscription starts on commit, keeping subscribe-time side effects out of the render phase. Keep the observable’s identity stable across renders (useMemo, useState, module scope, or React Compiler memoization): like useSyncExternalStore’s subscribe, an observable rebuilt on every render is re-subscribed on every render, and when it synchronously replays a value that differs from the initialValue this forces a render loop. On the server, this hook renders the resolved initialValue — exactly what the client’s first paint will show — and never subscribes the observable.

Prefer this hook for previews, validation, lists, and other non-input reads. Use useSyncObservable for controlled inputs.

Signature

function useObservable<T>( observable$: Observable<T>, initialValue: T | (() => T), options?: UseObservableOptions, ): T function useObservable<T, InitialValue>( observable$: Observable<T>, initialValue: InitialValue | (() => InitialValue), options?: UseObservableOptions, ): T | InitialValue interface UseObservableOptions { disabled?: boolean }

disabled pauses the live subscription (later emissions stop updating the component; the last value is kept). A disabled hook performs no subscriptions at all — disabled: true means zero subscriptions until it is re-enabled — see the guide.

Example

import {useMemo} from 'react' import {useObservable} from 'react-rx' import {interval} from 'rxjs' function MyComponent() { const observable = useMemo(() => interval(100), []) const number = useObservable(observable, 0) return <>The number is {number}</> }

useSyncObservable()

A React hook that returns the current/latest value from an observable synchronously via useSyncExternalStore. This is the v4 useObservable behavior.

Use it when the value feeds a controlled input, or must stay consistent within the same event.

initialValue is required and follows the same rules as useObservable: every value is valid (undefined included), functions act as useState-style initializers, and omitting the argument throws during render. The server always renders the resolved initialValue.

Caveat: store mutations cannot be marked as Transitions. Suspending on a value returned by this hook replaces already-visible content with the nearest Suspense fallback — see the useSyncExternalStore caveats . Compare the two hooks in the Suspense example.

Signature

function useSyncObservable<T>( observable$: Observable<T>, initialValue: T | (() => T), options?: UseObservableOptions, ): T function useSyncObservable<T, InitialValue>( observable$: Observable<T>, initialValue: InitialValue | (() => InitialValue), options?: UseObservableOptions, ): T | InitialValue

Example

import {useSyncObservable} from 'react-rx' import {Subject} from 'rxjs' const text$ = new Subject<string>() function SearchField() { // Controlled input values must update synchronously. const text = useSyncObservable(text$, '') return <input value={text} onChange={(event) => text$.next(event.currentTarget.value)} /> }

useObservablePromise()

A React hook that turns an observable into a use()-compatible promise for Suspense and Activity pre-rendering.

Signature

function useObservablePromise<T>( observable: Observable<T>, options?: UseObservablePromiseOptions, ): ObservablePromise<T> interface UseObservablePromiseOptions { disabled?: boolean ttl?: number } type ObservablePromise<T> = Promise<T> & ({status: 'pending'} | {status: 'fulfilled'; value: T} | {status: 'rejected'; reason: unknown})

The hook does not suspend, and mounting renders never subscribe the source: the fetch starts when the component that called the hook commits, when an already-live consumer re-renders with a new observable (so startTransition / useDeferredValue swaps fetch and commit on their own; see the Transitions and refetching example), or via preloadObservablePromise. Pass the returned promise as a prop to a child component that reads it with React’s use, with a <Suspense> boundary between the hook caller and that child — the caller must be able to commit while the child suspends. Never call use() on the promise in the same component that called the hook (or without a boundary in between): it suspends on its own pending promise before the fetch can start and deadlocks, the same wrong usage as use()-ing a promise created during your own render, and it is not guarded against. Suspends until the first emission; later emissions update without re-suspending. Errors reject the promise (Error Boundary). Hidden <Activity> trees calling the hook stay paused until revealed.

Client components only: on the server the observable is never subscribed, so server rendering emits the Suspense fallback and the fetch starts after hydration. react-rx is not a library for React Server Components or server-only flows — see server rendering in the guide. Also see the guide for startWith caveats, disabled / ttl, Activity patterns, and when to prefer useObservable.

Example

import {Suspense, use, useMemo} from 'react' import {useObservablePromise} from 'react-rx' import {fromFetch} from 'rxjs/fetch' function Profile({url}: {url: string}) { const data$ = useMemo(() => fromFetch(url, {selector: (r) => r.json()}), [url]) const promise = useObservablePromise(data$) return ( <Suspense fallback="Loading…"> <Pre promise={promise} /> </Suspense> ) } function Pre({promise}: {promise: Promise<unknown>}) { return <pre>{JSON.stringify(use(promise), null, 2)}</pre> }

preloadObservablePromise()

Warm the useObservablePromise cache before any consumer is live (for example on mouseenter, in a route loader, or ahead of a transition swap so it commits with no pending period). Not a hook — callable anywhere. Returns the same promise instance the hook would return for that observable.

Calling it starts the source subscription immediately, before any component has committed. On the server it is a no-op: it returns an inert, forever-pending promise and subscribes nothing (react-rx never subscribes observables on the server), so preloads in shared/isomorphic code only take effect in the browser. Pending entries are never timed out, so a never-emitting / hung observable keeps both the promise and the subscription alive until it settles. Prefer RxJS timeout (or cancel the source) when a preload can stall.

Signature

function preloadObservablePromise<T>( observable: Observable<T>, options?: {ttl?: number}, ): ObservablePromise<T>

Default ttl is 5000 (longer than the hook default) so a hover-warmed value survives until click/navigation.

What happened to useObservableEvent()?

It was removed in v7. Push events into an RxJS Subject from your event handler and read the derived stream with one of the hooks — see Handling events for the recommended patterns and the v6 → v7 migration guide for a before/after.

Last updated on