mckenziearts/livewire-markdown-editor
Installation
composer require mckenziearts/livewire-markdown-editor
npm install @github/markdown-toolbar-element github-text-expander-element
Publish assets:
php artisan vendor:publish --tag=livewire-markdown-editor-assets
Basic Usage Add the component to a Livewire blade:
<livewire:markdown-editor :content="$content" />
Initialize in a Livewire component:
public $content = "# Hello World";
public function mount()
{
$this->content = "# Default Markdown";
}
First Use Case Create a blog post editor:
// BlogPostController.php
public function edit(BlogPost $post)
{
return view('posts.edit', [
'post' => $post
]);
}
<!-- posts/edit.blade.php -->
<livewire:markdown-editor :content="$post->content" wire:model="content" />
Basic Editing
<livewire:markdown-editor
wire:model="postContent"
:toolbar-options="['bold', 'italic', 'heading']"
/>
wire:model for two-way binding with Livewire properties.File Uploads
// Livewire component
public function handleFileUpload($fileId, $fileName)
{
$path = $fileId . '.' . $fileName->extension();
$fileName->storeAs('uploads', $path);
return "[](uploads/$path)";
}
<livewire:markdown-editor
:file-upload-handler="handleFileUpload"
/>
Dark Mode Integration
<livewire:markdown-editor
dark-mode="{{ request()->wantsDarkMode() }}"
/>
Sync with Laravel's dark mode middleware or user preference.
Custom Toolbar
public function getToolbarOptions()
{
return [
'bold', 'italic', 'heading',
['label' => 'Custom', 'icon' => '✨', 'action' => 'customAction']
];
}
Pass via props:
<livewire:markdown-editor :toolbar-options="getToolbarOptions()" />
Code Highlighting
Ensure Spatie Shiki is configured in config/shiki.php:
'themes' => [
'light' => 'github-light',
'dark' => 'github-dark',
],
use Illuminate\Validation\Rule;
$rules = [
'content' => ['required', Rule::max(10000)],
];
use League\HTMLSanitizer\HTMLSanitizer;
$sanitizer = new HTMLSanitizer();
$cleanContent = $sanitizer->sanitize($this->content);
protected $listeners = ['contentUpdated' => 'handleContentUpdate'];
public function handleContentUpdate($content)
{
// Log or process content changes
}
Asset Loading
@github/markdown-toolbar-element and github-text-expander-element are included in your layout:
@vite(['resources/js/github-markdown-toolbar.js'])
vite.config.js:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/js/app.js',
'node_modules/@github/markdown-toolbar-element/dist/github-markdown-toolbar.js',
],
refresh: true,
}),
],
});
Dark Mode Sync
<script src="//unpkg.com/alpinejs" defer></script>
document.addEventListener('alpine:init', () => {
Alpine.data('markdownEditor', () => ({
init() {
this.$watch('darkMode', (value) => {
this.$el.querySelector('.markdown-preview').classList.toggle('dark', value);
});
}
}));
});
File Upload Quirks
config/cors.php:
'paths' => ['api/*', 'sanctum/csrf-cookie', 'uploads/*'],
config/filesystems.php:
'disks' => [
'local' => [
'driver' => 'local',
'max_file_size' => '10m', // Adjust as needed
],
],
Toolbar Customization
Alpine.store('markdownToolbar', {
init() {
this.$nextTick(() => {
const toolbar = document.querySelector('github-markdown-toolbar');
if (toolbar) {
toolbar.init();
}
});
}
});
<livewire:markdown-editor
:toolbar-options="[
@json($showAdvanced ? ['bold', 'italic', 'link', 'code'] : ['bold', 'italic'])
]"
/>
Console Logs Add debug logs in Livewire component:
public function updatedContent($value)
{
\Log::debug('Markdown content updated:', ['content' => $value]);
}
Check logs with:
tail -f storage/logs/laravel.log
Alpine.js Debugging Enable Alpine debug mode:
<script src="//unpkg.com/alpinejs" defer data-turbolinks-eval="false" data-turbolinks-track="reload" data-alpine-debug="true"></script>
Livewire Wire:ignore
If the editor flickers, wrap it in wire:ignore and manually sync:
<div wire:ignore>
<livewire:markdown-editor :content="$content" />
</div>
document.addEventListener('livewire:init', () => {
Livewire.hook('element.updated', (el) => {
if (el.querySelector('livewire-markdown-editor')) {
el.querySelector('livewire-markdown-editor').dispatchEvent(new CustomEvent('contentUpdated'));
}
});
});
Custom Markdown Processing
Extend the MarkdownEditor class to add pre/post-processing:
use Mckenziearts\LivewireMarkdownEditor\MarkdownEditor;
class CustomMarkdownEditor extends MarkdownEditor
{
public function processContent($content)
{
// Add custom logic (e.g., auto-links, placeholders)
return str_replace('# ', '# ', $content); // Example: Ensure heading spacing
}
public function render()
{
$this->content = $this->processContent($this->content);
return parent::render();
}
}
Register the component in AppServiceProvider:
Livewire::component('markdown-editor', CustomMarkdownEditor::class);
Plugin System
Use Livewire’s wire:model events to create plugins:
// Example: Auto-save plugin
public function updatedContent($value)
{
if ($this->autoSave) {
$this->saveDraft($value);
}
}
<livewire:markdown-editor
wire:model="content"
auto-save="true"
/>
Shiki Themes Dynamically switch themes based on user preference:
public function getShikiTheme()
{
return auth()->user()->prefers_dark_mode() ? 'github-dark' : 'github-light';
}
Pass to the component:
<livewire:markdown-editor :shiki-theme="getShikiTheme()" />
Toolbar Extensions Add custom toolbar buttons via Alpine:
document.add
How can I help you explore Laravel packages today?