HEEx Integration

View Source

MDEx integrates with Phoenix LiveView's HEEx templates, allowing you to use Markdown alongside Phoenix components, phx-* bindings, and Elixir expressions.

When to Use HEEx Integration

Use HEEx integration when you need:

  • Phoenix components (like <.link>, <.button>, or custom components) inside Markdown
  • LiveView bindings (phx-click, phx-submit, etc.)
  • Elixir expressions that evaluate at runtime ({@var})

For static Markdown without Phoenix components, use the regular ~MD[...]HTML modifier or MDEx.to_html/2 instead.

Setup

Add use MDEx to your module to enable both require MDEx (for to_heex/2) and import MDEx.Sigil (for the ~MD sigil):

defmodule MyAppWeb.PageLive do
  use Phoenix.LiveView
  use MDEx

  # Now you can use ~MD[...]HEEX and MDEx.to_heex/2
end

Two Approaches

~MD[...]HEEX Sigil (Compile-time)

The preferred approach for LiveView templates. The Markdown is parsed at compile-time for optimal performance:

def render(assigns) do
  ~MD"""
  # Welcome, {@username}!

  <.link href={@profile_url}>View Profile</.link>
  """HEEX
end

MDEx.to_heex/2 Macro (Runtime)

For dynamic content or when the sigil isn't available. The template is evaluated at runtime:

def render(assigns) do
  markdown = fetch_markdown_from_database()

  MDEx.to_heex!(markdown, assigns: assigns)
end

Note: Calling to_heex/2 repeatedly at runtime may impact performance. Prefer the sigil when possible.

Using Assigns

Pass variables to your Markdown templates using the {@var} syntax:

def render(assigns) do
  ~MD"""
  Welcome back, **{@user.name}**!

  You have {@notification_count} unread notifications.
  """HEEX
end

The old <%= @var %> EEx syntax also works for compatibility.

Phoenix Components

Use any Phoenix component directly in your Markdown:

~MD"""
# Navigation

- <.link navigate={~p"/home"}>Home</.link>
- <.link navigate={~p"/about"}>About</.link>

<.button phx-click="save">Save Changes</.button>

<MyAppWeb.Components.card title={@card_title}>
  Card content here
</MyAppWeb.Components.card>
"""HEEX

Component imports are not automatic

MDEx does not automatically import components. To use function components with the dot notation:

  • Import Phoenix.Component for core components like <.link>
  • Import your app's components module (e.g., import MyAppWeb.CoreComponents)
  • Or use fully qualified names: <Phoenix.Component.link href="/">Home</Phoenix.Component.link>

In Phoenix applications, use MyAppWeb, :live_view typically handles these imports for you.

Elixir Expressions

Embed Elixir expressions using curly braces:

~MD"""
Today is _{Calendar.strftime(DateTime.utc_now(), "%B %d, %Y")}_

<%= for item <- @items do %>
  - {item.name}: **{item.status}**
<% end %>
"""HEEX

Converting HEEx to HTML String

When you need the final HTML as a string (e.g., for emails or static pages):

MDEx.to_heex!(markdown, assigns: assigns)
|> MDEx.to_html!()

Full Example

defmodule MyAppWeb.BlogLive do
  use Phoenix.LiveView
  use MDEx

  def mount(_params, _session, socket) do
    {:ok, assign(socket, title: "My Post", likes: 42)}
  end

  def render(assigns) do
    ~MD"""
    # {@title}

    This post has **{@likes}** likes.

    <.button phx-click="like">Like this post</.button>

    ---

    Built with <.link href="https://hex.pm/packages/mdex">MDEx</.link>
    """HEEX
  end

  def handle_event("like", _, socket) do
    {:noreply, update(socket, :likes, &(&1 + 1))}
  end
end