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

Filament Blog Laravel Package

firefly/filament-blog

Feature-rich Filament blog plugin for Laravel 11 and Filament 3.x with scheduling, SEO meta tags, comments, newsletters, social sharing, author blocks, and configurable table of contents.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a standard Laravel plugin with a Filament panel entry point, so the fastest path is the happy path in the README — but the ordering matters, because the installer publishes a config file you must edit before migrating.

Minimal sequence:

composer require firefly/filament-blog
php artisan filament-blog:install   # publishes config/filamentblog.php + create_blog_tables migration
# edit config/filamentblog.php NOW — see below
php artisan migrate
php artisan storage:link

Then wire it into your panel:

use Firefly\FilamentBlog\Blog;

public function panel(Panel $panel): Panel
{
    return $panel->plugins([Blog::make()]);
}

And into your user model:

use Firefly\FilamentBlog\Traits\HasBlog;

class User extends Authenticatable
{
    use HasBlog;

    public function canComment(): bool
    {
        return true; // or auth check, karma threshold, verified email, etc.
    }
}

Look first at two things: config/filamentblog.php and the migration the installer dropped into database/migrations/. The config file is the entire integration contract (user model, foreign key, avatar column, route prefix/middleware, SEO defaults, filesystem disk, TOC/author toggles), and the migration is published rather than shipped — meaning you own the schema. Read it before running it.

First use case: add Blog::make() to a panel, log in, create a category, create an author-linked post with a future published_at, then hit /blogs on the front end. That single loop exercises the admin resources, the routing layer, the publish/schedule logic, and the front-end views — if all four work, the integration is sound and everything else is configuration.

Implementation Patterns

Map the package onto an existing User model rather than its own. The Firefly\FilamentBlog\Models\User default is a stub; almost every real deployment points user.model at App\Models\User. Set all three of model, foreign_key, and columns.name / columns.avatar together — changing only the model class and leaving avatar pointing at profile_photo_path when your column is avatar_url produces silent broken images, not an exception.

'user' => [
    'model' => App\Models\User::class,
    'foreign_key' => 'user_id',
    'columns' => [
        'name'   => 'name',
        'avatar' => 'avatar_url',
    ],
],

Treat canComment() as your policy seam. This is the one method the package deliberately hands you, and it's the right place for moderation gating: verified email, account age, a banned_at column, or a role check. Because it's a method on the user rather than a Laravel policy or gate, you can't intercept it from a service provider — the logic has to live on the model (or a trait the model uses). If you need per-post moderation you'll be overriding published views or adding a column to the published migration.

Schedule posts through published_at, not through draft/publish flags. The workflow is: write the post, set a future date, save. The list view then shows upcoming posts and the front end withholds them until that date passes. Practically, this means your content calendar lives in the DB, and your "publish" action is idempotent-ish — you can reschedule by editing the date. Pair this with a Slack/ops reminder or an admin dashboard filter if publishing cadence matters to you.

Configure rendering per-surface, not globally. post_rendering.show_author and post_rendering.table_of_content.enabled are front-end concerns; seo.* is a default that individual posts can override via the meta extension. Keep the defaults in filamentblog.php and push per-post overrides into the admin form — don't hardcode meta tags in Blade overrides, or you lose the editor-facing control that's the point of the SEO feature.

Own the views. Publish them early if you plan any design work at all:

php artisan vendor:publish --provider="Firefly\FilamentBlog\FilamentBlogServiceProvider" --tag=filament-blog-views
php artisan vendor:publish --provider="Firefly\FilamentBlog\FilamentBlogServiceProvider" --tag=filament-blog-components

There are four independent tags (views, config, components, migrations) — a common pattern is to publish views and components but not config, so you get update-able config defaults while still controlling markup. Same goes for the migration: keep it published only if you need schema changes; otherwise consider leaving it in the vendor path so upgrades stay clean.

Route surface is part of your app now. route.prefix (blogs), route.middleware (web by default), route.home.name/url, and route.login.name are all yours to change. If the blog lives behind auth, add auth to the middleware array rather than wrapping it in a route group in web.php — the package registers its own routes, so middleware config is the supported seam. The home URL is used for breadcrumbs/back-links; set it to your actual app root or the link will point at the wrong host in non-production environments.

Newsletter and comments are public POST endpoints. Recaptcha is opt-in via recaptcha.enabled plus RECAPTCHA_SITE_KEY / RECAPTCHA_SECRET_KEY. Enable it on any public deployment, and additionally consider throttling at the middleware layer — the comment form is a classic spam target and the package's built-in protection is only the recaptcha toggle.

Social share requires bringing your own snippet. The package doesn't ship a share widget; you generate JS/HTML from ShareThis and paste it into the published share component/section. Budget an afternoon for this and treat it as a customization, not a feature — it also means cookie-consent implications are yours to handle, since ShareThis sets third-party cookies.

Gotchas and Tips

Order of operations is load-bearing. filament-blog:install → edit config (especially tables.prefix) → migrate. If you migrate first and then decide on a prefix, you're in filament-blog:upgrade-tables territory. The prefix is baked into every model, relationship, and raw query in the package; changing it after data exists is the single most disruptive config change you can make. Decide it on day one, even if the answer is "no prefix."

The 1.x → 2.x upgrade is a migration event, not a composer bump. The README explicitly says back up the database, set tables.prefix, then run php artisan filament-blog:upgrade-tables. Skipping the backup here is the kind of mistake that costs a day. If you're adopting fresh, you'll never hit this — but note it as evidence that schema changes in this package arrive as commands you must run, which is exactly why you should read release notes before any minor bump.

Published migrations don't update themselves. Because create_blog_tables is copied into your app, a future package release that alters the schema will ship a new migration that your published copy may or may not conflict with. After every upgrade, diff the vendor migrations against yours. This is the most likely source of "works on a fresh install, breaks on my app" bugs with this package, and with 145 stars and 0 dependents you won't find it pre-reported on GitHub.

Version pinning is genuinely confusing right now. The README claims Laravel 11 + Filament 3.x, but release notes reference "v4 Support" (v3.0.0), multi-language (v3.1.0), and Filament v5 support (4.0.0). Check the tag you actually installed against your Filament major and pin the constraint ("firefly/filament-blog": "^4.0") rather than floating. A Filament major mismatch surfaces as a broken resource form or a missing Blog::make() method, not a clear error. Also note Packagist reports NOASSERTION for the license while the README says MIT — resolve that with your legal/procurement process before shipping commercially.

Scheduled publishing needs verification, not assumption. The feature is date-driven, but confirm in the package source whether withholding future posts is done at query time (route model binding / scope) or via a scheduled command. If it's a cron/queue job, it needs schedule:run and a worker in production; if it's a scope, it doesn't. Don't discover this at launch. Same discipline for the "new post published notification" — check whether it dispatches on save, on transition to published, or via a job, because that determines whether a queue worker is mandatory and whether backdating a post fires a blast to your whole list.

Subscriber notifications will block your publish action if the mail path isn't queued. Publishing a post apparently triggers notifications to subscribers. On a list of any size with synchronous mail, the Filament save button hangs for tens of seconds and may time out. Ensure QUEUE_CONNECTION is set, a worker is running, and your mail transport is configured before you seed subscribers. Test with a single subscriber first.

Filesystem config must match your Filament uploads. filesystem.disk and visibility govern where post/author images go. If your panel's FileUpload uses s3 or a non-public disk, the blog's images will 404 or vice versa. php artisan storage:link is required for the default public disk — it's a one-liner that's easy to forget in a deploy script, and the failure mode is broken images rather than an error.

Debugging levers, in order: (1) php artisan config:clear after editing filamentblog.php — cached config silently ignores your changes; (2) publish views to inspect what data the front-end components actually receive; (3) check route:list | grep blog to confirm the prefix and middleware applied as you expect; (4) read the published migration to understand the real schema, since docs don't document columns.

Plan for the bus factor. Small package, small maintainer team, thin documentation beyond the README. Practical mitigations: keep your published views/components in version control so you can diff against upstream on upgrade; prefer config over code changes wherever a seam exists; and treat the published migration as a fork point — if you need columns the package doesn't have (moderation state, canonical URL overrides, per-post OG images), add them in your own follow-up migration rather than editing the published one, so a package upgrade doesn't silently drop your changes.

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.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics