Announcing Phoenix Playground
Elixir v1.12 added Mix.install/2
, an ability to install and use dependencies which is especially useful in IEx sessions and single-file scripts. Ever since this capability existed, I wanted to create one particular kind of single-file scripts: single-file Phoenix LiveView apps. Phoenix Core Team member Gary Rennie created just that and it has been extremely helpful for prototyping, easily sharing Phoenix code, and even submitting Phoenix LiveView bug reports.
I kept thinking there must be an even easier way and in late 2023 I had an early prototype that removed all unnecessary boilerplate. Turns out I wasn’t alone, @lubien had a similar idea and created liveview_playground
. We decided to join forces and today I’m happy to announce a new project, Phoenix Playground, the easiest way to run single-file Phoenix applications.
Demo
Create a demo_live.exs
file:
Mix.install([
{:phoenix_playground, "~> 0.1.0"}
])
defmodule DemoLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0)}
end
def render(assigns) do
~H"""
<span><%= @count %></span>
<button phx-click="inc">+</button>
<button phx-click="dec">-</button>
<style type="text/css">
body { padding: 1em; }
</style>
"""
end
def handle_event("inc", _params, socket) do
{:noreply, assign(socket, count: socket.assigns.count + 1)}
end
def handle_event("dec", _params, socket) do
{:noreply, assign(socket, count: socket.assigns.count - 1)}
end
end
PhoenixPlayground.start(live: DemoLive)
and run it:
That’s it!
Phoenix Playground ships with all the basic pieces (endpoint, router, layout, etc) so you can focus on just your app. Besides Mix.install
and PhoenixPlayground.start
, the rest of the file is just a “vanilla” LiveView module and that is by design. It means you can drop that module into any Phoenix project and it should just work.
Phoenix Playground supports live code reloading: change the file, save it, and you’ll see updates right away, without even losing your LiveView state. It also streams real-time server logs to the browser development console out of the box too.
(State-preserving reloads require upcoming Phoenix LiveView 1.0.0-rc.1, i.e. add {:phoenix_live_view, github: "phoenixframework/phoenix_Live_view", override: true}
to your Mix.install
in the meantime!)
Phoenix LiveView ships with fantastic testing infrastructure. It feels like you are writing end-to-end tests with a browser but they are actually running without it and thus are blazingly fast. Phoenix Playground has built-in support for testing too:
Mix.install([
{:phoenix_playground, "~> 0.1.0"}
])
defmodule DemoLive do
use Phoenix.LiveView
def mount(_params, _session, socket) do
{:ok, assign(socket, count: 0)}
end
def render(assigns) do
~H"""
<span>Count: <%= @count %></span>
<button phx-click="inc">+</button>
"""
end
def handle_event("inc", _params, socket) do
{:noreply, update(socket, :count, &(&1 + 1))}
end
end
Logger.configure(level: :info)
ExUnit.start()
defmodule DemoLiveTest do
use ExUnit.Case
use PhoenixPlayground.Test, live: DemoLive
test "it works" do
{:ok, view, html} = live(build_conn(), "/")
assert html =~ "Count: 0"
assert render_click(view, :inc, %{}) =~ "Count: 1"
assert render_click(view, :inc, %{}) =~ "Count: 2"
end
end
$ elixir demo_live_test.exs
Running ExUnit with seed: 53183, max_cases: 16
.
Finished in 0.05 seconds (0.01s on load, 0.00s async, 0.04s sync)
1 test, 0 failures
Finally, Phoenix Playground also allows you to write good old controllers and even just plugs, all with code reloading out of the box.
I’ve been using Phoenix Playground to experiment with new ideas, learn new libraries, reproduce issues from our Elixir Development Subscription clients, and easily share all of this with people since it’s all encapsulated into single files. I can’t wait to hear what you will build with Phoenix Playground.
Happy hacking!