erusev/parsedown
Parsedown is a fast, lightweight PHP Markdown parser with CommonMark-style syntax support. It converts Markdown to HTML with sensible defaults, minimal dependencies, and easy integration—ideal for blogs, docs, and templating in PHP and Laravel apps.
Install via Composer
composer require erusev/parsedown
Basic Usage in Laravel
In a controller or service class:
use Parsedown;
$parsedown = new Parsedown();
$html = $parsedown->text('# Hello, world!');
// Outputs: <h1>Hello, world!</h1>
First Use Case
Render Markdown stored in a database field (e.g., blog content):
$post = Post::find($id);
$content = $parsedown->text($post->markdown_body);
return view('posts.show', compact('post', 'content'));
Where to Look First
Parsedown.php — read inline docblocks and method signatures.text() and line() methods — text() for full documents, line() for inline-only.setStrictMode(true) for CommonMark-compliant ATX headings (requires a space after #), especially if rendering user content where # alone should not produce a heading.Service Binding in Laravel
Register a shared instance via AppServiceProvider:
$this->app->singleton(\Parsedown::class, function () {
return (new \Parsedown())->setBreaksEnabled(true); // optional: better newline handling
});
Tip (v1.8.0): If rendering untrusted input, instantiate with
->setSafeMode(true)and pair with HTML Purifier — safe mode is now recursively applied.
Blade Component Wrapper
Create a reusable component:
{{-- resources/views/components/markdown.blade.php --}}
<div class="markdown-content">
@php
$parsedown = new \Parsedown();
$parsedown->setBreaksEnabled(true);
// Uncomment for strict CommonMark compliance:
// $parsedown->setStrictMode(true);
@endphp
{!! $parsedown->text($slot) !!}
</div>
Usage:
<x-markdown>
## Notes
- *Lightweight*
- **Fast**
</x-markdown>
Caching Renders
Cache parsed HTML to avoid repeated parsing — especially important given improved security via strict parsing (reduces ReDoS risk, but still expensive for high-throughput):
$cacheKey = "markdown:{$id}";
$html = Cache::remember($cacheKey, now()->addDay(), function () use ($parsedown, $markdown) {
return $parsedown->text($markdown);
});
Extending for Custom Features
Subclass to add GitHub-style task lists or custom syntax — note significant structural changes for v1.8.0:
class ExtendedParsedown extends \Parsedown
{
protected function blockTaskList($Line)
{
if (preg_match('/^\s*-\s*\[(x| )\]\s+(.*)$/i', $Line['text'], $matches)) {
$checked = $matches[1] === 'x' ? 'checked' : '';
return [
'element' => [
'name' => 'li',
'text' => "<input type='checkbox' disabled {$checked}> {$matches[2]}"
]
];
}
}
// ⚠️ Breaking in v1.8.0: Element structure changed
// Previously: $Block['element']['text']['text']
// Now: $Block['element']['element']['text']
}
Safe Mode Is Now Safer (but Still Not Enough)
setSafeMode(true) now recursively sanitizes nested elements — a major improvement. However, it does not prevent all XSS (e.g., onerror on <img>). For untrusted input:
setStrictMode(true) to avoid edge cases like #-only headings becoming paras.Performance Considerations
Markdown Behavior Changes (v1.8.0)
*, -, +) or switching between . and ) in ordered lists creates separate lists (CommonMark-compliant).- Item
* Item 2 # ← Now starts a *new* list instead of continuing
> First
> Second # ← Now two `<blockquote>`s, not one.
# now outputs <h1></h1>, not a paragraph.Extensibility Updates (v1.8.0)
text may now be nested under element → element.$this->allowRawHtmlInSafeMode($element) in extensions to exempt trusted HTML in safe mode.PHP & Laravel Compatibility
classmap isn’t overriding Parsedown.Table Alignment Still Not Supported
Custom subclassing (blockTable()) remains necessary for alignment — no change in v1.8.0.
Emoji & Shortcode Handling
Parsedown still doesn’t support emoji shortcodes (e.g., :smile:) — use a preprocessor (e.g., ParsedownExtra) or Laravel mix-in if needed.
Breaking Extension Changes
If using third-party Parsedown extensions (e.g., ParsedownExtra), verify compatibility. The internal AST restructuring may require updates for custom block/inline handlers.
How can I help you explore Laravel packages today?