Hologram v0.9: Realtime and More - Hologram
Hologram v0.9: Realtime and More
v0.9 brings a realtime layer to Hologram - your server can now push updates to connected clients, in pure Elixir, with no JavaScript and no client-side polling.
Why Realtime Matters
Modern web apps are live. A chat message appears the instant it's sent, a notification pops up when a background job finishes, a collaborator's edit shows up without a refresh. Until now, building any of that in Hologram meant reaching outside the framework.
v0.9 closes that gap. When something happens on the server, you broadcast an action, and Hologram runs the matching action handler on every subscribed client - the same action/3 handler you already write for user events, just triggered from the server instead of a click. This was the most complex feature Hologram has shipped to date, and the one that took the most work: the realtime PR (#806) landed 414 commits and over 12,000 lines across 108 files.
Here's a complete chat room - init/3 subscribes the page to a room, a command broadcasts each new message, and the action handler runs on every subscriber, including the sender:
defmodule MyApp.RoomPage do<br>use Hologram.Page
route "/rooms/:id"
layout MyApp.Layout
def init(%{id: room_id}, component, server) do<br>component = put_state(component, room_id: room_id, messages: Chat.recent_messages(room_id))<br>server = put_subscription(server, {:room, room_id})<br>{component, server}<br>end
def command(:send_message, %{room_id: room_id, text: text}, server) do<br>message = Chat.create_message(room_id, text)<br>put_broadcast(server, {:room, room_id}, :add_message, message: message)<br>end
def action(:add_message, %{message: message}, component) do<br>put_state(component, messages: [message | component.state.messages])<br>end<br>end
The publisher names only the channel and the action, never a component - which is what lets one broadcast update many clients at once. Channels are structured, typed values (:notifications, {:room, 42}, {:doc, "abc-123", "v2"}), not raw topic strings, so a malformed channel is rejected instead of routing nowhere. Alongside the application channels you define, three built-in identity channels - {:instance, id}, {:session, id}, and {:user, id} - let you address a single tab, a session, or every device a user is signed in on. Those ids come from three new server struct fields - instance_id, session_id, and user_id - joined by subscriptions and broadcasts for inspecting what you've queued.
The handler calls above (put_subscription, put_broadcast) also have Hologram.Realtime counterparts for code that runs outside a handler - a background job, a worker, or existing Phoenix code. So a finished job can toast every one of a user's open tabs:
Hologram.Realtime.broadcast_action({:user, user_id}, :show_toast, text: "Upload complete")
The full guide - subscription lifecycle, authorization, and delivery semantics - lives in the Realtime documentation.
The with Special Form
Hologram now supports Elixir's with special form on the client. Thanks to Robert Prehn (@prehnRA), who opened the issue (#690) and implemented the bulk of it.
with {:ok, user} fetch_user(id),<br>{:ok, account} fetch_account(user) do<br>put_state(component, account: account)<br>else<br>{:error, reason} -> put_state(component, error: reason)<br>end
AI Assistant Support
Coding assistants are everywhere now, but Hologram is unusual: it's Elixir, yet it runs in the browser, and its conventions don't match LiveView's. Left to guess, assistants tend to suggest patterns that don't apply. v0.9 gives them the guidance they need (#779).
There are now machine-readable docs and usage rules so tools like Claude Code, Cursor, and others understand how Hologram actually works:
llms.txt and llms-full.txt - LLM-friendly documentation, served on the Hologram website
usage-rules.md - conventions and gotchas shipped with the package, compatible with the usage_rules ecosystem
mix holo.gen.agents_md and mix holo.gen.claude_md - generate an AGENTS.md or CLAUDE.md for your project
The result: assistants stop suggesting LiveView patterns that don't apply and start writing idiomatic Hologram.
Faster Dev Cycles with mix holo
⚠️ Heads up: behavior change
After upgrading, Hologram's compiler and runtime no longer start automatically in dev and test - this changes how you run your app in development.
The reason: Hologram's compiler adds startup time, which hurt the tight edit-test loop of TDD (#254, reported by @frankdugan3). v0.9 skips it unless you opt in (#767).
When you want the full app with client-side compilation, start it with the new task:
$ mix holo
Or set HOLOGRAM_START=1 to opt in for a given command. Your test suite and iterative development get markedly faster, because you're not paying the compiler's cost on every run unless you ask for it.
Quality-of-Life Improvements
Optional action/3, command/3, and init/2 callbacks. Thanks to Andrew Haust (@sodapopcan), pages and components now declare action/3,...