← docs.speechwave.live

HEEx template syntax

Outputting values

SyntaxMeaning
{@variable}Output an assign value (HTML-escaped)
{expression}Output any Elixir expression (HTML-escaped)
{"string #{interpolation}"}Output interpolated string

Control flow

<%= if @condition do %>
  ...shown when true...
<% else %>
  ...shown when false...
<% end %>

<%= for item <- @list do %>
  ...repeated for each item...
<% end %>

<%= case @value do %>
  <% :a -> %> ...
  <% :b -> %> ...
<% end %>

<%= cond do %>
  <% @count > 10 -> %> ...
  <% @count > 0 -> %>  ...
  <% true -> %>        ...default...
<% end %>

<%= %> outputs the result. <% %> (no =) executes but doesn't output. <%!-- --%> is an HTML comment that is stripped from output.

LiveView events

AttributeTriggersHandler
phx-click="name"Clickhandle_event("name", params, socket)
phx-change="name"Form field changehandle_event("name", params, socket)
phx-submit="name"Form submithandle_event("name", params, socket)
phx-value-key={val}n/aAdds %{"key" => val} to params
phx-hook="Name"n/aAttaches a JavaScript hook
data-confirm="msg"n/aShows browser confirm dialog before event

Values from phx-value-* are always strings. Use String.to_integer/1 in the handler if you need an integer.

Navigation

AttributeBehavior
navigate={~p"/path"}Full LiveView navigation (new mount)
patch={~p"/path"}Same LiveView, new URL (calls handle_params)
href={~p"/path"}Traditional full-page navigation

Components

<!-- Function component (defined in CoreComponents or same module) -->
<.button>Click me</.button>
<.input field={@form[:title]} type="text" />
<.icon name="hero-trash" class="w-4 h-4" />

<!-- Module component -->
<Layouts.app flash={@flash}>
  ...content...
</Layouts.app>

<!-- Form component -->
<.form for={@form} phx-change="validate" phx-submit="save">
  <.input field={@form[:email]} type="email" />
  <button type="submit">Save</button>
</.form>

<!-- Link component -->
<.link navigate={~p"/dashboard"}>Dashboard</.link>
<.link href={~p"/users/log-out"} method="delete">Log out</.link>

.component (with dot) = function component. Module.component = component from another module. Both receive attributes as assigns.

Dynamic attributes

<!-- Static attribute -->
class="text-sm"

<!-- Dynamic attribute -->
class={@dynamic_class}

<!-- Conditional class list -->
class={[
  "base-class",
  @active && "text-bold",
  if(@selected, do: "bg-blue", else: "bg-gray")
]}

<!-- Dynamic ID -->
id={"item-#{@item.id}"}

The ~p sigil

~p"/dashboard"                    # verified route (compile-time checked)
~p"/sessions/#{session.id}"       # with interpolation
~p"/t/#{@talk.slug}"              # using assigns

~p routes are checked at compile time. If the route doesn't exist in router.ex, you get a warning.