stepanenko3/nova-markdown
stepanenko3/nova-markdown provides a Markdown editor field for Laravel Nova, letting you write and preview Markdown content directly inside Nova resource forms. It stores plain Markdown and integrates cleanly with your existing Nova admin panel and models.
Architecture fit. This is not a Laravel package in the general sense — it is a Laravel Nova field (a PHP field class plus pre-compiled Vue assets) that slots into a Nova resource form. Architecture fit is therefore binary and cheap to determine: if the product runs Nova, the field is a drop-in presentation-layer component; if it runs Filament, Livewire, Inertia, or a standalone admin panel, fit is zero and evaluation ends. Where it does fit, the architectural property that matters most to a TPM is that it stores raw Markdown in an existing TEXT/LONGTEXT column. That means no schema change, no data migration, and no proprietary JSON/block format. The stored value is the contract; rendering is a separate, app-owned, server-side concern. This is the single most risk-mitigating characteristic of the package and should be written down as a hard constraint (in a model comment or a test) so a future contributor cannot casually introduce lock-in.
Integration feasibility. Realistically under half a day for a Nova-fluent developer, gated on three checks that must be done before any code is written:
composer.json.laravel/nova constraint — the last release is 2023-09, which predates Vue 2 → Vue 3 field toolchain changes in newer Nova majors. If that constraint does not cover the pinned major today and the upgrade roadmap, the evaluation is over.The audit is roughly 0.5 engineer-days (composer constraints, src/ for the real field class and fluent method names, dist/ for whether assets are pre-compiled, and the bundled Markdown parser version). The spike is another 0.5 days and should end with a rendered, sanitized page from a real Nova resource. If it doesn't, that is a "no."
Technical risk. Ranked by what a TPM actually has to accept:
dist/, and sanitize at the app layer regardless (Str::markdown($value, ['html_input' => 'strip', 'allow_unsafe_links' => false]), or league/commonmark plus a sanitizer behind a single MarkdownRenderer service). Never render the preview's HTML on the read path.->onlyOnForms() and a cheap read-only Text column on index. Test two Markdown fields (body and excerpt) on one resource before committing.Key questions to answer before approving.
Stack fit. Laravel + Nova only, with the field added fluently to a resource's fields() method alongside standard Nova fields. Verify the namespace in src/ rather than trusting the README — "editor height" and "behavior" options often map to fluent methods that changed between releases. Grep for public function in the field class before writing ->height(400) into ten resources.
Migration path. There is effectively none, which is the point. Point the field at an existing TEXT/LONGTEXT column on one low-risk internal resource — an FAQ entry, an internal doc, a blog draft. No cast on the model. No schema change. No backfill. If the pilot fails, the column is still plain Markdown and a Textarea::make() renders it.
Compatibility. Pin both sides to exact-ish constraints and add a CI check that fails when the Nova major moves without a compatibility review:
"laravel/nova": "4.35.*",
"stepanenko3/nova-markdown": "1.0.*"
Confirm the package resolves from Packagist; if it requires a VCS repository entry, treat that as a standing cost in CI and onboarding.
Sequencing (time-boxed, one sprint).
composer.json constraints, src/ field class and public methods, dist/ asset state and parser version, README accuracy.vendor:publish, nova:publish, wire one field to one existing column, save and render a sanitized page server-side. Include a staging deploy so asset publishing is exercised for real.$editor = config('content.markdown_field', Markdown::class);
$editor::make('Body')->rules('required'),
Document that flip in the runbook before the pilot starts. 4. Pilot — one resource, internal audience only. Authoring on forms only; cheap read-only column on index. Manual smoke checklist in the PR: create, edit, save empty, save long content, preview, paste HTML, reload and confirm persistence. Check Chrome and Safari. 5. Decide. Expand, hold, or flip the config back. Pre-agree the three exit paths (swap to in-house field, vendor/fork under MIT, revert to textarea) — all are days, not quarters.
Upgrade-day runbook (every Nova major bump, staging first): composer update laravel/nova → nova:publish → view:clear → optimize:clear → app asset build if involved → load create/edit/preview on the affected resource in two browsers. Check the network tab for 404s on vendor assets before debugging anything else.
Maintenance. The dominant cost, and it is not optional. Budget a recurring allowance that lands on the same calendar as Nova upgrades, and name an owner by person. Scope for that allowance: re-auditing the bundled parser on security advisories, re-running the publish step per upgrade, and validating the constraint matrix. Treat the package as internally owned code with an upstream that occasionally ships.
Support. MIT, no SLA, no vendor, single maintainer, no security response commitment. Internal support is you. Mitigate by keeping the integration surface deliberately thin — one field class, one config key, one renderer service, no forking of behavior into ten resources — so the blast radius of any upstream or Nova change is one file.
Scaling. Two axes:
->onlyOnForms() plus a truncated read-only Text column keeps index pages fast; per-row editor mounting is the classic performance and duplicate-ID regression.Failure modes and detection. Each of these should have a named signal:
| Failure | Symptom | Detection |
|---|---|---|
| Asset publish skipped/stale | Blank field, missing toolbar, no error | Network tab 404s on public/vendor/...; publish step in deploy pipeline |
| Nova major drift | Field renders but breaks silently after upgrade | CI check on Nova constraint; staged upgrade runbook |
| Preview XSS | No visible symptom — this is the danger | Parser audit + app-layer sanitization + CSP on Nova routes; do both |
| Empty string vs. null | '' in DB, inconsistent whereNull queries |
Normalize via fillUsing if the field doesn't |
| Duplicate component IDs | Broken editor when two Markdown fields share a form | Explicit test before shipping body + excerpt |
| Double escaping | <p> in output |
Renderer tests asserting sanitized HTML in, HTML out |
| Unmaintained upstream | Slow accumulation of unresolved constraint drift | Pinned versions + scheduled audit slot |
Ramp-up. Minimal for developers (half a day), non-trivial for authors. The split-pane raw-syntax surface is a technical-writer tool; if the pilot audience is marketing or support staff, the correct answer is WYSIWYG or a CMS and no amount of configuration fixes it. That is a staffing/product decision, not a tooling one, and it should be made before the pilot, not after.
Overall posture for the TPM. Classify as an internal productivity enhancement, not a platform bet. Approve only a time-boxed pilot (audit + spike + one sprint) on a non-critical internal workflow, with a named owner, pinned versions, a pre-approved rollback to textarea, and a documented exit. The downside is bounded because the content format is vendor-neutral. The honest comparison is not "this package vs. nothing" but "this package vs. 1–2 engineer-days of an in-house field around a maintained editor" — and at 10 stars and 0 dependents, defaulting to build unless the audit is clean is a defensible call, not a cop-out.
How can I help you explore Laravel packages today?