Real-Time Updates
irgo with Datastar provides real-time updates via Server-Sent Events (SSE). This enables live dashboards, notifications, and collaborative features.
SSE with Datastar
Datastar uses SSE natively for all interactions. Every Datastar request receives an SSE stream response, making real-time updates natural and simple.
templ LiveDashboard() { <div data-init="@get('/api/dashboard')" data-signals="{stats: {}}"> <div id="live-stats"> Loading... </div> </div>}Polling for Updates
Use Datastar's interval modifier to poll for updates:
templ StatusIndicator() { <div data-init="@get('/api/status')" data-on:load__interval.5s="@get('/api/status')" > <div id="status">Checking...</div> </div>}func GetStatus(ctx *router.Context) error { status := checkSystemStatus() return ctx.SSE().PatchTempl(templates.StatusBadge(status))}Server Push Pattern
For true server push (where the server initiates updates), use a long-running SSE connection with the Datastar hub:
import "github.com/stukennedy/irgo/pkg/datastar"// Create hubhub := datastar.NewHub()go hub.Run()// Handle SSE connectionsr.GET("/api/events", func(ctx *router.Context) error { return hub.HandleSSE(ctx.ResponseWriter, ctx.Request, datastar.WithRoom("dashboard"), )})// Push updates from anywhere in your codefunc updateDashboard(stats Stats) { html := renderer.RenderToString(templates.DashboardStats(stats)) hub.BroadcastToRoom("dashboard", datastar.PatchElements(html))}Note
Datastar's SSE approach is more efficient than WebSockets for most use cases since it uses standard HTTP/2 connections that work through proxies and CDNs.
Chat Example
templates/chat.templ
templ ChatRoom(roomID string) { <div class="chat-room" data-signals="{message: ''}" data-init={ "@get('/chat/" + roomID + "/connect')" } > <div id="messages" class="messages"> // Messages will be inserted here </div> <form data-on:submit__prevent={ "@post('/chat/" + roomID + "/send')" }> <input type="text" data-bind:message placeholder="Type a message..." /> <button type="submit">Send</button> </form> </div>}templ ChatMessage(user string, message string, timestamp time.Time) { <div id="messages" class="message"> <strong>{ user }</strong> <span>{ message }</span> <time>{ timestamp.Format("3:04 PM") }</time> </div>}handlers/chat.go
func ConnectChat(ctx *router.Context) error { roomID := ctx.Param("roomID") return hub.HandleSSE(ctx.ResponseWriter, ctx.Request, datastar.WithRoom(roomID), )}func SendMessage(ctx *router.Context) error { roomID := ctx.Param("roomID") var signals struct { Message string `json:"message"` } ctx.ReadSignals(&signals) // Broadcast to all clients in the room html := renderer.RenderToString(templates.ChatMessage( ctx.UserID(), signals.Message, time.Now(), )) hub.BroadcastToRoom(roomID, datastar.PatchElements(html)) // Clear the message input return ctx.SSE().PatchSignals(map[string]any{"message": ""})}Live Notifications
templ NotificationListener() { <div data-signals="{unreadCount: 0}" data-init="@get('/api/notifications/connect')" > <div id="notification-badge" data-show="$unreadCount > 0"> <span data-text="$unreadCount"></span> </div> <div id="notification-list"></div> </div>}templ Notification(n Notification) { <div id="notification-list" class="notification"> <p>{ n.Message }</p> <time>{ n.CreatedAt.Format("3:04 PM") }</time> </div>}// Send notification from anywhere in your codefunc notifyUser(userID string, message string) { n := Notification{ Message: message, CreatedAt: time.Now(), } html := renderer.RenderToString(templates.Notification(n)) count := getUnreadCount(userID) hub.SendToSession(userID, datastar.PatchElements(html), datastar.PatchSignals(map[string]any{"unreadCount": count}), )}Next Steps
- Datastar Integration - Core Datastar patterns
- Examples - Complete examples
