Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

Workspace navigation API

WorkspaceNavigation is the interface the SDK's components navigate through: they open, close and check channels and threads with it, without knowing the layout. useWorkspaceNavigation() returns it. Navigation explains how it's used, and Moving between channels, threads and messages lists every component that calls it.

Why an adapter sits here

The SDK's components depend on the interface, not on a layout. ChatView implements it over its slots and provides it to the components inside it. Outside a provider, useWorkspaceNavigation() returns a default whose operations do nothing: openChannels and openThreads are empty, and every check returns false. The first call of each of openChannel, openThread, closeChannel, closeThread and goBack also logs a console warning. defaultWorkspaceNavigation has the same members without the warnings. Without a layout lists what that means for each component.

Export From What it is
useWorkspaceNavigation() stream-chat-react Returns the WorkspaceNavigation of the nearest provider, or a no-op default that warns on first use.
WorkspaceNavigationProvider stream-chat-react Gives the components below it the navigation passed as value. See Providing your own navigation.
defaultWorkspaceNavigation stream-chat-react The default that does nothing. Spread it to fill the members your own navigation leaves out.
useWorkspacePanel() stream-chat-react The panel the calling component renders in, or undefined outside one.
WorkspacePanelProvider stream-chat-react Declares the panel, its panel prop, that its children render in. Slot, ChannelSlot and ThreadSlot render one with their slot name.
WorkspaceNavigationAdapter stream-chat-react/slot-layout ChatView's implementation. ChatView renders it, so an app doesn't.

The API

The descriptions are what ChatView's implementation does. A navigation of your own decides its own behaviour.

Member Type Description
openChannel (channel: Channel, options?: WorkspaceNavigationOptions) => void Opens the channel. It places the channel; it doesn't load it. Where an opened channel or thread lands has the rules.
openThread (target: OpenThreadTarget, options?: WorkspaceNavigationOptions) => void Opens a Thread, or the thread of { channel, message }, which it gets from client.threads.ensure().
closeChannel (cid?: string, options?: WorkspaceNavigationOptions) => void Closes the panel that shows the channel. A panel beside the main one closes with everything stacked in it. The main panel loses its top layer, or is emptied when nothing is stacked on it.
closeThread (threadId?: string, options?: WorkspaceNavigationOptions) => void Closes the panel that shows the thread, the same way. Without threadId, or when no panel shows it, the first panel holding a thread. It doesn't deactivate the thread.
goBack (key?: string, options?: WorkspaceNavigationOptions) => void Removes the channel or thread with key, a cid or a thread id, from the top of its panel, and shows what's beneath. Does nothing when canGoBack is false.
canGoBack (key?: string, panel?: string) => boolean Whether the panel showing key has something beneath it.
isChannelActive (cid?: string) => boolean Whether the channel is on screen: shown in a panel of the shown view, not covered by something stacked on top of it.
isThreadActive (threadId?: string) => boolean The same, for a thread.
isChannelDismissable (cid?: string, panel?: string) => boolean Whether its panel can be closed. Only a channel opened beside another one can.
isThreadDismissable (threadId?: string, panel?: string) => boolean Whether its panel can be closed. In the channels view, true for every thread. In the threads view, only for a thread beside the view's main thread.
isThreadsView boolean Whether the threads view is shown.
openChannels Channel[] The channels the shown view's slots hold, underneath any layers.
openThreads Thread[] The threads the shown view's slots hold, underneath any layers.

Channel and Thread are the stream-chat classes.

Options

openChannel, openThread, closeChannel, closeThread and goBack take the same options, a WorkspaceNavigationOptions:

Option Type Description
additive boolean Opens beside the current content instead of replacing it. A layout with one slot ignores it.
layer boolean Opens on top of what the panel shows. Going back reveals what was beneath.
event WorkspaceNavigationTriggerEvent The click, key press or touch that started the navigation. The channel list, search results and header buttons pass it. The SDK doesn't act on it: an override reads the ⌘ or ctrl key from it.
panel string The panel the call comes from, for a channel or thread open in two panels. Without it, the first panel showing it. The close and back operations use it.

The types they name:

Type Definition
OpenThreadTarget Thread, or { channel: Channel; message: LocalMessage }
WorkspaceNavigationTriggerEvent React.MouseEvent, React.KeyboardEvent or React.TouchEvent
WorkspacePanelId string

Beside and on top and Panels show the options at work.

Overriding navigation

ChatView's deriveWorkspaceNavigation prop changes what its navigation does. It's a DeriveWorkspaceNavigation, (base: WorkspaceNavigation) => Partial<WorkspaceNavigation>: it receives the navigation ChatView built, and returns only the members to replace. Every member it leaves out, or sets to undefined, keeps ChatView's behaviour. Call base to keep that behaviour around your own:

import type { DeriveWorkspaceNavigation } from "stream-chat-react/slot-layout";

// Declared outside the component, so ChatView gets the same function on every render.
export const deriveWorkspaceNavigation: DeriveWorkspaceNavigation = (base) => ({
  openChannel: (channel, options) => {
    console.info("Opening", channel.cid);
    base.openChannel(channel, options);
  },
});

Pass it as <ChatView deriveWorkspaceNavigation={deriveWorkspaceNavigation}>. Keep it stable: declare it outside the component, or wrap it in useCallback. A new function on every render rebuilds the navigation on every render.

The override reaches every component that calls useWorkspaceNavigation(), selecting a pinned message and Send direct message in channel details included. Those two call openChannel(channel) with no options, so an override that reads options.event gets no event from them.

Changing navigation behaviour opens a channel beside the open one on ⌘/ctrl-click, and ⌘/ctrl-click opens beside walks through it. To replace the navigation outside ChatView, render a WorkspaceNavigationProvider with your own.

See also

  • Navigation: the interface, opening beside and on top, panels, and navigation without a layout.
  • Workspace with ChatView: where ChatView puts what the navigation opens.
  • Slot layout: useChatViewNavigation(), which opens anything in a named slot.
  • Slot geometry: which panels are covered on screen.