Open-source project
jfyne/live avatar
jfyne/live

live renders interactive Go pages over a websocket

Live views and components for golang

748 stars35 forksGoMIT

At a glance

What is it?
live is a Go library for real-time user experiences with server-rendered HTML, in the shape of Phoenix LiveView, where the server holds the state and pushes diffs to the browser over a websocket. It suits a Go team that wants interactive pages without a JavaScript framework, and it asks you to design your UI as server-rendered state rather than client-side components.
Who is it for?
Adopt live when your team writes Go and wants interactive web pages without adopting React, Vue or Angular, and when your application is state heavy enough that a server round trip per interaction is acceptable. Do not adopt it if you need offline first client behaviour, if your pages are mostly static, or if you want the component model of a JavaScript framework.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

LiveView in Go, with the state on the server

live provides real-time user experiences with server-rendered HTML in Go, and it says plainly that it is inspired by and borrows from Phoenix LiveViews. The positioning is a replacement for React, Vue, Angular and the rest, so you can write an interactive web app using only Go and its templates. The mechanism follows the LiveView model. The browser connects over a websocket, the server holds the state, and when something changes the server computes the difference between the last render and the new one and pushes those diffs to the browser. The key line in the documentation is that Live will calculate the diff between the last time it rendered and now, produce a set of diffs and push them to the browser to update. So the browser is not a small application, it is a rendering target. The dependency list in go.mod is short and tells you the transport, with coder/websocket for the connection, rs/xid for identifiers, golang.org/x/net and golang.org/x/time, and go-cmp for the diffing.

The module is fetched with:

bash
go get github.com/jfyne/live

The handler is compatible with net/http, which is the point

The structures provided in this package are compatible with net/http, so they play nicely with middleware and other frameworks. That single sentence decides whether this library fits your stack. You are not adopting a new web framework. You create a handler, register it on the standard mux, and the only new thing in your routing table is a route that serves a websocket instead of returning a response. The example does exactly that, with http.Handle for the page and a separate http.Handle for the JavaScript. The handler itself has three moving parts, and the first example is worth reading as the canonical shape. A MountHandler is called on the initial HTTP load and then on the initial web socket connection, and it is where you create the initial state. A RenderHandler produces the HTML, either by hand or through the provided WithTemplateRenderer which works with html/template. And a HandleEvent call registers each event name to a function that receives the request context, the socket and the params, and returns the new state.

Events are named strings in the markup and functions in Go

The thermostat example shows the whole loop, and it is small enough to read in one sitting. The model is a struct with a single float, the current temperature. The mount function returns a model with C set to 19.5, and the documentation notes that the data returned in the mount function will automatically be assigned to the socket. The event handlers are ordinary Go functions that load the model from the socket, adjust the value, and return it. The buttons in the template name their events with a live-click attribute, and the server registers the matching names with h.HandleEvent. The params carry query string parameters and any live-value- bindings. The helper function NewThermoModel uses s.Assigns() to pull the model out of the socket, and if it is not already there it creates a fresh one, which is the pattern for initialising state lazily. A second handler function shows the ergonomics of this: a handler can be registered for an event name that appears in the markup, and the server pushes only the diff.

Components factor out the event logic

Live can also render components, described as an easy way to encapsulate event logic and make it repeatable across a page. The component model is built on the same mount and render ideas, with WithMount to set the initial state and WithRender to produce the output, and the greeter example shows a component that renders a greeting and includes the script tag. The components examples directory shows how to create them, and the world clocks example uses them. The components feature is what turns the library from a demo into something you can structure a page around, because a page that has six live controls can put each of them in a component with its own state and handlers instead of one handler function growing a switch statement. The same examples directory carries a chart, a chat, a cluster, an error, pagination, prefill, todo and uploads example, and there is an Alpinejs example, which suggests the library is designed to coexist with a small amount of client-side JavaScript rather than replace it entirely.

live.js is required, and the build pipeline has a JavaScript step

There is one piece of ceremony that catches people out, and the documentation points at it directly. Notice the script tag, because Live's javascript is embedded within the library for ease of use, and it is required to be included for it to work. In the examples the tag is a script element with a src of /live.js, and the library serves that route itself through a Javascript handler value. If you want to serve it from your own asset pipeline instead, there is a companion npm package, @jfyne/live, which you can add to any existing web app build pipeline. The justfile shows the other half of the pipeline. The build target installs the web node_modules if they are missing, runs go generate on web/build.go, and then runs embedmd over the README. That last step is a nice touch and a slightly unusual one, because it means the code examples in the README are generated from the real example files in the repository, so the documentation cannot drift from the code. The justfile also fetches embedmd itself if it is not on the machine.

Navigation, uploads and the wider surface

Beyond the core loop the library covers the parts of an application that need a server anyway. Navigation uses the browser pushState API to update query parameters, and it can be done from both the client side and the server side. On the client the live-patch handler is placed on an anchor tag, and it reads the href attribute in order to apply the URL patch, so a link that would normally navigate becomes a partial update instead. On the server side the same query parameter handling is available through the Params type, which carries the query string alongside the event bindings. The tree at the top level shows the rest of the surface, with upload.go for file uploads, pubsub.go for publishing to multiple sockets, diff.go for the diffing that drives the updates, and a page/ directory that holds the component implementation. The diff machinery is the core of the whole idea, and the presence of diff_test.go, event_test.go and socket_race_test.go says the library treats the tricky parts as the ones worth testing.

Editorial conclusion

Adopt live when your team writes Go and wants interactive web pages without adopting React, Vue or Angular, and when your application is state heavy enough that a server round trip per interaction is acceptable. Do not adopt it if you need offline first client behaviour, if your pages are mostly static, or if you want the component model of a JavaScript framework. Verify first that the live.js script is served on every page that needs it, that your reverse proxy passes websocket connections through, and that the event handler model, where the server returns a new state and the library computes the diff, matches how you want client interaction to behave.

Frequently asked questions

How do I install live and add it to a Go project?

Run go get github.com/jfyne/live. The structures are compatible with net/http, so you create a handler, register it with http.Handle, and serve the live.js script on a separate route, as in the examples.

Why does my live page not update?

The documentation notes that Live's javascript is embedded within the library and is required to be included for it to work, so the page must include the script tag pointing at /live.js, which the library serves through a Javascript handler. You can also serve it from your own build pipeline with the companion npm package @jfyne/live.

How does live push updates to the browser?

An event handler is registered for an event name with HandleEvent, the handler loads the model from the socket, changes it and returns the new state. Live then calculates the diff between the last time it rendered and now, produces a set of diffs and pushes them to the browser over the websocket.

What are live components for?

They are a way to encapsulate event logic and make it repeatable across a page. A component provides its own mount function to set state and its own render function, and the components examples directory shows how to create them, with the world clocks example putting them to use.

Official sources

  1. Issues
  2. jfyne/live on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jfyne-live.svg)](https://hysenlabs.com/projects/jfyne-live)

Was this useful?