Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Markdown Extra Laravel Package

twig/markdown-extra

Twig extension that adds markdown_to_html and html_to_markdown filters for converting Markdown blocks to HTML and HTML back to Markdown directly in your Twig templates, with configurable parsers.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package:

    composer require twig/markdown-extra
    

    If using Laravel with Twig, ensure your composer.json pins the new release:

    "require": {
        "twig/twig": "^3.0",
        "twig/markdown-extra": "^3.29.0"
    }
    
  2. (Optional) Choose a Markdown Backend: The extension ships with a default CommonMark-based converter. As of v3.29.0 you can also use tempest/markdown as the rendering engine:

    composer require tempest/markdown
    

    Pick a backend up front — converter choice affects supported syntax (tables, footnotes, attributes) and how you write custom extensions.

  3. Register the Extension: In your AppServiceProvider (or TwigServiceProvider if using a custom setup):

    use Twig\Environment;
    use Twig\Extension\MarkdownExtraExtension;
    
    public function boot()
    {
        // Default backend
        $this->app->make(Environment::class)->addExtension(new MarkdownExtraExtension());
    
        // Or, v3.29.0+: explicit tempest/markdown backend
        // $this->app->make(Environment::class)->addExtension(
        //     new MarkdownExtraExtension(new \Tempest\Markdown\Markdown())
        // );
    }
    
  4. First Use Case: Render Markdown in a Twig template:

    {{ markdown_content|markdown_to_html }}
    

    Convert HTML back to Markdown:

    {{ html_content|html_to_markdown }}
    

Where to Look First

  • Documentation: Twig Markdown Extra README for filter details and the new backend options.
  • Release Notes: v3.29.0 diff — feature #4890 adds tempest/markdown support; nothing is deprecated or removed.
  • Laravel Integration: Check your Twig service provider for where the Environment is built — that is the single place extension registration should happen.
  • Security Note: Review the CVE-2026-46637 fix to understand auto-escaping behavior.

Implementation Patterns

Core Workflows

1. Rendering User-Generated Markdown (Safe)

{# Forum post rendering #}
<div class="post">
    {{ user_post.markdown|markdown_to_html }}
</div>
  • Key: The filter auto-escapes untrusted input by default (post-CVE fix). No manual |e escaping needed.

2. Swapping the Markdown Backend (v3.29.0+)

// config/twig.php or a dedicated service provider
use Tempest\Markdown\Markdown;
use Twig\Extension\MarkdownExtraExtension;

$converter = new Markdown();               // tempest/markdown
$twig->addExtension(new MarkdownExtraExtension($converter));
  • Keep the converter behind a container binding so tests can swap it:
$this->app->bind(MarkdownConverterInterface::class, fn () => new Markdown());

3. Bidirectional Content Pipelines

// Laravel Controller: Convert HTML to Markdown for version control
public function updateLegacyContent()
{
    $html = file_get_contents('legacy_post.html');
    $markdown = $this->twig->render(
        '{{ html|html_to_markdown }}',
        ['html' => $html]
    );
    file_put_contents('post.md', $markdown);
}

4. Dynamic Markdown in Blade (Laravel)

// Blade directive for Markdown (custom extension)
Blade::directive('markdown', function ($expression) {
    return "<?php echo app(\\Twig\\Environment::class)->render('{{ content|markdown_to_html }}', ['content' => {$expression}]); ?>";
});

Usage:

@markdown($post->content)

5. Caching Rendered Markdown

// Cache the rendered HTML for 1 hour
$cachedHtml = Cache::remember("markdown_{$post->id}", now()->addHour(), function () use ($post) {
    return $this->twig->render('{{ content|markdown_to_html }}', ['content' => $post->markdown]);
});
  • Cache keys should include the package version and backend name — switching to tempest/markdown will change output for the same input.

Integration Tips

  • Laravel Nova: Use the markdown_to_html filter in custom fields to render Markdown in the admin panel.
  • Livewire: Stream real-time Markdown updates:
    public function updatedMarkdown()
    {
        $this->renderedHtml = $this->twig->render(
            '{{ markdown|markdown_to_html }}',
            ['markdown' => $this->markdown]
        );
    }
    
  • API Responses: Serve Markdown as HTML in JSON APIs:
    return response()->json([
        'content' => $this->twig->render('{{ body|markdown_to_html }}', ['body' => $request->body]),
    ]);
    
  • Form Handling: Convert submitted HTML back to Markdown for storage:
    $markdown = $this->twig->render('{{ html_content|html_to_markdown }}', ['html_content' => $request->html]);
    

Advanced Patterns

Custom Filter for Trusted Content

// Override escaping for trusted Markdown (e.g., admin-only)
$twig->addFilter(new \Twig\TwigFilter('trusted_markdown', function ($markdown) use ($converter) {
    return $converter->convert($markdown);
}));

Warning: Only use this for explicitly trusted sources (e.g., admin inputs).

Extending Markdown Syntax

// Register extra syntax on the chosen backend, then pass it to the extension
$converter = new \League\CommonMark\CommonMarkConverter([
    'commonmark' => ['use_asterisk' => true],
]);
$twig->addExtension(new MarkdownExtraExtension($converter));
  • Whatever backend you pick, configure it before constructing MarkdownExtraExtension; the extension holds a reference to the converter, not a copy.

Gotchas and Tips

Pitfalls

  1. Double Escaping:

    • Issue: Applying |e (Twig’s escape filter) after markdown_to_html breaks HTML rendering.
    • Fix: Remove redundant escaping:
      {# Wrong: Double-escapes #}
      {{ user_comment|markdown_to_html|e }}
      
      {# Correct: Auto-escaping is handled #}
      {{ user_comment|markdown_to_html }}
      
  2. Backend Output Drift (v3.29.0):

    • Issue: Upgrading and switching to tempest/markdown changes rendered HTML (whitespace, table markup, footnote anchors) even though your templates are untouched.
    • Fix: Golden-file test your existing Markdown corpus before flipping the converter, and bump your cache namespace.
  3. Legacy HTML Breakage:

    • Issue: Auto-escaping may corrupt nested HTML (e.g., <div><a href="..."> in Markdown).
    • Fix: Use html_to_markdown cautiously for complex HTML. Test with:
      {{ legacy_html|html_to_markdown|markdown_to_html }}
      
  4. Custom Filter XSS Risks:

    • Issue: Bypassing auto-escaping in custom filters re-introduces XSS risks.
    • Fix: Document and audit all custom filters. Example of a safe custom filter:
      $twig->addFilter(new \Twig\TwigFilter('safe_markdown', [$extension, 'renderMarkdown'], ['is_safe' => ['html']]));
      
  5. Twig Version Conflicts:

    • Issue: Older Laravel/Twig pins may conflict with Twig 3.x dependencies.
    • Fix: Pin versions in composer.json:
      "require": {
          "twig/twig": "^3.0",
          "twig/markdown-extra": "^3.29.0"
      }
      
  6. Optional Dependency Guard:

    • Issue: Referencing Tempest\Markdown\Markdown when tempest/markdown is not installed throws a class-not-found at boot.
    • Fix: Only instantiate the Tempest converter behind a class_exists() check or a container binding resolved lazily.
  7. Performance with Large Content:

    • Issue: Parsing massive Markdown blocks (e.g., 10MB docs) may time out.
    • Fix: Push rendering into a queued job and store the HTML; chunk or pre-render on write rather than on read.

Debugging Tips

  • Inspect Rendered Output: Use Twig’s dump filter to debug Markdown parsing:
    {{ markdown_content|markdown_to_html|dump }}
    
  • Check for Malicious Input: Test with payloads like:
    {{ '<script>alert(1)</script>'|markdown_to_html }} {# Should render as text #}
    
  • Confirm Which Converter Is Active:
    dump(get_class(app(\Twig\Environment::class)->getExtension(\Twig\Extension\MarkdownExtraExtension::class)));
    
  • Enable Twig Debug Mode: In config/twig.php:
    'debug' => env('APP_DEBUG', true),
    

Configuration Quirks

  1. Auto-Escaping Override: The package cannot disable auto-escaping post-v3.26.0. For trusted content, use custom filters (see above).

  2. Extension Registration:

    • If your Twig wiring auto-registers extensions, manual registration may cause duplicates.
    • Fix: Register MarkdownExtraExtension exactly once, and assert it in a boot test.
  3. Markdown Flavor: The default backend is CommonMark-based and may differ from GitHub-flavored Markdown (e.g., tables, task lists). Configure the converter explicitly:

    $converter = new \League\CommonMark\CommonMarkConverter([
        'html_input' => 'escape',
        'allow_unsafe_links' => false,
    ]);
    

    Alternatively, adopt tempest/markdown (v3.29.0) if its syntax coverage better matches your content — just document the choice in your README.

Extension Points

  1. Custom Markdown Extensions: Register backend-specific syntax extensions on the converter, then hand the configured converter to the Twig extension:

    $converter = new \League\CommonMark\CommonMarkConverter([...]);
    $twig->addExtension(new MarkdownExtraExtension($converter));
    
  2. Pluggable Converter (v3.29.0): Because the Twig extension now accepts alternative implementations such as tempest/markdown, you can wrap your own converter (or a decorator that logs/telemetry-wraps conversions) and inject it:

    $this->app->singleton(MarkdownExtraExtension::class, function ($app) {
        return new MarkdownExtraExtension($app->make(MyLoggingConverter::class));
    });
    
  3. Pre/Post-Processing: Hook into the parsing pipeline via a decorator around the converter rather than patching the Twig extension — this keeps upgrades (including future backend additions like #4890) non-breaking.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin