Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

React channels

Combine authoritative route data with typed live updates in React.

Subscribe a React component to one channel partition with useChannel(). RELKIT shares equivalent subscriptions by channel name and canonical parameters, so multiple components do not create duplicate transport subscriptions.

The component still owns its application state. A channel callback may patch local state, update the TanStack Query cache, or invalidate an ordinary route query.

Subscribe to a channel

The defineChannel announcement example generates announcements.feed, its empty partition parameters, and the posted event. This component combines that subscription with the authoritative GET /announcements query:

"use client";

/** Combines the commerce announcement query with channel invalidation and reconnect recovery. */
import { useChannel, useRoute } from "@relkit/client/react";

/** Restores authoritative messages on publication, catch-up, and replay gaps.
 * @returns The current messages with query failure and channel status feedback. */
export function Announcements() {
  const messages = useRoute("GET /announcements", { input: {} });
  const channel = useChannel("announcements.feed", {
    params: {},
    on: {
      posted: () => {
        void messages.refetch();
      },
    },
    onCaughtUp: async () => {
      await messages.refetch();
    },
    onGap: async () => {
      await messages.refetch();
    },
  });

  if (messages.isError) return <p role="alert">{messages.error.message}</p>;
  return (
    <section>
      <p role="status">Channel: {channel.status}</p>
      {messages.isPending && <p>Loading messages…</p>}
      <ul>
        {messages.data?.messages.map((message, index) => (
          <li key={index}>{message}</li>
        ))}
      </ul>
    </section>
  );
}

The generated registry determines the allowed channel names, partition parameters, event names, payloads, and presence result. Misspelled events and invalid payload assumptions fail during type checking.

Render it under the Frontend guide’s provider after pulling a contract containing those operations. See useChannel and useRealtime for the exact hook contracts.

useChannel() options

Option Purpose
params Selects one validated channel partition.
on Maps declared event names to typed callbacks. Omitted events are ignored.
onCaughtUp Runs after RELKIT has attached to the live tail and applied any valid reconnect replay.
onGap Restores authoritative state when replay cannot bridge the previous checkpoint. It may be asynchronous.

The result exposes:

Field Meaning
status Transport state: idle, connecting, connected, stale, or error.
checkpoint Last applied provider checkpoint, when one exists.
caughtUp Whether live delivery is safe to present after catch-up or gap recovery.
gap The latest recovery reason, when replay could not continue.
presence Present only when the channel declares count or member presence.

caughtUp does not mean your database query has loaded. It means RELKIT has completed the subscription handoff. Coordinate it with the loading state of the route query that owns the page’s current data.

Patch or invalidate?

Patch local state when an event carries the complete change and applying it is simple and deterministic. Invalidate the route query when permissions, derived fields, ordering, or server-side calculations could make a local patch incomplete.

onCaughtUp is a useful final invalidation point: load the route, attach the channel, then confirm that no update was missed between those operations. onGap must perform a full restoration before it resolves.

Use the lower-level manager

useRealtime() exposes the shared connection status and four typed operations:

Operation Behavior
subscribe(name, params, listener) Starts or joins a shared subscription and returns an unsubscribe function.
bind(...) Alias of subscribe().
unsubscribe(stop) Calls the returned unsubscribe function.
unbind(stop) Alias of unsubscribe().

Prefer useChannel() for components because it handles subscription cleanup and exposes catch-up state. Use useRealtime() for an application-level adapter or store that already owns its own lifecycle.

Mount and identity behavior

The hook waits until RelkitClientProvider has a compatible contract and resolved identity. identityKey: null blocks private restoration while application identity is unknown. Changing identity or session aborts the old subscription and rejects late frames from it.

Unmounting the final listener releases the shared subscription. It does not delete authoritative application data from your QueryClient.

Reference and source