dewsign/nova-blog
dewsign/nova-blog is a Laravel Nova package for managing blog content from your admin panel. It adds Nova resources for creating, editing, and organizing blog posts, so you can publish and maintain a simple blog directly alongside your Laravel application.
Verify compatibility before you composer require anything. The last release is January 2020, which puts it in the Nova 2/3 and Laravel 6/7 era — a current Nova 4/5 or Laravel 10+ app will almost certainly refuse the constraint. Run a dry run first:
composer require dewsign/nova-blog --dry-run
If it resolves, the install is the standard package dance:
composer require dewsign/nova-blog
php artisan vendor:publish --provider="Dewsig\NovaBlog\NovaBlogServiceProvider" # confirm the provider name from src/
php artisan migrate
php artisan storage:link
php artisan nova:publish && php artisan optimize:clear
Read three things in vendor/dewsign/nova-blog/ before writing any code: the service provider (what it auto-registers), the database/migrations (table names — you'll query them directly), and src/Models (the exact namespace and the published/draft column). Those three files define your whole integration surface.
First use case — end to end in five minutes: open Nova, find the blog menu group, create a Category, create an Author, then create an Article with a Markdown body, a featured image plus its alt text, a slug, meta fields, and a publish date. That single Article exercises every feature the package ships: relations, rich text, media, SEO, and publishing state. Once it exists in the database, you know exactly what your front end has to consume.
Front-end rendering is yours — the package gives you Nova admin screens and Eloquent models, not public routes or Blade views. Expect to write a controller, a route, and a template.
Query published content explicitly. The package stores a publish date; it does not magically filter your front-end queries. Wrap it once and use it everywhere:
$articles = Article::query()
->whereNotNull('published_at')
->where('published_at', '<=', now())
->with(['category', 'author']) // avoid N+1 on the index page
->latest('published_at')
->paginate(12);
Scheduled posts then work for free, provided you're consistent about timezone (config('app.timezone') vs. how editors think about the date field).
Bind by slug explicitly. Don't subclass the vendor model just to add getRouteKeyName(). Use implicit binding in the route:
Route::get('/blog/{article:slug}', [BlogController::class, 'show']);
This keeps the package model untouched and survives upgrades.
Customize Nova without forking. The usual pattern is to subclass the package's Nova resource and register your subclass instead of the original, or to publish the config and disable auto-registration so NovaServiceProvider::resources() is the single source of truth. You'll want this the moment you need an extra field, a policy, or a reordered form. Watch for the original and your subclass both appearing in the sidebar — that's the classic symptom of auto-registration plus manual registration.
Keep Markdown and WYSIWYG separate in your head. If you store Markdown, render with CommonMark and you're XSS-safe by construction. If you store WYSIWYG HTML, sanitize on output (HTMLPurifier or a whitelist) before {!! !!}. Pick one per field and document it for editors; mixing both across Article and Category bodies makes templates unpredictable.
Render alt text with an explicit fallback. v1.3.1 added the field on both Articles and Categories, but existing rows are null. Decide the policy now:
<img src="{{ $article->image_url }}"
alt="{{ $article->image_alt ?: $article->title }}">
A missing alt attribute is an accessibility failure; an empty alt="" is a deliberate choice for decorative images. Never omit the attribute entirely.
Add policies early. Nova resources are gated by policies on their underlying models. Without them, every Nova user can edit and delete blog content. Write ArticlePolicy, CategoryPolicy, and AuthorPolicy as part of the initial install, not as a follow-up ticket.
Write your own factories. Small packages rarely ship them. Article::factory() with states for draft/published/scheduled will pay for itself the first time you test the public index.
nullable(). If they're not, the migration will fail on any table with existing rows. Back up first regardless.NovaServiceProvider. If you can't find a config flag to turn off the package's resource registration, you may be stuck with duplicate menu entries once you subclass. The workaround is to remove the package's provider from config/app.php (or bootstrap/providers.php) and register the resources manually.public or private. A private disk plus no signed URL logic equals broken thumbnails in production but working ones locally — a classic Nova trap.composer update on a Nova package you usually need php artisan nova:publish, config:clear, view:clear, and a browser hard refresh — Nova caches compiled assets aggressively and stale JS looks like a bug in the package.How can I help you explore Laravel packages today?