MDPhlex

MDPhlex is a Phlex component for rendering Markdown. No... not rendering markdown into HTML, rendering plain Markdown, programmatically.

MDPhlex is perfect for dynamically creating context for LLMs, generating an llms.txt file from real content, or composing markdown out of componentized pieces.

Installation

Add this line to your application's Gemfile:

gem 'mdphlex'

Creating LLM Prompts with MDPhlex

MDPhlex shines when creating structured prompts for LLMs allowing comments and organization without cluttering the prompt. Here's a simple example using custom tags:

class LLMPrompt < MDPhlex::MD
  # Register custom elements for structured prompts
  register_block_element :system
  register_block_element :tools
  register_block_element :tool

  def initialize(task:, tools: [])
    @task = task
    @tools = tools
  end

  def view_template
    system do
      p "You are an AI assistant specialized in #{@task}."

      h2 "Goal"
      # we should define the goal more clearly.
      p "Use the available tools to help the user."

      # what about guardrails?
    end

    if @tools.any?
      tools do
        @tools.each do |tool_def|
          tool name: tool_def[:name] do
            plain tool_def[:description]
            # need to add input schemas
          end
        end
      end
    end
  end
end

# Dynamic prompt generation
prompt = LLMPrompt.new(
  task: "Ruby code analysis",
  tools: [
    { name: "analyze_code", description: "Analyze Ruby code for improvements" },
    { name: "explain_concept", description: "Explain Ruby concepts and patterns" }
  ]
)

puts prompt.call

This outputs clean, structured markdown perfect for LLMs:

<system>
You are an AI assistant specialized in Ruby code analysis.

## Goal
Use the available tools to help the user.

</system>
<tools>
<tool name="analyze_code">
Analyze Ruby code for improvements</tool>
<tool name="explain_concept">
Explain Ruby concepts and patterns</tool>
</tools>

Traditional Markdown Generation

MDPhlex also works great for generating regular Markdown content.

Rails Integration with Markdown Rendering

With Rails 8.1's native markdown support, MDPhlex components integrate seamlessly:

# app/components/page_component.rb
class PageComponent < MDPhlex::MD
  def initialize(page)
    @page = page
  end

  def view_template
    h1 @page.title

    p do
      em "Last updated: #{@page.updated_at.strftime('%B %d, %Y')}"
    end

    hr

    # Render page sections with proper markdown structure
    @page.sections.each do |section|
      h2 section.heading

      p section.content

      if section.code_example?
        pre(language: section.language) { plain section.code }
      end
    end

    if @page.references.any?
      h2 "References"
      ul do
        @page.references.each do |ref|
          li do
            a(href: ref.url) { ref.title }
          end
        end
      end
    end
  end
end

# app/controllers/pages_controller.rb
class PagesController < ApplicationController
  def show
    @page = Page.find(params[:id])

    respond_to do |format|
      format.html
      format.md { render markdown: PageComponent.new(@page) }
    end
  end
end

When someone visits /pages/123.md, Rails will render:

# Getting Started with Ruby

*Last updated: September 12, 2025*

---

## Introduction

Ruby is a dynamic, object-oriented programming language...

## Basic Syntax

Here's how to define a method in Ruby:

```ruby
def greet(name)
  puts "Hello, #{name}!"
end
```

## References
- [Official Ruby Documentation](https://ruby-doc.org)
- [Ruby Style Guide](https://rubystyle.guide)

Dynamic Content Generation

class ArticleContent < MDPhlex::MD
  def initialize(title:, sections:)
    @title = title
    @sections = sections
  end

  def view_template
    h1 @title

    @sections.each do |section|
      h2 section[:heading]

      p section[:content]

      if section[:code_example]
        pre(language: "ruby") { plain section[:code_example] }
      end
    end

    p do
      plain "Learn more in the "
      a(href: "https://docs.example.com") { "documentation" }
      plain "."
    end
  end
end

article = ArticleContent.new(
  title: "Getting Started with Ruby",
  sections: [
    {
      heading: "Variables",
      content: "Ruby variables are dynamically typed and don't require declaration.",
      code_example: "name = 'Alice'\nage = 30"
    }
  ]
)

puts article.call

Outputs:

# Getting Started with Ruby

## Variables

Ruby variables are dynamically typed and don't require declaration.

```ruby
name = 'Alice'
age = 30
```

Learn more in the [documentation](https://docs.example.com).

Rendering MDPhlex inside Phlex::HTML (and vice versa)

MDPhlex components are Phlex compatible. Integrate them into any Phlex::HTML views or show HTML or other wcomented in Markdown:

class ArticlePage < Phlex::HTML
  def initialize(article)
    @article = article
  end

  def view_template
    html do
      head do
        title { @article.title }
      end
      body do
        article do
          # Render MDPhlex component inside HTML
          div(class: "markdown-content") do
            plain render(ArticleContent.new(@article))
          end
        end
      end
    end
  end
end

class ArticleContent < MDPhlex::MD
  def initialize(article)
    @article = article
  end

  def view_template
    h1 @article.title

    p @article.summary

    h2 "Contents"

    @article.sections.each do |section|
      h3 section.title
      p section.content
    end
  end
end

Why MDPhlex?

But markdown is just text!? Yes, but have you ever tried to render clean markdown from a lot of conditional logic? MDPhlex tames the mess with a simple and familiar API.

  • Component-based: Build reusable Markdown with simple ruby classes
  • Dynamic Markdown: Generate markdown from dynamic data
  • Composable: Mix Phlex and MDPhlex components freely

License

The gem is available as open source under the terms of the MIT License.