Skip to content

Instantly share code, notes, and snippets.

@aerosol
Forked from ukutaht/minimal_liveview.exs
Last active September 10, 2025 06:04
Show Gist options
  • Save aerosol/57bb9ebf39c64ff91a15ed09d7ba9909 to your computer and use it in GitHub Desktop.
Save aerosol/57bb9ebf39c64ff91a15ed09d7ba9909 to your computer and use it in GitHub Desktop.
Liveview dynamic rendering issue
#!/usr/bin/env elixir
# Minimal Phoenix LiveView single file example
# Run with: elixir minimal_liveview.exs
# Access at: http://localhost:4001
Application.put_env(:minimal_app, MinimalApp.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4001],
server: true,
live_view: [signing_salt: "aaaaaaaa"],
secret_key_base: String.duplicate("a", 64),
debug_errors: true
)
Mix.install([
{:plug_cowboy, "~> 2.5"},
{:jason, "~> 1.0"},
{:phoenix, "~> 1.7"},
{:phoenix_live_view, "~> 1.0"}
])
defmodule MinimalApp.ErrorHTML do
def render(template, _assigns) do
Phoenix.Controller.status_message_from_template(template)
end
end
defmodule MinimalApp.Layouts do
use Phoenix.Component
def app(assigns) do
~H"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="csrf-token" content={Plug.CSRFProtection.get_csrf_token()} />
<title>Minimal LiveView</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/priv/static/phoenix.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/priv/static/phoenix_live_view.min.js"></script>
<style>
body { font-family: system-ui, sans-serif; margin: 40px; }
.container { max-width: 800px; }
button { padding: 8px 16px; margin: 4px; background: #3b82f6; color: white; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #2563eb; }
</style>
</head>
<body>
<div class="container">
<%= @inner_content %>
</div>
<script>
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket, {params: {_csrf_token: csrfToken}})
liveSocket.connect()
</script>
</body>
</html>
"""
end
end
defmodule MinimalApp.Components do
use Phoenix.Component
attr(:type, :string, default: nil)
attr(:class, :string, default: nil)
attr(:rest, :global, include: ~w(disabled form name value))
slot(:inner_block, required: true)
def button(assigns) do
~H"""
<button
type={@type}
class={@class}
{@rest}
>
{render_slot(@inner_block)}
</button>
"""
end
attr(:class, :string, default: "")
attr(:as, :any, default: nil)
slot(:inner_block, required: true)
def dropdown_trigger(assigns) do
{fun, assigns} = Map.pop(assigns, :as)
fun.(assigns)
end
end
defmodule MinimalApp.HomeLive do
use Phoenix.LiveView
import MinimalApp.Components
def mount(_params, _session, socket) do
{:ok, socket}
end
def render(assigns) do
~H"""
<.dropdown_trigger as={&MinimalApp.Components.button/1}>
inner block is required
</.dropdown_trigger>
"""
end
end
defmodule MinimalApp.Router do
use Phoenix.Router
import Phoenix.Controller
import Phoenix.LiveView.Router
pipeline :browser do
plug(:accepts, ["html"])
plug(:fetch_session)
plug(:fetch_live_flash)
plug(:put_root_layout, html: {MinimalApp.Layouts, :app})
plug(:protect_from_forgery)
end
scope "/", MinimalApp do
pipe_through(:browser)
live("/", HomeLive)
end
end
defmodule MinimalApp.Endpoint do
use Phoenix.Endpoint, otp_app: :minimal_app
@session_options [
store: :cookie,
key: "_minimal_app_key",
signing_salt: "aaaaaaaa"
]
socket("/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]])
plug(Plug.RequestId)
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
plug(Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()
)
plug(Plug.MethodOverride)
plug(Plug.Head)
plug(Plug.Session, @session_options)
plug(MinimalApp.Router)
end
# Start the application
{:ok, _} = Supervisor.start_link([MinimalApp.Endpoint], strategy: :one_for_one)
IO.puts("\n๐Ÿš€ Minimal Phoenix LiveView started!")
IO.puts("๐Ÿ“ฑ Open http://localhost:4001 in your browser")
IO.puts("๐Ÿ›‘ Press Ctrl+C to stop\n")
Process.sleep(:infinity)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment