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 Pages Laravel Package

beier/filament-pages

A Filament plugin for creating and managing pages in your admin panel. Build page resources with flexible content blocks, control publishing and visibility, and edit everything through a clean, native Filament interface. Ideal for adding simple CMS-style page management to Laravel applications power...

View on GitHub
Deep Wiki
Context7

Getting Started

This package is an extracted Page CMS for Filament: it ships a Page model + migration, a PageResource for the admin panel, and a front-end catch-all route that resolves a slug to a rendered page. It was packaged against Filament v2 and last released in November 2022, so treat it as a reference implementation to copy from rather than a dependency you install and forget.

1. Install (on a Filament v2 app, or a scratch app to read the code):

composer require beier/filament-pages
php artisan vendor:publish --provider="Beier\FilamentPages\FilamentPagesServiceProvider"
php artisan migrate

Publish the config and views before migrating — the migration and the front-end view are the two things you will almost certainly want to edit.

2. Where to look first, in this order:

  • src/FilamentPagesServiceProvider.php — what gets registered (PageResource, route file, views, config, migrations).
  • routes/web.php inside the package — the catch-all route definition and the middleware it runs under.
  • src/Models/Page.php — the schema (slug, title, published flag, layout/view, content blocks) and any resolveRouteBinding override.
  • The PageResource + form schema — this is the actual contract of what fields exist.
  • resources/views/ — the single Blade view every page renders through. This is your extension point for layouts.

3. First use case (15 minutes):

Create a page in the admin panel (/admin/pages), give it a slug like about, fill the content field, mark it published, save — then hit /about in the browser. If that works, you understand 90% of the package. If it 404s, the problem is almost always the catch-all route being shadowed by another route or the published scope.

Implementation Patterns

Let the package own the model, but override the resource. The fastest sustainable pattern with an archived package is: keep Beier\FilamentPages\Models\Page as-is, and extend PageResource in your own app/Filament/Resources/PageResource.php to add fields. Re-publishing views/migrations and editing in place works too, but you lose the upgrade path (there is none) and you pollute vendor/.

Custom front-end layouts via the view column. The common pattern is a layout / view column on the page that maps to a Blade view:

// routes or a Page::render() helper
return view($page->view ?? 'pages.default', ['page' => $page]);

Keep a whitelist in config and validate the layout field with ->in(config('filament-pages.layouts')). Never let an editor free-type a view name — arbitrary view resolution is a file-inclusion footgun.

Slug routing with a scoped fallback. The catch-all route (/{slug} or /{page:slug}) must be registered last. In routes/web.php, register all your explicit routes and Route::fallback(...) style page route after them, and exclude admin/auth prefixes via a regex constraint:

Route::get('/{page:slug}', PageController::class)
    ->where('page', '^(?!admin|api|login).*$')
    ->middleware('web');

Separate "draft preview" from public routing. Add a published_at (or boolean) check in the controller, and give authenticated admins a bypass:

$page = Page::where('slug', $slug)
    ->when(! auth()->user()?->can('view unpublished pages'), fn ($q) => $q->published())
    ->firstOrFail();

Treat content as structured, not one blob. If the package gives you a single rich-text column, keep it, but add SEO fields (title, description, OG image) as separate columns and pass them to the layout rather than embedding meta tags in the editor body. Filament's RichEditor output is HTML; render it with {!! !!} only after sanitizing (see Gotchas).

Integrate with navigation/menus. Pages are rarely standalone. Query the model for menu building:

$nav = Page::published()->where('in_menu', true)->orderBy('menu_order')->get(['title', 'slug']);

Cache that query — page content is a read-heavy workload and the catch-all route means every front request touches this table.

Test the route resolution, not the resource. A Pest/PHPUnit feature test asserting /about returns 200 with the page title is worth more than testing the Filament form; the form will break loudly, the route will silently 404.

Gotchas and Tips

  • Archived + Filament v2 only. Do not install this into a Filament v3/v4 app expecting it to work — APIs like Forms\Components namespacing, Filament\Resources\Resource signatures, and ->schema() vs ->form() changed. Read it as a blueprint and port the pieces.
  • Route order is the #1 failure mode. A catch-all /{slug} swallows /login, /admin, /sitemap.xml, livewire asset routes, and storage routes. Constrain the slug pattern ([a-z0-9-]+, no slashes) and register last. Debug with php artisan route:list --path=about before assuming the package is broken.
  • Slug collisions and reserved words. Add a unique:pages,slug validation rule plus a reserved-slug blocklist (admin, api, login, register, sitemap). Editing a published page's slug breaks inbound links — either freeze slugs after publish or add a redirect table.
  • Draft pages still resolve by direct URL unless you scope the query. The published filter belongs in the controller/resolveRouteBinding, not only in the admin form. Also remember soft-deleted pages: firstOrFail on a trashed page throws only if the model uses SoftDeletes and the default scope applies.
  • HTML injection from the editor. RichEditor/Tiptap output is arbitrary HTML. Sanitize on save (HtmlSanitizer, strip_tags with an allowlist) or on render — the package ships no sanitization. Especially important if multiple users can edit pages.
  • Image uploads and storage. If the resource has FileUpload fields, verify disk() and directory() match your FILESYSTEM_DISK and that php artisan storage:link has been run. A published page referencing /storage/... that 404s is the most common "it worked in admin" bug.
  • Cache invalidation is on you. If you cache page HTML or the route list (php artisan route:cache), a newly created page won't appear, and cached HTML survives edits. Add Page::saved/deleted observers that flush a tagged cache (Cache::tags('pages')->flush() — requires a tag-capable store; the database/array drivers don't support tags).
  • Model swapping. If you need custom behavior, bind your own model in a service provider and update the resource's $model. The package's migrations are already published, so your model can extend the original and add relationships (categories, translations) without a schema rewrite.
  • Extension points are the views and the resource. There are no events or contracts to hook into — plan on copying the Blade views into resources/views/vendor/filament-pages/ (if the provider registers loadViewsFrom) and overriding the resource. Grep the service provider to confirm which publishes() keys actually exist before writing publish tags in your deploy scripts.
  • Deployment check. On a fresh environment the sequence is: migrate → storage:link → view:clear → confirm the catch-all route position. Missing view:clear after copying package views is a surprisingly common silent failure with cached compiled views.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core