Erlang/OTP web framework
Real-time web apps in plain Erlang
Write views as Erlang terms. Arizona renders them on the server, keeps each page live over a WebSocket, and sends only the values that changed.
Arizona is in 0.x: the core is tested and in use, but the API can still change between minor versions.
Example
A live counter, in full
No JavaScript to write. A click is an event on the server, and the reply patches the one number that changed.
-module(counter).
-include_lib("arizona/include/arizona_stateful.hrl").
-export([mount/1, render/1, handle_event/3]).
mount(_Bindings) ->
{#{id => ~"counter", count => 0}, #{}}.
render(Bindings) ->
?html(
{'div', [{id, ?get(id)}], [
{span, [], [?get(count)]},
{button, [{az_click, arizona_js:push_event(~"inc")}], [~"+"]}
]}
).
handle_event(~"inc", _Payload, #{count := Count} = Bindings) ->
{Bindings#{count => Count + 1}, #{}, []}.
[{arizona, [
{server, #{
adapter => arizona_roadrunner_server,
transport_opts => [{port, 8080}],
routes => [
%% The page, its WebSocket and the client runtime.
{live, ~"/", counter, #{
layouts => [{my_layout, render}]
}},
{ws, ~"/ws", #{}},
{asset, ~"/assets", {priv_dir, arizona, "static/assets/js"}}
]
}}
]}].%% Render views to HTML files. A layout that loads the static client
%% (arizona-static.min.js) and calls start() keeps the browser-side
%% commands working, with no server: this site is built this way.
{Written, []} = arizona_static:generate(~"dist", [
{home_page, ~"index.html"},
{about_page, ~"about/index.html"}
], #{layouts => [{site_layout, render}]}).This page
Interactive, with no server
This site is generated by arizona_static and served as plain files. Its controls are the same event attributes a live page uses; Arizona's static client runs them in the browser, with no WebSocket.
The theme switch sets a value the browser owns
arizona_js:set_all(~"theme", ~"dark")Each tab sets the one the panels and the guide read
arizona_js:set_all(~"tab", ~"config")Get started opens a modal dialog
arizona_js:show_modal(~"#quick-start")Every code block copies itself
arizona_js:copy_to_clipboard(~"#code-view")The guide floats in its own window, and still drives the page
arizona_js:request_pip(~"guide")
Example guide
Pop it out: it follows the page, and drives it.
- ?get(count)
- Reads a binding. Only this slot re-renders when it changes.
- arizona_js:push_event(~"inc")
- The click becomes an event on the server.
- handle_event/3
- Returns the new bindings; the reply patches one number.
- adapter
- The server package: Roadrunner or Cowboy.
- {live, Path, View, Opts}
- A page that stays live over its socket.
- {ws, ~"/ws", #{}}
- The WebSocket every live page connects to.
- {asset, ...}
- Serves the client runtime from Arizona's priv.
- arizona_static:generate/3
- Renders views to HTML files, no server.
- arizona-static.min.js
- Runs the page's commands in the browser.
- start()
- What this site calls instead of connect('/ws').
Features
Why Arizona
Live by default
The first load is plain HTML. After that each page stays live over a WebSocket, and PubSub pushes updates to every connected tab.
Minimal updates
Templates split into static and dynamic parts at compile time. Statics go once; an update sends only the slots whose data changed.
Plain Erlang templates
Views are {Tag, Attrs, Children} terms the compiler checks. Streams, client-owned state and SPA navigation are built in.
BEAM reliability
Every live page is a supervised process. Deploys drain sessions gracefully and clients reconnect on their own.
Beyond the browser
The same views and diff engine drive native Android and iOS clients, and render to a terminal over a TTY or SSH.
Static when you want it
Generate pages like this one from the same views, with browser-side commands and no server to run.
Servers
Runs on the server you already have
The HTTP and WebSocket server is an adapter package, so Arizona fits in beside what you run today.