Skip to content

Instantly share code, notes, and snippets.

@CamKem
Last active October 18, 2025 00:34
Show Gist options
  • Save CamKem/4c5a2eb9a0c9c470cbc790829c8da355 to your computer and use it in GitHub Desktop.
Save CamKem/4c5a2eb9a0c9c470cbc790829c8da355 to your computer and use it in GitHub Desktop.
Blade in markdown
<?php
declare(strict_types=1);
namespace App\View\Components;
use Closure;
use Dom\Element;
use Dom\HtmlDocument;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Str;
use Illuminate\View\Component;
use Illuminate\View\ComponentAttributeBag;
use Illuminate\View\ComponentSlot;
final class Markdown extends Component
{
public function render(): Closure
{
return static function (array $data) {
/** @var ComponentSlot $content */
$content = $data['slot'];
/** @var ComponentAttributeBag|null $attributes */
$attributes = $data['attributes'] ?? null;
$dom = HtmlDocument::createFromString(
source: "<markdown>{$content->toHtml()}</markdown>",
options: LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED
);
/** @var Element $doc */
$doc = $dom->documentElement;
$map = collect();
foreach ($doc->childNodes as $index => $child) {
if ($child instanceof Element) {
$placeholder = "::=={$index}==::";
$fragment = $dom->createDocumentFragment();
$fragment->appendXml($placeholder);
$doc->replaceChild($fragment, $child);
$map->put($placeholder, $dom->saveHTML($child));
}
}
$lines = explode("\n", $doc->innerHTML);
$trimmedLines = array_map(
callback: static fn (string $line): string => (string) Str::of($line)->ltrim(),
array: $lines,
);
$trimmed = implode("\n", $trimmedLines);
$parsed = Str::of($trimmed)->markdown();
foreach ($map as $placeholder => $originalHtml) {
$parsed = Str::of($parsed->toString())
/** @phpstan-ignore argument.type */
->replace($placeholder, $originalHtml);
}
$result = Blade::render((string) $parsed);
// If attributes are provided, wrap the content with a div that has those attributes
if ($attributes !== null && $attributes->isNotEmpty()) {
return "<div {$attributes->toHtml()}>{$result}</div>";
}
return $result;
};
}
}
<x-app-layout pageTitle="{{ __('Test Blade-in-Markdown') }}">
<x-section.component id="test">
<x-section.h1>
This is Blade-in-Markdown
</x-section.h1>
<x-markdown>
# Hello,
This is a markdown file that also contains internal blade components, that need rendering
- Here is and icon using the include directive:
@include('components.icons.menu-icon')
- Here is a component:
<x-icons.alpine class="size-10"/>
<pre>
<x-torchlight-code language="php">
if ($this === true) {
echo "this is true";
}
$code = 'code';
$hasIndentations = true;
if ($hasIndentations) {
echo "hey, im indented";
}
</x-torchlight-code>
</pre>
- An asset
{{ asset('images/logo.svg') }}
</x-markdown>
</x-section.component>
</x-app-layout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment