wires

Version, currently main branch1 version
  • main branchlatestJun 14, 2026

github.com/garyhe0780/wires

server-driven live components for Crystal and Kemal. Inspired by Hotwire, Phoenix LiveView, and Laravel Livewire.

0 stars
0 dependents
License: MIT

Installation

# Add this to your shard.yml
dependencies:
  wires:
    github: garyhe0780/wires
    branch: main

main is a branch, not a release, so this tracks it rather than pinning a version.

Then run:

shards install

shard.yml

Crystal
>= 1.20.2
License
MIT
Author
gary
Targets
  • clock from examples/clock/app.cr
  • wires from src/wires.cr
  • layout from examples/layout/app.cr
  • counter from examples/counter/app.cr
  • targeted from examples/targeted/app.cr
  • dashboard from examples/dashboard/app.cr
  • docs_site from examples/docs_site/app.cr
  • resilient from examples/resilient/app.cr
  • user_card from examples/user_card/app.cr
  • navigation from examples/navigation/app.cr
  • contact_form from examples/contact_form/app.cr
  • readonly_stream from examples/readonly_stream/app.cr
  • static_component from examples/static_component/app.cr

Dependencies

Runtime Dependencies

  • kemal*github: kemalcr/kemal

README

Crystal Live

Server-driven live components for Crystal and Kemal. Inspired by Hotwire, Phoenix LiveView, and Laravel Livewire.

Crystal Live keeps all component state on the server, renders HTML with Declarative Shadow DOM for style isolation, and streams updates over WebSocket using the <?marker?> / <template for> patch protocol.

Installation

Add to your shard.yml:

dependencies:
  wires:
    github: your-org/wires

Then run shards install.

Quick start

require "kemal"
require "crystal_live"

class Clock < CrystalLive::LiveComponent
  def initialize(session_id : String)
    super("clock", session_id)
    spawn { loop { sleep 1.seconds; push_update } }
  end

  def render : String
    "<p>#{Time.utc.to_s("%H:%M:%S")}</p>"
  end
end

CrystalLive::KemalExtensions.mount!

get "/" do |context|
  session_id = CrystalLive::KemalExtensions.ensure_session_id(context)
  CrystalLive::KemalExtensions.page_html("Clock", Clock.new(session_id).marker_html)
end

Kemal.run

Registry macros

register_live_component :clock, Clock

get "/" do |context|
  session_id = CrystalLive::KemalExtensions.ensure_session_id(context)
  live_component :clock, session_id
end

Configuration

CrystalLive.configure do |config|
  config.ws_path = "/live"
  config.stream_path = "/live/stream"
  config.heartbeat_interval_ms = 30_000
end

CrystalLive::KemalExtensions.mount!

page_html embeds window.CrystalLiveConfig for the polyfill automatically.

Set config.debug = true to log WebSocket/SSE protocol activity on server and client.

See CHANGELOG.md for release history.

Examples

crystal run examples/static_component/app.cr
crystal run examples/clock/app.cr
crystal run examples/user_card/app.cr
crystal run examples/dashboard/app.cr
crystal run examples/counter/app.cr
crystal run examples/contact_form/app.cr
crystal run examples/targeted/app.cr
crystal run examples/readonly_stream/app.cr
crystal run examples/resilient/app.cr
crystal run examples/layout/app.cr
crystal run examples/docs_site/app.cr

Documentation

Run the documentation site locally:

crystal run examples/docs_site/app.cr
# open http://localhost:3000/docs/

Development

crystal spec

License

MIT