{"id":31528429,"url":"https://github.com/arizona-framework/arizona","last_synced_at":"2026-04-26T22:00:58.585Z","repository":{"id":246107962,"uuid":"820110343","full_name":"arizona-framework/arizona","owner":"arizona-framework","description":"A web framework for Erlang/OTP","archived":false,"fork":false,"pushed_at":"2026-04-22T16:03:32.000Z","size":14512,"stargazers_count":67,"open_issues_count":14,"forks_count":4,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-04-22T18:07:32.535Z","etag":null,"topics":["arizona","arizona-framework","beam","erlang","gfm","github-flavored-markdown","html","html5","markdown","otp","pubsub","real-time","ssg","static-site-generator","webdevelopment","webframework"],"latest_commit_sha":null,"homepage":"https://arizona-framework.org","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arizona-framework.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["williamthome"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":["https://www.buymeacoffee.com/williamthome"]}},"created_at":"2024-06-25T20:26:25.000Z","updated_at":"2026-04-08T04:35:09.000Z","dependencies_parsed_at":"2024-06-25T22:28:08.208Z","dependency_job_id":"081dcf65-1676-4c15-859b-fd56859e001f","html_url":"https://github.com/arizona-framework/arizona","commit_stats":null,"previous_names":["arizona-framework/arizona"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/arizona-framework/arizona","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arizona-framework%2Farizona","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arizona-framework%2Farizona/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arizona-framework%2Farizona/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arizona-framework%2Farizona/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arizona-framework","download_url":"https://codeload.github.com/arizona-framework/arizona/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arizona-framework%2Farizona/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32314116,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T21:09:39.134Z","status":"ssl_error","status_checked_at":"2026-04-26T21:09:21.240Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arizona","arizona-framework","beam","erlang","gfm","github-flavored-markdown","html","html5","markdown","otp","pubsub","real-time","ssg","static-site-generator","webdevelopment","webframework"],"created_at":"2025-10-03T23:40:42.539Z","updated_at":"2026-04-26T22:00:58.567Z","avatar_url":"https://github.com/arizona-framework.png","language":"Erlang","funding_links":["https://github.com/sponsors/williamthome","https://www.buymeacoffee.com/williamthome"],"categories":["Web"],"sub_categories":["Web Frameworks"],"readme":"# Arizona\n\n[![Erlang CI](https://github.com/arizona-framework/arizona/actions/workflows/erlang.yml/badge.svg?branch=main)](https://github.com/arizona-framework/arizona/actions/workflows/erlang.yml)\n[![Node.js CI](https://github.com/arizona-framework/arizona/actions/workflows/node-js.yml/badge.svg?branch=main)](https://github.com/arizona-framework/arizona/actions/workflows/node-js.yml)\n[![Hex.pm](https://img.shields.io/hexpm/v/arizona.svg)](https://hex.pm/packages/arizona)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/arizona/)\n[![npm version](https://img.shields.io/npm/v/@arizona-framework/client.svg)](https://www.npmjs.com/package/@arizona-framework/client)\n[![License](https://img.shields.io/hexpm/l/arizona.svg)](https://github.com/arizona-framework/arizona/blob/main/LICENSE.md)\n\n![arizona logo](https://raw.githubusercontent.com/arizona-framework/arizona/main/assets/logo.jpg)\n\nArizona is a real-time web framework for Erlang/OTP. It renders HTML on the server, diffs changes at\nthe template level, and pushes minimal updates to the browser over WebSocket.\n\nTemplates are plain Erlang terms compiled via parse transform. The server owns the state; the client\nis a thin DOM patcher.\n\n## 🚧 Status\n\nArizona is in `0.x`. The core is functional and covered by tests, but the API may change between\nminor versions. Pin an exact version in your deps (e.g. `{arizona, \"0.1.0\"}`) if you need stability\nacross upgrades.\n\n## Features\n\n- **SSR + live updates** -- HTML on first load, WebSocket diffs after\n- **Erlang-native templates** -- `{Tag, Attrs, Children}` tuples compiled by parse transform\n- **Compile-time static/dynamic split** -- statics sent once, only dynamics cross the wire\n- **Three handler kinds** -- `arizona_view` (route pages), `arizona_stateful` (components),\n  `arizona_stateless` (pure templates)\n- **Streams** -- keyed collections with insert/delete/update/move/sort/limit\n- **SPA navigation** -- `az_navigate` links, server renders the next page over WebSocket\n- **PubSub** -- cross-view, cross-tab messaging via `arizona_pubsub`\n- **Route middlewares** -- gate or rewrite requests before mount (auth, sessions, URL projection)\n- **On-mount hooks** -- per-route pipeline that runs before every mount, including navigate\n- **Element hooks** -- client-side `mounted`/`updated`/`destroyed` callbacks via `az_hook`\n- **Dev-mode hot reload** -- `fs` watcher recompiles changed `.erl` files and pushes reload events\n- **Pluggable transport** -- cowboy adapter built-in; write your own to swap it out\n\n## Requirements\n\n- Erlang/OTP 28 or later\n\n## Installation\n\nAdd Arizona to your `rebar.config` dependencies. Cowboy is required for the built-in\nHTTP/WebSocket transport; skip it only if you write your own `arizona_req` adapter.\n\n```erlang\n{deps, [\n    {arizona, \"~\u003e 0.1\"},\n    cowboy\n]}.\n```\n\nTo track unreleased changes, swap the version for a git ref:\n\n```erlang\n{arizona, {git, \"https://github.com/arizona-framework/arizona.git\", {branch, \"main\"}}}\n```\n\nThe client JavaScript ships baked into the rebar3 build (`priv/static/assets/js/*.min.js`). If\nyou need to bundle it yourself or consume it from a non-Erlang backend, install via npm:\n\n```bash\nnpm install @arizona-framework/client\n```\n\n```js\nimport { connect } from '@arizona-framework/client';\n\nconnect('/ws');\n```\n\n## Quick start\n\nA page with an embedded counter.\n\n### 1. The counter component\n\n`id` and initial `count` come from the parent via `?stateful` (step 2), so `mount/1` just passes\nthem through:\n\n```erlang\n%% src/my_counter.erl\n-module(my_counter).\n-include_lib(\"arizona/include/arizona_stateful.hrl\").\n-export([mount/1, render/1, handle_event/3]).\n\nmount(Bindings) -\u003e\n    {Bindings, #{}}.\n\nrender(Bindings) -\u003e\n    ?html(\n        %% id must be on the root element -- if it changes, the component is remounted\n        {'div', [{id, ?get(id)}], [\n            {button, [{az_click, arizona_js:push_event(~\"dec\")}], [~\"-\"]},\n            {span, [], [~\" Count: \", ?get(count), ~\" \"]},\n            {button, [{az_click, arizona_js:push_event(~\"inc\")}], [~\"+\"]}\n        ]}\n    ).\n\nhandle_event(~\"inc\", _Payload, Bindings) -\u003e\n    {Bindings#{count =\u003e maps:get(count, Bindings) + 1}, #{}, []};\nhandle_event(~\"dec\", _Payload, Bindings) -\u003e\n    {Bindings#{count =\u003e maps:get(count, Bindings) - 1}, #{}, []}.\n```\n\n`?get(count)` registers `count` as a dependency of that template slot. When `handle_event` returns\nnew bindings, only slots whose tracked keys changed re-render -- the `\u003cspan\u003e` patches; the buttons\ndon't.\n\nThe tuples carry more than just bindings: `mount/1` returns `{Bindings, Resets}` (an explicit\nslot-reset map -- usually `#{}`), and `handle_event/3` returns `{Bindings, Resets, Effects}` where\n`Effects` is a list of `arizona_js` commands (`set_title`, `navigate`, …) executed on the client.\n\n### 2. The parent page\n\nA **view** is the route's root handler. It receives initial bindings plus the request:\n\n```erlang\n%% src/my_page.erl\n-module(my_page).\n-include_lib(\"arizona/include/arizona_view.hrl\").\n-export([mount/2, render/1]).\n\nmount(Bindings, _Req) -\u003e\n    {Bindings, #{}}.\n\nrender(Bindings) -\u003e\n    ?html(\n        {main, [{id, ?get(id)}], [\n            {h1, [], [~\"Counter demo\"]},\n            %% id is required -- it's how the diff engine routes patches to this component\n            ?stateful(my_counter, #{id =\u003e ~\"counter\", count =\u003e 0})\n        ]}\n    ).\n```\n\n### 3. The layout\n\nThe HTML shell. Loads the client runtime that connects over WebSocket:\n\n```erlang\n%% src/my_layout.erl\n-module(my_layout).\n-include_lib(\"arizona/include/arizona_stateless.hrl\").\n-export([render/1]).\n\nrender(Bindings) -\u003e\n    ?html([\n        ~\"\u003c!DOCTYPE html\u003e\",\n        {html, [], [\n            {head, [], [\n                {meta, [{charset, ~\"utf-8\"}]},\n                {title, [], [?get(title, ~\"Arizona\")]}\n            ]},\n            {body, [], [\n                ?inner_content,\n                {script, [{type, ~\"module\"}], [\n                    ~\"\"\"\n                    import { connect } from '/assets/arizona.min.js';\n                    connect('/ws');\n                    \"\"\"\n                ]}\n            ]}\n        ]}\n    ]).\n```\n\n### 4. Configure the server\n\nAdd `arizona` to your app's `applications` list in `.app.src`:\n\n```erlang\n{applications, [kernel, stdlib, cowboy, arizona]}\n```\n\nThen declare routes in `config/sys.config`:\n\n```erlang\n[{arizona, [\n    {server, #{\n        routes =\u003e [\n            {live, ~\"/\", my_page, #{\n                layouts =\u003e [{my_layout, render}],\n                bindings =\u003e #{id =\u003e ~\"page\", title =\u003e ~\"Counter demo\"}\n            }},\n            {ws, ~\"/ws\", #{}},\n            {asset, ~\"/assets\", {priv_dir, arizona, \"static/assets/js\"}}\n        ]\n    }}\n]}].\n```\n\n### 5. Run it\n\n```bash\nrebar3 shell\n```\n\nOpen \u003chttp://localhost:4040\u003e and click the buttons -- the server renders the initial HTML, then\npushes minimal diffs over WebSocket as the count changes.\n\n## Documentation\n\nSee [docs/architecture.md](docs/architecture.md) for the full architecture reference -- module\nbreakdown, op codes, dev-mode file watchers, custom schemes/proto_opts, and imperative startup.\n\n## Sponsors\n\nIf you like Arizona, please consider [sponsoring me](https://github.com/sponsors/williamthome).\nI'm thankful for your never-ending support ❤️\n\nI also accept coffees ☕\n\n[![\"Buy Me A Coffee\"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/williamthome)\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for development setup,\ntesting guidelines, and contribution workflow.\n\n### Contributors\n\n\u003ca href=\"https://github.com/arizona-framework/arizona/graphs/contributors\"\u003e\n  \u003cimg\n    src=\"https://contrib.rocks/image?repo=arizona-framework/arizona\u0026max=100\u0026columns=10\"\n    width=\"15%\"\n    alt=\"Contributors\"\n  /\u003e\n\u003c/a\u003e\n\n## Star History\n\n\u003ca href=\"https://star-history.com/#arizona-framework/arizona\"\u003e\n  \u003cpicture\u003e\n    \u003csource\n      media=\"(prefers-color-scheme: dark)\"\n      srcset=\"https://api.star-history.com/svg?repos=arizona-framework/arizona\u0026type=Date\u0026theme=dark\"\n    /\u003e\n    \u003csource\n      media=\"(prefers-color-scheme: light)\"\n      srcset=\"https://api.star-history.com/svg?repos=arizona-framework/arizona\u0026type=Date\"\n    /\u003e\n    \u003cimg\n      src=\"https://api.star-history.com/svg?repos=arizona-framework/arizona\u0026type=Date\"\n      alt=\"Star History Chart\"\n      width=\"100%\"\n    /\u003e\n  \u003c/picture\u003e\n\u003c/a\u003e\n\n## License\n\nCopyright (c) 2023-2026 [William Fank Thomé](https://github.com/williamthome)\n\nArizona is open-source under the Apache 2.0 License on\n[GitHub](https://github.com/arizona-framework/arizona).\n\nSee [LICENSE.md](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farizona-framework%2Farizona","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farizona-framework%2Farizona","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farizona-framework%2Farizona/lists"}