What’s changing?
Beginning in Mattermost v11.3 (January 2026 release), some plugins that register a Right Hand Sidebar (RHS) component using registerRightHandSidebarComponent will need to implement additional code to support RHS popouts if their RHS component relies on plugin-specific state.
Why are we making this change?
RHS popouts allow users to open the right-hand sidebar in a separate window. When a popout window is created, it starts with a fresh Redux store that doesn’t contain the plugin’s state from the parent window. If your plugin’s RHS component depends on state that isn’t automatically synchronized, it won’t function correctly in the popout window without explicit state synchronization.
Who may be affected?
If you maintain a plugin that:
- Uses
registerRightHandSidebarComponentto register an RHS component, and - Your RHS component relies on plugin-specific state stored in Redux
Then you will need to update your plugin to support RHS popouts. If your RHS component is self-contained and doesn’t rely on any plugin-specific state, no changes are required.
What do you need to do as a plugin maintainer?
If your plugin’s RHS component relies on plugin-specific state, you need to:
- Register a listener to handle state requests from popout windows
- Load any necessary content when initializing in a popout window
An example of the required changes is below (from this PR):
import {getSidebarContent, updateRhsState} from '@/actions';
class PluginClass {
async initialize(registry, store) {
// ... other registrations ...
const {showRHSPlugin} = registry.registerRightHandSidebarComponent(SidebarRight, 'MyPlugin');
store.dispatch(setShowRHSAction(() => store.dispatch(showRHSPlugin)));
// Register listener to handle state requests from popout windows
if (registry.registerRHSPluginPopoutListener) {
registry.registerRHSPluginPopoutListener(pluginId, (teamName, channelName, listeners) => {
listeners.onMessageFromPopout((channel) => {
if (channel === 'GET_RHS_STATE') {
// Send your plugin's RHS state to the popout window
listeners.sendToPopout('SEND_RHS_STATE', store.getState()[`plugins-${manifest.id}`].rhsState);
}
});
});
// Handle popout window initialization
if (window.WebappUtils.popouts && window.WebappUtils.popouts.isPopoutWindow()) {
// Load any necessary content for the RHS
store.dispatch(getSidebarContent());
// Listen for state updates from the parent window
window.WebappUtils.popouts.onMessageFromParent((channel, state) => {
if (channel === 'SEND_RHS_STATE') {
// Update your plugin's state with the state from the parent window
store.dispatch(updateRhsState(state));
}
});
// Request the current RHS state from the parent window
window.WebappUtils.popouts.sendToParent('GET_RHS_STATE');
}
}
// ... rest of initialization ...
}
}
Key points:
registerRHSPluginPopoutListenerallows your plugin to respond to state requests from popout windows, providing listeners to do sowindow.WebappUtils.popouts.isPopoutWindow()detects if the current window is a popoutwindow.WebappUtils.popouts.sendToParent()sends messages to the parent windowwindow.WebappUtils.popouts.onMessageFromParent()receives messages from the parent window
Note: The channel names ('GET_RHS_STATE' and 'SEND_RHS_STATE') and the structure of the state you pass are up to you - you can customize them based on your plugin’s needs. The example above uses a simple pattern that works for most cases.
The registerRHSPluginPopoutListener API and popout utilities are available in Mattermost v11 and later, and plugins that don’t implement this will continue to work in non-popout contexts. For backwards compatibility with older Mattermost versions, you should check that registry.registerRHSPluginPopoutListener exists before calling it, and check that window.WebappUtils.popouts exists before using popout-related methods.