nitrado/code-mirror-bundle
A Symfony bundle that integrates the CodeMirror code editor into PHP applications, simplifying asset setup and configuration so you can add syntax-highlighted, customizable code editing to forms and templates with minimal effort.
This is a Symfony2 bundle, not a Laravel package. There is no service provider, no config/code-mirror.php, and its Composer constraints will almost certainly conflict with a modern Laravel install (Symfony 2.x-era symfony/framework-bundle, PHP 5.x/7.0-era code). Treat this repository as a reference implementation to port, not a dependency to composer require. Budget an afternoon, not five minutes.
Where to look first, in this order:
composer.json – confirm the Symfony/PHP constraints. This tells you immediately whether it can coexist with anything you run today (it usually can't).DependencyInjection/Configuration.php and SolutionCodeMirrorExtension.php – this is the entire configuration surface: default mode, theme, height, whether assets are auto-included.Form/Type/ – the bundle's value is almost certainly a CodeMirrorType (or a code_mirror widget option) that wraps a textarea.Resources/views/ – the Twig template that emits the <textarea> plus the CodeMirror.fromTextArea(...) init script.Resources/public/ – whether CodeMirror itself is vendored (old bundles shipped it) or expected from elsewhere.Minimal Symfony2 wiring (for reference when porting):
# app/config/config.yml
solution_code_mirror:
mode: yaml
theme: default
height: 400px
$builder->add('template', CodeMirrorType::class);
Your actual first use case in Laravel is the one this bundle was built for: an admin form where users edit JSON/YAML/Twig/Markdown in a plain <textarea>, and you want syntax highlighting, line numbers, and bracket matching. The port is:
npm install codemirror
{{-- resources/views/components/code-editor.blade.php --}}
<div wire:ignore x-data x-init="
const cm = CodeMirror.fromTextArea($refs.ta, { mode: '{{ $mode }}', lineNumbers: true, theme: '{{ $theme }}' });
cm.setSize(null, '{{ $height }}');
cm.on('change', () => $refs.ta.value = cm.getValue());
">
<textarea x-ref="ta" name="{{ $name }}" {{ $attributes }}>{{ $slot }}</textarea>
</div>
That single Blade component replaces the whole bundle. Everything below assumes you are either doing that port or maintaining a legacy Symfony2 app that already has it installed.
Legacy Symfony2 (existing app):
<script> tags, so mode/theme come from config and you get consistent markup. Override per-field with the type's options if it exposes them (mode, theme, height) instead of editing the bundle.config.yml under twig.form_themes rather than calling form_widget with custom markup per view. One theme, applied globally, is the maintenance-light path.<script>/<link> via a Twig extension or a kernel.request listener. If you have a proper asset pipeline (Assetic, Webpack Encore), disable the bundle's auto-include and load CodeMirror once in your base layout to avoid duplicate copies on every page.Laravel port (what you'll actually build):
CodeMirror.fromTextArea call inside a small Alpine component so it works identically in plain Blade, Livewire, and dynamically-added rows. Do not scatter init scripts through views.cm.on('change', ...) writing back to the textarea is more robust than a submit handler — it survives Livewire's wire:model round-trips and browser back/forward. If you rely on save()-on-submit, any Livewire re-render will silently drop edits.wire:ignore + $nextTick), re-run CodeMirror.fromTextArea on the new textarea. Keep an init function you can call repeatedly; guard with if (el.dataset.cmInit) return;.@codemirror/lang-json, or in v5 codemirror/mode/yaml/yaml). A single "load everything" import is what bloats the bundle; the Symfony bundle used to sidestep this by shipping prebuilt mode/ files, which you should not copy.Validator::make / FormRequest exactly as before. The bundle never touched validation and neither should your component.mode, theme, height, readonly, lineNumbers as component props with sane defaults, so mode="yaml" is the only thing a view author writes.<textarea> first; if JS fails or Vite hasn't loaded, the form still submits. This is the main thing the old bundle got right — preserve it.composer.json; vendor the idea, not the code.CodeMirror.fromTextArea is v5 API). If you follow its examples while installing codemirror@6, nothing will work — v6 is EditorView/EditorState with no fromTextArea. Pick v5 (simple, drop-in, deprecated) or v6 (ESM, tree-shaken) deliberately and don't mix tutorials.fromTextArea requires the element to be in the DOM and visible. Initializing inside a hidden tab, modal, or collapsed x-show="false"} panel yields a zero-width editor. Call cm.refresh() after showing, or defer init until visible.change → textarea.value.data-* attributes and read them with el.dataset, not via string interpolation into <script>.x-init/<script> blocks break under a strict script-src. If you have CSP, move init into a compiled JS file and bind by data-code-editor attribute rather than inline handlers.mode in config.yml may require cache:clear. Don't expect per-request flexibility; if you need per-form modes, that's an argument for the component port.codemirror script tags before debugging anything else.How can I help you explore Laravel packages today?