Earmark live_view component

Hi,

I want to write a component to display markdown:

~H"""
<.markdown>
# Hi there

* test
</.markdown>

My current component looks like this:

def markdown(assigns) do
  ~H"""
    <%= Earmark.as_html!(@inner_block, code_class_prefix: "lang- language-") |> Phoenix.HTML.raw() %>
   """
end

but @inner_block is not the “text” content from the block. Any idea on how to get to that content, or how to solve this problem in a different way?

@inner_block is just a reference to a slot. If you want the contents then you need to render the slot with render_slot/2. Also while it may be fine with markdown I’m not sure you can have arbitrary text as slot content with heex.

Thanks, this is what works, but it feels wrong

      <%= render_slot(@inner_block)
      |> then(fn %{static: [code]} ->
        code = String.trim(code)
        Earmark.as_html!(code, code_class_prefix: "lang- language-") |> Phoenix.HTML.raw()
      end) %>

I’m starting to think my approach is not really possible…
The heex formatter keeps realigning my markdown code and so it breaks.

Too bad, would’ve been nice to write

<.markdown>
# Hello

## A list

* first item
* second item
</.markdown>

I know it’s not LiveView but Surface UI has a Markdown Component. Maybe you can have a look and see if you can get some hints/ideas from it?

1 Like

Isn’t there the phx-no-format attribute for the formatter?

1 Like

Indeed, thanks :heart:

Works like a charm (with phx-no-format)
Any idea how we could add a function component in the markdown, sth like:

<.markdown>
# Markdown with a function-component-call

This is some regular *markdown* content.

<.some_component foo="bar" />

And some more markdown.
</.markdown>

This obviously does not work with just render_slot().static.

this is what I came up with, no idea how evil it is. (handles only one inner block yet)

In the heex-template:

<div>
<Markdown.markdown phx-no-format>
# Markdown from inner-content

<%= "TEST" %>
*Hello* this is markdown from inner-content.
</Markdown.markdown>
</div>

the component:

def markdown(assigns) do
    inner_block_rendered =
      case assigns.inner_block do
        [inner_block] ->
          rendered =
            inner_block.inner_block.(assigns.__changed__, nil)
            |> Phoenix.HTML.Safe.to_iodata()
            |> to_string()

        _ ->
          nil
      end

    dbg(inner_block_rendered)
    ...
end

rendered:

inner_block_rendered #=> "\n# Markdown from inner-content\nTEST\n*Hello* this is markdown from inner-content.\n      "

I am using the MD Library to be able to render any HEEX template inside a markdown file, that is rendered by a custom Phoenix template engine and this is how I approached it:

I also like the idea of using the <.markdown> slot, need to try it out.

1 Like