Weave Code
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 adding Markdown support to templates. Provides markdown_to_html and html_to_markdown filters to convert Markdown blocks to HTML and turn HTML back into Markdown, making it easy to render or edit rich content in Twig views.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package (updated for security patch):

    composer require twig/markdown-extra:^3.26
    
  2. Register the Twig extension in AppServiceProvider:

    use Twig\Extra\Markdown\MarkdownExtension;
    
    public function boot()
    {
        $this->twig->addExtension(new MarkdownExtension());
    }
    

    For Laravel 10+, add to config/twig.php under 'extensions'.

  3. First use case (now with auto-escaping for security):

    <div class="content">
        {{ $article->body|markdown_to_html }}  <!-- Note: {{ }} instead of {!! !!} for auto-escaping -->
    </div>
    

Where to Look First

  • Filters (now safer by default):
    • markdown_to_html: Converts Markdown to auto-escaped HTML (CVE-2026-46637 fix).
    • html_to_markdown: Converts HTML back to Markdown (unchanged).
  • Security: Critical: Always use {{ }} (auto-escaping) for user-generated content. The package now pre-escapes input on HTML-emitting filters.
  • Documentation:

Implementation Patterns

Core Workflows

1. Rendering User-Generated Content (Updated for Security)

  • Pattern: Use {{ }} (auto-escaping) by default. Only use {!! !!} with explicit sanitization.
  • Example (safe):
    <article>
        {{ $comment->text|markdown_to_html }}
    </article>
    
  • If raw HTML is required (e.g., for trusted content):
    <article>
        {!! Str::of($comment->text|markdown_to_html)->allowedTags(['p', 'strong', 'em', 'a']) !!}
    </article>
    
  • Tip: Cache rendered output with auto-escaping:
    Cache::remember("comment_{$comment->id}_html", now()->addHours(1), function() use ($comment) {
        return e($comment->text|markdown_to_html); // Explicit escaping
    });
    

2. Markdown-First Documentation (Unchanged)

  • Pattern: Store docs in Markdown files and render dynamically.
  • Example:
    $markdown = file_get_contents(storage_path("app/docs/api.md"));
    return view('docs.api', ['content' => $markdown]);
    
    <div class="docs">
        {{ $content|markdown_to_html }}  <!-- Auto-escaped -->
    </div>
    

3. Bidirectional Conversion for Migrations (Unchanged)

  • Pattern: Convert legacy HTML to Markdown for version control.
  • Example:
    $legacyHtml = '<p>Old <strong>HTML</strong> content</p>';
    $markdown = $legacyHtml|html_to_markdown;
    file_put_contents("storage/app/migrated/{$id}.md", $markdown);
    

4. Custom Markdown Processing (Updated for Security)

  • Pattern: Extend functionality with a custom Twig filter (ensure input is escaped).
  • Example:
    // In AppServiceProvider
    $this->twig->addFilter(new class extends \Twig\TwigFilter {
        public function __invoke($markdown, $customOption = null) {
            $html = (new \Symfony\Component\Markdown\Markdown())->convert($markdown);
            return $customOption ? e($html) . '<!-- custom: '.$customOption.' -->' : e($html);
        }
    }, new \Twig\TwigFilter('markdown_custom', [$this, 'markdownCustomFilter']));
    
    {{ $content|markdown_custom('highlight') }}  <!-- Auto-escaped -->
    

Integration Tips

  • Laravel Cache: Cache escaped Markdown to avoid reprocessing:
    $html = Cache::remember("markdown_{$id}", now()->addDays(7), function() use ($markdown) {
        return e($markdown|markdown_to_html);
    });
    
  • Form Handling: Convert submitted HTML to Markdown for storage (unchanged):
    $markdown = request()->input('content')|html_to_markdown;
    $post->update(['body' => $markdown]);
    
  • Livewire/Alpine: Combine with frontend frameworks for real-time preview (use {{ }}):
    <div x-data="{ preview: '' }" x-init="
        $watch('input', (val) => {
            preview = val|markdown_to_html;
        })
    ">
        <textarea x-model="input"></textarea>
        <div x-html="preview"></div>  <!-- Use x-html for raw output -->
    </div>
    

Gotchas and Tips

Pitfalls

  1. XSS Risks (Critical Fix in v3.26.0):

    • Gotcha: Breaking change: {!! !!} now auto-escapes by default. Malicious payloads like <script>alert(1)</script> are blocked.
    • Fix:
      • Use {{ }} for all user-generated content.
      • If raw HTML is required, explicitly sanitize:
        {!! Str::of($userInput|markdown_to_html)->allowedTags(['p', 'a']) !!}
        
    • Tip: Use Laravel’s Purifier for strict sanitization:
      use Illuminate\Support\Str;
      $safeHtml = Str::of($markdown|markdown_to_html)->purify();
      
  2. Double Escaping (New Behavior):

    • Gotcha: Using {{ $var|markdown_to_html }} twice may break rendering (input is escaped twice).
    • Fix: Cache or process Markdown once:
      $cachedHtml = Cache::remember("markdown_{$id}", now()->addDays(1), function() use ($var) {
          return $var|markdown_to_html;
      });
      {{ $cachedHtml }}
      
  3. Performance with Large Content (Unchanged):

    • Gotcha: Parsing massive Markdown blocks can slow templates.
    • Fix: Cache the result or process offline:
      $html = Cache::remember("markdown_{$id}", now()->addDays(7), function() use ($markdown) {
          return $markdown|markdown_to_html;
      });
      
  4. HTML-to-Markdown Limitations (Unchanged):

    • Gotcha: Complex HTML may not convert cleanly.
    • Fix: Pre-process HTML or accept manual cleanup.

Debugging

  • Log Raw Input/Output (Updated for Escaping):
    \Log::debug('Markdown Input:', [$markdown]);
    \Log::debug('HTML Output (escaped):', [e($markdown|markdown_to_html)]);
    
  • Test Edge Cases:
    • Empty strings, malformed Markdown, and special characters (<, >, &).
    • New: Test XSS payloads (e.g., <img src=x onerror=alert(1)> should be blocked).
  • Symfony Markdown Logs: Enable debug mode for verbose errors:
    $markdown = new \Symfony\Component\Markdown\Markdown();
    $markdown->setEnvironment('dev');
    

Configuration Quirks

  1. Twig Auto-Reloading (Unchanged):

    • Issue: Changes to Markdown templates may not reflect due to Twig caching.
    • Fix: Clear Blade cache:
      php artisan view:clear
      
  2. Extension Registration (Unchanged):

    • Issue: Extension not loading in Laravel 10+.
    • Fix: Ensure config/twig.php includes:
      'extensions' => [
          Twig\Extra\Markdown\MarkdownExtension::class,
      ],
      
  3. Markdown Extra Features (Unchanged):

    • Tip: Enable additional syntax (e.g., tables) via Symfony Markdown:
      $markdown = new \Symfony\Component\Markdown\Markdown();
      $markdown->addExtension(new \Symfony\Component\Markdown\Extension\TableExtension());
      

Extension Points

  1. Custom Filters (Updated for Security):
    • Override default behavior with explicit escaping:
      $this->twig->addFilter(new class extends \Twig\TwigFilter {
      
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope