ornj/markdown-bundle
ornj/markdown-bundle is a Symfony bundle for parsing and rendering Markdown as HTML. It integrates Markdown conversion into Symfony services and templating, making it easy to add Markdown support to content, controllers, and Twig views with minimal setup.
First, correct the premise before you do anything else. ornj/markdown-bundle is a Symfony2 bundle, not a Laravel package. It has no service provider, no facade, no Blade integration, and its Twig filter, FormType, and DI wiring don't map onto Laravel's container or view layer. There is no composer require ornj/markdown-bundle path into a Laravel app that ends well: the package is pinned to Symfony2's EOL'd kernel, the Form component, Twig, and (likely) Assetic-era asset handling. Its dependencies will collide with a modern Laravel/PHP stack.
So the honest "getting started" depends on which stack you're actually on:
AppKernel.php (new Ornj\MarkdownBundle\OrnjMarkdownBundle()), expose the service in config.yml, and use the Twig filter in templates. This is the one scenario where the package works as advertised.Where to look first in the source:
league/commonmark in Laravel).First use case for a Laravel dev (build, don't install):
composer require league/commonmark mews/purifier
Then a minimal Blade component:
{{-- resources/views/components/markdown-editor.blade.php --}}
<div x-data="{ md: @entangle($model).live }">
<textarea x-model="md"></textarea>
<div>{!! $previewHtml !!}</div>
</div>
…with the server-side render going through league/commonmark + mews/purifier, never the raw parser output.
If you legitimately run Symfony2 (legacy):
{{ article.body|markdown }}. Be explicit about escaping — see gotchas.If you're porting the pattern to Laravel (the realistic path):
Single rendering path (non-negotiable): preview and published output must go through the same function. Encapsulate it once:
final class MarkdownRenderer
{
public function __construct(private CommonMarkConverter $parser, private Purifier $purifier) {}
public function toHtml(string $md): string
{
return $this->purifier->purify((string) $this->parser->convert($md));
}
}
Call it from both the preview endpoint and the Blade render of stored content. Divergence between the two is the classic markdown-editor bug.
Canonical source is markdown: store the raw markdown in the DB (diffable, version-controllable); treat rendered HTML as disposable/cacheable output. This is the pattern the bundle models and the one worth carrying forward.
Preview strategy: prefer client-side preview (marked/remark in the browser, sanitized with DOMPurify) to avoid a render request per keystroke-debounce. If you need server parity, expose one debounced endpoint (e.g. POST /markdown/preview) that returns the sanitized HTML from your MarkdownRenderer, and rate-limit it.
Editor component: wrap a maintained editor (EasyMDE, Toast UI, or a CodeMirror-based one) inside a Livewire component or Blade + Alpine. This replaces the bundle's JS/FormType entirely, with active maintenance.
Form integration: on the Laravel side the "form field" is a Blade/Livewire component bound to a model attribute; validation (size limits, allowed chars) belongs in a FormRequest — none of this comes from the bundle.
mews/purifier, HTML Purifier, DOMPurify client-side). Treat the preview as not a trust boundary.debug:container) to confirm the service ID and parser config actually registered; misconfigured bundle registration silently falls back or throws at boot, not at render.league/commonmark extensions; centralize the config so preview and stored output enable the same set.How can I help you explore Laravel packages today?