Channels
Channels are color-coded, named contexts that enable user-driven data linking between windows. Users assign windows to channels via a Channel Selector UI, and all apps on the same channel automatically share data.
How Channels Work
Unlike Shared Contexts (which are programmatically controlled), Channels are user-driven. The user decides which apps should be linked by assigning them to the same color channel (Red, Green, Blue, etc.) using the Channel Selector widget in each window's title bar.
- Each channel has a name and a color
- Apps on the same channel receive each other's published data
- Switching channels changes the data flow without code changes
- Channels are global — they work across the entire platform
Channel Modes
io.Connect supports three channel modes, configured in the app definition:
- Single — the app can join one channel at a time (default)
- Multi — the app can join multiple channels simultaneously
- Directional — the app can publish to one channel and subscribe to another
Enabling the Channel Selector
Add a Channel Selector to an app's window by setting it in the app definition JSON:
{
"name": "portfolio-app",
"type": "window",
"details": {
"url": "http://localhost:3001",
"channelSelector": {
"enabled": true,
"type": "single"
}
}
}Joining a Channel
// Join the Red channel
await io.channels.join("Red");
// Leave the current channel
await io.channels.leave();
// Get the current channel
const currentChannel = io.channels.my();
console.log(currentChannel); // "Red"Publishing Data
Publish data to your current channel. All other apps on the same channel will receive it.
await io.channels.publish({
type: "fdc3.instrument",
id: { ticker: "AAPL" },
name: "Apple Inc.",
});Subscribing to Data
Subscribe to receive data published by other apps on the same channel. The handler receives the data, the full channel context, and the publisher's peer ID.
io.channels.subscribe((data, context, updaterId) => {
// Ignore updates from myself
const isMe = io.interop.instance.peerId === updaterId;
if (isMe) return;
console.log("Received:", data);
// { type: "fdc3.instrument", id: { ticker: "AAPL" }, name: "Apple Inc." }
});io.contexts.subscribe(), the Channels subscribe() method does not return a Promise. The subscription is synchronous.React Pattern
import { useContext, useEffect, useState } from "react";
import { IOConnectContext } from "@interopio/react-hooks";
interface InstrumentData {
type: string;
id: { ticker: string };
name: string;
}
function InstrumentPanel() {
const io = useContext(IOConnectContext);
const [instrument, setInstrument] = useState<InstrumentData | null>(null);
useEffect(() => {
if (!io) return;
io.channels.subscribe((data: InstrumentData, _ctx, updaterId: string) => {
const isMe = io.interop.instance.peerId === updaterId;
if (!isMe) {
setInstrument(data);
}
});
}, [io]);
return (
<div>
<h2>Current Instrument</h2>
{instrument ? (
<p>{instrument.name} ({instrument.id.ticker})</p>
) : (
<p>Join a channel and select an instrument</p>
)}
</div>
);
}Listing Available Channels
const channels = await io.channels.list();
// Returns array of channel objects:
// [{ name: "Red", meta: { color: "red" } }, ...]Channels vs. Shared Contexts
| Feature | Channels | Shared Contexts |
|---|---|---|
| Control | User-driven (Channel Selector) | Developer-driven (programmatic) |
| Scope | Global (color-coded groups) | Global (named objects) |
| FDC3 Mapping | FDC3 User Channels | FDC3 App Channels |
| Use Case | User wants to link specific windows | Automatic sync between known apps |