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

Html To Markdown Laravel Package

league/html-to-markdown

Convert HTML to Markdown in PHP with league/html-to-markdown. Create an HtmlConverter, pass valid HTML to convert(), and get clean Markdown back. Useful for editing legacy HTML, storing content as HTML while authoring in Markdown, and turning HTML email into plain text.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package via Composer (use ^5.1.4 or later — see the security note below):
    composer require league/html-to-markdown
    
  2. Ensure required PHP extensions (dom, xml, libxml) are enabled.
  3. Create and use a basic converter:
    use League\HTMLToMarkdown\HtmlConverter;
    
    $converter = new HtmlConverter();
    $markdown = $converter->convert('<h1>Hello World</h1>');
    echo $markdown; // => "# Hello World"
    

First use case: convert rich-text HTML content (e.g., from a CMS or user input) into clean Markdown for storage, editing, or export (e.g., to plain-text email).

Upgrade first if you convert untrusted HTML. 5.1.4 is a security release that fixes a denial-of-service issue where conversion time grew with the square of the number of sibling elements (GHSA-h3wg-gqjr-jjmw). Run composer show league/html-to-markdown and ensure it resolves to >=5.1.4; the fix produces identical Markdown, so it is a drop-in upgrade with no output changes.

Implementation Patterns

  • Sanitization-first conversion for untrusted input: Combine with HTMLPurifier and use strip_tags/remove_nodes options:
    $converter = new HtmlConverter([
        'strip_tags' => true,
        'remove_nodes' => 'script iframe object',
    ]);
    
  • Bound the input before converting (defense in depth alongside 5.1.4): the quadratic-work class of bug is driven by many elements sharing one parent, so cap payload size and/or element counts at the validation layer:
    $html = Str::limit($request->input('body'), 100_000); // reject or truncate oversized payloads
    
  • Custom converters: Extend existing converters or add new ones (e.g., custom TableConverter for table support):
    $converter->getEnvironment()->addConverter(new MyCustomConverter());
    
  • Project-wide configuration: Reuse a configured instance (e.g., in a service container or singleton):
    $converter = new HtmlConverter([
        'header_style' => 'atx',
        'italic_style' => '*',
        'bold_style' => '**',
        'hard_break' => true,  // GitHub-style line breaks
    ]);
    
  • Offload risky conversions: for user-submitted documents, dispatch conversion to a queued job with a sensible $timeout (and retryUntil) rather than doing it inline in the request — a slow conversion then fails the job instead of tying up a PHP-FPM worker.
  • Preserve metadata: Use preserve_comments to retain CMS-specific comments (e.g., <!-- {title} -->) for round-trip editing workflows. Note that preserved tags/comments were among the affected cases in the 5.1.4 advisory, so this is another reason to stay current.

Gotchas and Tips

  • Security (5.1.4): prior versions degrade quadratically with sibling count for list items, <pre>, <code>, table rows/cells and preserved HTML tags on all PHP versions, and essentially all elements on PHP 8.0 and below. Upgrade to >=5.1.4, and treat user HTML length as a hard constraint. If you cannot upgrade yet, run conversion in a queue with a timeout and cap input size.
  • HTML escaping: The package escapes * and _ in text nodes by default, but still may mangle math or code blocks—test with edge cases.
  • Missing features: Tables and nested lists require explicit Converter\TableConverter (and custom handling for nested lists—see To-Do list in README). Tables are also the highest-risk structure for large documents, so validate row/cell counts before converting.
  • DOMDocument quirks: Invalid HTML can cause parsing errors. Wrap convert() in libxml_use_internal_errors(true) + libxml_get_errors() if pre-parsing is needed.
  • Whitespace sensitivity: div is treated as block-level; newlines appear around div content—configure line-break behavior via hard_break and test with <div><p>...</p></div>.
  • Link/output hygiene: Use strip_placeholder_links to remove empty links (e.g., <a href="#">), and use_autolinks to control link syntax ([text](url) vs <url>).
  • Debugging slow conversions: since 5.1.4 changes no output, a performance diff after upgrading is environmental, not behavioral. Profile with microtime(true) around convert() using fixtures of many sibling <li>/<tr> elements to confirm the fix is actually installed (a stale lock file is the usual culprit).
  • Testing tip: Use HTML fixtures and compare against expected Markdown using loose equality (ignore trailing whitespace/newlines). Keep a large-input fixture in the suite to catch pathological performance regressions early.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console