Chat - Advanced store access
Use the store escape hatch to subscribe to exactly the runtime slices you want.
This demo is intentionally advanced. It demonstrates how to bypass the convenience hooks and work directly with the normalized store for custom dashboards, metrics, or highly specialized derived views:
useChatStore()to get the underlying store instancechatSelectorsto read specific store slices- custom selector subscriptions for derived values
Key concepts
Getting the store
useChatStore() returns the ChatStore<Cursor> instance created by ChatProvider:
import { useChatStore, chatSelectors } from '@mui/x-chat/headless';
import { useStore } from '@mui/x-internals/store';
function Dashboard() {
const store = useChatStore();
const messageCount = useStore(store, chatSelectors.messageCount);
const conversationCount = useStore(store, chatSelectors.conversationCount);
const isStreaming = useStore(store, chatSelectors.isStreaming);
return (
<div>
<span>{messageCount} messages</span>
<span>{conversationCount} conversations</span>
<span>{isStreaming ? 'Streaming' : 'Idle'}</span>
</div>
);
}
Parameterized selectors
Some selectors accept arguments.
Pass them as additional arguments to useStore():
// Single message by ID
const message = useStore(store, chatSelectors.message, 'msg-42');
// Single conversation by ID
const conversation = useStore(store, chatSelectors.conversation, 'support');
// Typing users in a specific conversation
const typingUserIds = useStore(store, chatSelectors.typingUserIds, 'support');
// Typing users in the active conversation (no conversationId needed)
const activeTypingUserIds = useStore(
store,
chatSelectors.typingUserIdsForActiveConversation,
);
When to use direct store access
Use useChatStore() + chatSelectors when:
- you need a derived value that no built-in hook provides (for example, message count, active conversation title)
- you are building a metrics dashboard or debug panel
- you need to subscribe to store changes outside the React render cycle
- you want to combine multiple selectors into a single subscription
For standard rendering, the built-in hooks (useChat(), useMessageIds(), useMessage(), etc.) are easier and sufficient.
Store escape hatch
The runtime stays headless, but advanced consumers can subscribe to exactly the slices they need.
4
2
Support
Track this with a custom selector.
none
Welcome to the headless runtime demo.
Show me the smallest possible chat setup.
The controlled API keeps public state array-first.
That is the behavior we want to document.
Key takeaways
useChatStore()is the escape hatch for advanced store accesschatSelectorsprovides memoized selectors for all store slices- Combine with
useStore()for React subscriptions or use the store directly for imperative access - Prefer the built-in hooks for standard use cases — they wrap these selectors with a better developer experience
See also
- Selectors for the full selector reference
- Hooks for the convenience hooks that wrap these selectors
- State and store for the normalized store internals
- Selector-driven thread for the
useMessageIds()+useMessage(id)pattern