Hooks
The SDK includes two hooks that make it easier to build custom AI UI components.
Best Practices
- Use
useAIStateto avoid duplicating backend state logic. - Keep streaming speeds readable; avoid overly fast typewriter effects.
- Prefer
useMessageTextStreamingfor AI messages only. - Keep custom indicators simple and state-driven.
- Reset streaming when message IDs change to avoid stale text.
useAIState
Returns the current AI state for the active channel.
Example Usage
const isMessageAIGenerated = (message) => !!message.ai_generated;
const MyAIStateIndicator = () => {
const { aiState } = useAIState();
return aiState === AIStates.Thinking ? (
<div>
<p>The chat-bot is currently thinking...</p>
</div>
) : null;
};
const App = () => (
<Chat client={client} isMessageAIGenerated={isMessageAIGenerated}>
<Channel channel={channel}>
<MessageList />
<AIStateIndicator />
<MessageComposer />
</Channel>
</Chat>
);In the example above, the custom indicator renders only when the AI is in the AI_STATE_THINKING state.
useMessageTextStreaming
Returns text in a streamed, typewriter fashion. Control the speed with streamingLetterIntervalMs and renderingLetterCount.
Example usage
const isMessageAIGenerated = (message) => !!message.ai_generated;
const MyStreamedMessageText = ({ message: messageFromProps, renderText }) => {
const { message: messageFromContext } = useMessageContext(
"StreamedMessageText",
);
const message = messageFromProps || messageFromContext;
const { text = "" } = message || {};
const { streamedMessageText } = useMessageTextStreaming({
renderingLetterCount: 1,
streamingLetterIntervalMs: 10,
text,
});
return (
<MessageText
message={{ ...message, text: streamedMessageText }}
renderText={renderText}
/>
);
};
const App = () => (
<Chat client={client} isMessageAIGenerated={isMessageAIGenerated}>
<Channel channel={channel} StreamedMessageText={MyStreamedMessageText}>
<MessageList />
<MessageComposer />
</Channel>
</Chat>
);This override uses useMessageTextStreaming to speed up the typewriter animation.