Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Markdown Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the ground truth before you install anything

This is a Nova field, not a general Laravel package. Two checks first, in this order:

  1. Nova major pinned in the app's composer.json (e.g. laravel/nova: ^4.0).
  2. The package's own composer.json constraint on laravel/nova. Last release is 2023-09, which predates several Nova field toolchain changes. If the constraint doesn't cover your pinned major today and your upgrade roadmap, stop — the cost of adoption is now unbounded relative to the benefit.

Then read the package's src/ directory before you write a single line of integration code. At 10 stars and a single maintainer, src/ is the spec, not the README.

Install (feature branch, staging first)

composer require stepanenko3/nova-markdown
php artisan vendor:publish --provider="Stepanenko3\NovaMarkdown\FieldServiceProvider"
php artisan nova:publish

The provider class name above is illustrative — confirm the actual class in src/. If the package resolves from a VCS repository entry rather than Packagist, treat that as real friction for CI and for whoever inherits the repo.

Where to look first

File Why
composer.json Nova/Laravel/PHP constraints, license header
src/*.php Actual field class name, namespace, and public option methods
dist/ or resources/js/ Pre-compiled assets (no Node needed) vs. must-build
Bundled parser in dist/ marked / markdown-it version — this is your XSS surface

First use case: one low-risk internal resource

An FAQ entry, internal doc, or blog draft backed by an existing TEXT/LONGTEXT column. Nothing public-facing, no multi-author workflow.

// app/Nova/FaqEntry.php
use Stepanenko3\NovaMarkdown\Markdown; // verify namespace in src/

public function fields(Request $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Title')->rules('required', 'max:255'),
        Markdown::make('Body')
            ->rules('required')
            ->help('Markdown supported. Rendering is handled server-side.'),
    ];
}

The field stores raw Markdown in body. Do not add a cast. Rendering is a separate, app-owned, server-side concern — see below.


Implementation Patterns

Separate authoring from display — always

The editor is a form control. On index and detail views you almost always want something cheap and read-only instead of mounting a full editor per row:

Markdown::make('Body')
    ->onlyOnForms()
    ->rules('required'),

Text::make('Body')
    ->onlyOnIndex()
    ->displayUsing(fn ($value) => Str::limit(
        strip_tags(Str::markdown($value ?? '')), 80
    )),

Mounting the editor on the index is the most common cause of slow Nova index pages and duplicated component IDs when several rows render.

Never render the preview's HTML — render your own

The live preview is client-side convenience. The stored value is the contract, and the read path is yours:

use Illuminate\Support\Str;

$html = Str::markdown($post->body ?? '', [
    'html_input' => 'strip',        // drop raw HTML from authors
    'allow_unsafe_links' => false,  // block javascript: / data:
]);

If you need more control, wire league/commonmark directly and add an HTML sanitizer. Put this in a single MarkdownRenderer service or Blade component so every consumer (API, Blade, mail) goes through one path. If the app already has a renderer, use it — don't let the field imply a second one.

Treat plain-Markdown storage as a hard constraint

No JSON casts, no block schema, no per-field metadata table. Encode this in a test or a code comment in the model so a future contributor can't casually introduce lock-in. The entire risk-mitigating property of this package is that reverting to a textarea costs you formatting affordances, not content.

Rollout pattern: config-gated swap

Since you're adopting unmaintained code, make the field swappable per resource without a revert PR:

// config/content.php
'editor' => env('CONTENT_EDITOR', Stepanenko3\NovaMarkdown\Markdown::class),

// resource
$editor = config('content.editor');

$editor::make('Body')->rules('required'),

A one-line config flip returns every resource to Textarea::make(). Document that flip in the runbook before the pilot.

Upgrade-day workflow

Every Nova major bump, run in this order on staging:

composer update laravel/nova
php artisan nova:publish
php artisan view:clear
php artisan optimize:clear
npm run build   # if the app's own assets are involved

Then load create / edit / preview on the affected resource in Chrome and Safari. Silent asset-publish failures are the single most common breakage mode for custom fields.

Testing

You will not get meaningful coverage of the editor without a JS test harness the package doesn't ship. Test what you own:

  • Model/renderer tests: given this Markdown, assert this sanitized HTML.
  • Rule validation: required, max: behave on the field.
  • A manual smoke checklist in the PR: create, edit, save empty, save long content, preview, paste HTML, reload and confirm persistence.

Guard the version matrix

Pin both Nova and the field to exact-ish constraints, and add a CI check that fails when the Nova major changes without a compatibility review:

"laravel/nova": "4.35.*",
"stepanenko3/nova-markdown": "1.0.*"

Gotchas and Tips

You are adopting the code, not consuming it. Last release 2023-09, 10 stars, 0 dependents, one maintainer. Plan on being the maintainer: budget a recurring allowance that lands on the same calendar as Nova upgrades, and name an owner by person, not by team.

The preview pane is a staff-session XSS vector. The bundled parser executes inside an authenticated admin session. Two mitigations, do both:

  1. Audit dist/ for the parser and sanitizer (marked, markdown-it, DOMPurify) and their versions.
  2. Sanitize at the app layer regardless — never render author HTML directly, and set a CSP on the Nova routes.

Asset publishing fails silently. Stale files in public/vendor/... after a deploy will give you a blank field or a missing toolbar with no error. If the editor isn't there, check the network tab for 404s on the vendor assets first, then re-run nova:publish. Ensure the publish step runs in your deploy pipeline, not just locally.

Method names are guesses until you read src/. Descriptions of "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.

Empty string vs. null. Editors commonly emit '' on an empty field. If the column is nullable and your rules say nullable, you'll get '' in the DB and inconsistent whereNull queries. Normalize in a fillUsing if the field doesn't already:

->fillUsing(function ($request, $model, $attribute) {
    $model->{$attribute} = $request->input($attribute) ?: null;
})

Media uploads: default to "no." If the field exposes image or file upload, it writes wherever it likes and bypasses your storage abstraction, ACLs, and lifecycle rules. Disable it, or scope it deliberately — that's a separate decision, not an install detail.

Multiple Markdown fields on one resource is the classic duplicate-ID bug in thin editor wrappers. Test body and excerpt on the same form before you ship.

Don't double-escape on the read path. The DB holds Markdown, not HTML. If output looks like <p>, something is escaping the stored value before the renderer or twice after it.

Non-technical authors won't use this. A split-pane Markdown editor with a raw syntax surface is a technical-writer tool. If your pilot authors are marketing or support staff, a WYSIWYG or a proper CMS is the correct answer and no amount of configuration fixes it.

Time-box the whole thing. Audit half a day, spike half a day. If the spike doesn't end with a rendered, sanitized page from a real Nova resource, the honest alternative is a thin in-house field around EasyMDE or Toast UI — roughly 1–2 engineer-days, and you inherit a maintained editor instead of owning an abandoned one. At this package's adoption level, in-house is a genuine competitor, not a fallback.

Pre-agree the exit before you merge. Three documented paths: swap to the in-house field, vendor/fork under MIT, or flip the config back to textarea. All are days, not quarters — which is exactly why this deserves a time-boxed pilot rather than a prolonged evaluation.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo