guides/heex.md

# HEEx Integration

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):

```elixir
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:

```elixir
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:

```elixir
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:

```elixir
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:

```elixir
~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 {: .info}
>
> 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:

```elixir
~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):

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

## Full Example

```elixir
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
```