Skip to main content

Notification Feed

A simulated app with a notification bell that receives real-time notifications. Click the bell to see existing notifications, and watch as new ones arrive. The channel auto-subscribes on connect using a friendly alias. Deploy notifications also update the Recent Deploys list in real time.

Get the code for this example and run it yourself from the GitHub repository.

How it works​

This example uses the @hotsock/hotsock-js client library to demonstrate per-user channels with channel aliases, autoSubscribe, and server-published notifications. Below is a walkthrough of each piece.

Authentication​

The token grants subscribe access to a per-user notification channel with several features enabled:

{
"exp": 1693963905,
"scope": "connect",
"uid": "user-1",
"umd": { "name": "Sarah" },
"channels": {
"notifications-user-1": {
"subscribe": true,
"alias": "notifications",
"autoSubscribe": true,
"historyStart": 0
}
}
}

Key claims:

  • alias: The client uses "notifications" as the channel name instead of the real "notifications-user-1". This hides internal naming from client code.
  • autoSubscribe: The channel subscribes automatically when the connection is established — no explicit subscribe call needed.
  • historyStart: 0: Grants access to the full stored message history for loading past notifications.
  • No messages claim — notifications are server-published only.

Auto-subscribing with aliases​

With autoSubscribe: true, the client doesn't need to call subscribe explicitly. It just references the channel using the alias and waits for the hotsock.subscribed event:

const channel = client.channels("notifications")

channel.bind("hotsock.subscribed", () => {
// Channel is ready — start receiving notifications
})

The client code only ever references "notifications" — it doesn't need to know the real channel name "notifications-user-1". This is useful when channel names contain dynamic IDs that the client shouldn't need to manage.

Receiving notifications​

All notifications arrive as a single notification event. The notification type (info, success, warning) is included in the data payload and determines how the notification is styled:

channel.bind("notification", (message) => {
const { type, title, body, icon } = message.data
setNotifications((prev) => [
{
id: crypto.randomUUID(),
event: type || "info",
title,
body,
icon,
timestamp: new Date(),
read: false,
},
...prev,
])
})

Using a single event name with type inside the data is simpler than binding to multiple event names — adding new notification types later doesn't require any client code changes.

Updating multiple UI elements​

A single notification can update multiple parts of the UI. When a deploy notification arrives, the app also extracts the version number and prepends it to the Recent Deploys list:

if (title === "Deploy complete") {
const match = body.match(/v[\d.]+/)
if (match) {
setDeploys((prev) => [match[0], ...prev])
}
}

This shows how real-time messages can drive updates across different components simultaneously.

Per-user channels​

Each user has their own dedicated channel (notifications-user-{id}). This means notifications are private — only the intended recipient receives them. The server publishes to a specific user's channel using the Publish Messages API:

{
"channel": "notifications-user-1",
"event": "notification",
"data": {
"type": "info",
"title": "New follower",
"body": "Alex started following you",
"icon": "user-plus"
}
}

Cleanup​

When the component unmounts, terminate the client to close the WebSocket connection:

useEffect(() => {
return () => {
client.terminate()
}
}, [])

Putting it all together​

The full flow is: the user connects with a token that auto-subscribes to their private notification channel via a friendly alias, and receives new notifications in real time as a single notification event with the type in the data payload. Deploy notifications also update the Recent Deploys list, showing how one real-time message can drive updates across multiple UI elements. The client never needs to know the real channel name or explicitly subscribe — everything is handled by the token claims.

For a deeper dive, see the Channel Aliases documentation, the autoSubscribe claim, and the Publish Messages API.