LiveView got braces: a complete guide to Phoenix LiveView v1.0.0's new curly brace syntax

Learn Phoenix LiveView is the comprehensive tutorial that teaches you everything you need to build a complex, realistic, fully-featured web app with Phoenix LiveView. Click here to learn more!

It’s finally here: 10 years since Phoenix was first released, and 5 years since the first commit to Phoenix LiveView, LiveView has reached v1.0.0! I wish huge congratulations to the Phoenix team, and I hope this news encourages more people to take a chance on my favorite framework.

v1.0.0 isn’t actually a huge jump from the previous version, v0.20. There’s only one backwards-incompatible change: the replacement of phx-feedback-for with Phoenix.Component.used_input?/2, which you can read about in the changelog (and also in my new course, which has a chapter that explains the new used_input?/2 function in detail 😉.)

Other than that, the release mostly contains minor enhancements and bug fixes, but there’s one major enhancement that will be the subject of this post: you can now use curly braces { … } as an alternative to <%= … %>:

<!-- old style -->
<%= @something %>

<!-- new style -->
{@something}

The old style still works, and won’t be removed, but the new style makes HEEx more consistent. Previously you’d use {…} to interpolate data into HTML attributes and <%= … %> to render data visibly, but now you can use {…} everywhere:

<!-- old style -->
<div id={@id} class={["block mt-2 mx-auto", @class]} >
  <%= @user.name %>
</div>

<!-- new style -->
<div id={@id} class={["block mt-2 mx-auto", @class]} >
  {@user.name}
</div>

That’s the tl;dr. In the rest of this post I’ll cover everything else you need to know about this new feature.

mix format

Once you upgrade to v1.0.0, then mix format will automatically convert <%= … %> tags to {…}. So you can migrate your entire codebase to the new syntax with a single command. Here’s me migrating the codebase for Slax, the app you’ll build at LearnPhoenixLiveView.com:

To disable this new behaviour, add migrate_eex_to_curly_interpolation: false to your formatter config:

 # .formatter.exs
 [
   import_deps: [:ecto, :ecto_sql, :phoenix],
   subdirectories: ["priv/*/migrations"],
   plugins: [Phoenix.LiveView.HTMLFormatter],
+  migrate_eex_to_curly_interpolation: false
   inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"]
 ]

Want more posts like this in your inbox?

No spam. Unsubscribe any time.

<script> and <style> tags

{ and } are meaningful tokens in Javascript and CSS. So to avoid a world of pain, they’re not parsed as HEEx within <script> or <style> tags. Instead you must stick to the old syntax:

 <!-- this won't work: -->
 <script>
   window.URL = "{@my_url}"
 </script>

 <!-- do this instead: -->
 <script>
   window.URL = "<%= @my_url %>"
 </script>

Rendering a “{” for real.

Sometimes you actually want to display a { on the page, and not have it be parsed as HEEx. To achieve this you can write:

  • the HTML escape character &lbrace;, or:
  • <%= "{" %>.

Note that {"{"} won’t work, which is a rare case where {…} isn’t exactly equivalent to <%= … %>. As José Valim explains, supporting {"{"} would require significantly increasing the complexity of the implementation, so it isn’t worth it.

phx-no-curly-interpolation

To disable the new syntax entirely within a particular tag, add the attribute phx-no-curly-interpolation.

For example, here’s some HEEx from Slax that’s used to render the room name in ChatRoomLive:

 <h1 class="text-sm font-bold leading-none">
   #{@room.name}</h1>

If I add phx-no-curly-interpolation

-<h1 class="text-sm font-bold leading-none">
+<h1 class="text-sm font-bold leading-none" phx-no-curly-interpolation>
   #{@room.name}</h1>

… then the results are self-explanatory:

<% … %> is not dead

As a quick recap, here’s how things worked before v1.0.0. All of the below syntax is still perfectly valid.

To run Elixir code inside HEEx templates and output its result to the page, you can use <%= … %> (and now { … }):

<!-- so this HEEx… -->
<div><%= 2 + 2 %></div>
<!-- … renders this HTML: -->
<div>4<div>
<!-- and this new syntax is exactly equivalent: -->
<div>{2 + 2}</div>

Using <% instead of <%= runs the code but doesn’t render the result:

<!-- so this HEEx… -->
<div><% 2 + 2 %></div>
<!-- …  simply renders this: -->
<div><div>

<% … %> is useful if, for example, you want to assign a value to a variable without rendering it, or call a function for its side effect (like IO.puts/1).

<% is also used before certain keywords like else and end:

<%= if @condition do %>
  <div>It's true!</div>
<% else %>
  <div>It's false!</div>
<% end %>

HEEx “code” between <%!-- and --%> is a comment that doesn’t get executed or output. (You can also embed comments between <%# and %>, but this is deprecated.)

Note the difference between a HEEx comment <%!-- … --%> and a normal HTML comment <!-- … -->. HTML comments don’t affect the page’s appearance, but they’re still rendered in the HTML document, meaning that users can see them in the page source. HEEx comments, however, aren’t output at all or rendered anywhere, so your users can’t read them.

{ … } now serves as a (mostly) drop-in replacement for <%= … %>, but it doesn’t replace <% … %> or <%!-- … --%>. So the above if/else example can’t be precisely rewritten with the new syntax. Likewise for other multiline constructs like for or case.

However, don’t forget that HEEx provides the special attributes :if and :for which can be used instead of if and for:

<!-- so this: -->
<%= if @condition do %>
  <div>It's true!</div>
<% end %>

<!-- can be rewritten like this: -->
<div :if={@condition}>It's true!</div>

<!-- and this: -->
<ul>
  <%= for user <- @users do %>
    <li>{user.name}</li>
  <% end %>
</ul>

<!-- can be rewritten like this: -->
<ul>
  <li :for={user <- @users}>{user.name}</li>
</ul>

There’s no :else attribute. If you need an “else”, you can write :if again but invert the condition:

<div :if={@condition}>It's true!</div>
<div :if={!@condition}>It's false!</div>

But this is a bit repetitive. You can always just write <%= if … do %><% else %>. Just because the {…} syntax exists, doesn’t mean you have to use it.


That, I think, covers everything you need to know about LiveView’s new curly brace syntax. I look forward to seeing what comes next in future LiveView releases!

Want more posts like this in your inbox?

No spam. Unsubscribe any time.