nickcernis/html-to-markdown
Convert HTML into clean, readable Markdown in PHP. Parse tags and structure into Markdown output with configurable rules, custom converters, and strong defaults—handy for scraping, email content, CMS migrations, and turning rich text into Markdown for storage or editing.
Installation
composer require nickcernis/html-to-markdown
Register the service provider in config/app.php (if not auto-discovered):
'providers' => [
// ...
NickCernis\HtmlToMarkdown\HtmlToMarkdownServiceProvider::class,
],
Basic Usage
use NickCernis\HtmlToMarkdown\HtmlToMarkdown;
$html = '<h1>Hello World</h1><p>This is <strong>bold</strong> text.</p>';
$markdown = HtmlToMarkdown::convert($html);
// Outputs:
// # Hello World
// This is **bold** text.
First Use Case Convert raw HTML from a CMS (e.g., TinyMCE, CKEditor) or user-generated content into clean Markdown for storage or display.
Sanitization + Conversion
$html = '<div class="unsafe"><script>alert("xss")</script></div>';
$cleanHtml = purify_html($html); // Use a sanitizer like `htmlpurifier`
$markdown = HtmlToMarkdown::convert($cleanHtml);
Integration with Laravel Blade Create a Blade directive for inline conversion:
Blade::directive('md', function ($expression) {
return "<?php echo NickCernis\HtmlToMarkdown\HtmlToMarkdown::convert({$expression}); ?>";
});
Usage:
@md($htmlContent)
Batch Processing Useful for migrating legacy HTML content:
$posts = Post::where('content_html', '!=', null)->get();
foreach ($posts as $post) {
$post->update(['content_markdown' => HtmlToMarkdown::convert($post->content_html)]);
}
Customizing Output Extend the converter for domain-specific rules:
$converter = new HtmlToMarkdown();
$converter->setOptions([
'double_newlines' => true,
'strip_tags' => ['div', 'span'], // Remove unwanted tags
]);
$markdown = $converter->convert($html);
Nested Tags
Complex HTML (e.g., nested <table>s or <ul>s) may produce malformed Markdown. Test edge cases:
<ul><li><div>Item</div></li></ul>
Fix: Pre-process HTML with a DOM parser to flatten structures if needed.
Unescaped Characters
HTML entities (e.g., , ©) may not convert as expected. Use html_entity_decode() first:
$html = html_entity_decode($html, ENT_QUOTES, 'UTF-8');
$markdown = HtmlToMarkdown::convert($html);
Performance Avoid converting large HTML strings in loops. Cache results or process in chunks:
$markdown = cache()->remember("md_{$htmlHash}", now()->addHours(1), fn() =>
HtmlToMarkdown::convert($html)
);
Inspect Intermediate Steps
Use HtmlToMarkdown::getMarkdown() with debug: true to see the conversion pipeline:
$converter = new HtmlToMarkdown(['debug' => true]);
$converter->convert($html);
// Check Laravel logs for step-by-step output.
Validate Output Use a Markdown linter (e.g., PHP-Markdown) to verify results:
$parsed = Markdown::parse($markdown);
Custom Rules
Override the NickCernis\HtmlToMarkdown\Rules class to add support for proprietary HTML tags:
class CustomRules extends \NickCernis\HtmlToMarkdown\Rules {
public function getCustomRules() {
return [
'custom-tag' => ['markdown' => '[CUSTOM]'],
];
}
}
Register via:
$converter->setRules(new CustomRules());
Pre/Post-Processing Use Laravel events to hook into conversion:
// In a service provider:
event(new ConvertingHtml($html));
$markdown = HtmlToMarkdown::convert($html);
event(new HtmlConverted($markdown));
Configuration
Set defaults in config/services.php:
'html-to-markdown' => [
'default_options' => [
'double_newlines' => true,
'strip_tags' => ['br'],
],
],
Access via:
$converter = app(HtmlToMarkdown::class)->setOptions(config('html-to-markdown.default_options'));
How can I help you explore Laravel packages today?