Install via Composer:
composer require nben/filament-record-nav
First use case: Add navigation to a ViewRecord or EditRecord page by including these two lines in getHeaderActions():
use Nben\FilamentRecordNav\Actions\{NextRecordAction, PreviousRecordAction};
protected function getHeaderActions(): array
{
return [
PreviousRecordAction::make(),
NextRecordAction::make(),
];
}
Where to look first:
order_column settingsDefault Integration (90% of cases):
// ViewPost.php
protected function getHeaderActions(): array
{
return [
PreviousRecordAction::make()
->label('← Previous')
->color('gray')
->size(Size::Small),
NextRecordAction::make()
->label('Next →')
->color('gray')
->size(Size::Small),
];
}
Page-Type Control:
// Navigate previous to view, next to edit
PreviousRecordAction::make()->navigateTo(NavigationPage::View),
NextRecordAction::make()->navigateTo(NavigationPage::Edit),
Custom Route Handling:
// In resource:
public static function getPages(): array { return ['custom-view' => ...]; }
// In page:
PreviousRecordAction::make()
->navigateTo(NavigationPage::custom('custom-view'))
->label('View Alternate'),
order_column across all resources for predictable navigation->size()/->color() to match your admin theme->keyBindings() for power users:
PreviousRecordAction::make()->keyBindings(['mod+left'])
Scoped Navigation (using the trait):
use Nben\FilamentRecordNav\Concerns\WithRecordNavigation;
class ViewPost extends ViewRecord
{
use WithRecordNavigation;
public function getPreviousRecord(): ?Model
{
return $this->getRecord()
->newQuery()
->where('status', 'published')
->where('published_at', '<', $this->getRecord()->published_at)
->orderBy('published_at', 'desc')
->first();
}
}
Dynamic URL Resolution:
public function getRecordNavigationUrl(Model $record, $page): string
{
return match($page->value) {
'view' => $record->is_verified
? static::getResource()::getUrl('verified-view', ['record' => $record])
: static::getResource()::getUrl('view', ['record' => $record]),
default => static::getResource()::getUrl($page->value, ['record' => $record]),
};
}
Disabled Buttons:
order_column values (e.g., identical timestamps)->orderBy('id') to queries in custom navigation methodsRoute Not Found:
NavigationPage::custom()getPages() keys match exactly (including hyphens/casing)Performance Issues:
order_columnSchema::table('posts', function (Blueprint $table) {
$table->index('order_column');
});
Verify Queries: Temporarily add:
public function getPreviousRecord(): ?Model
{
\Log::info('Query:', [
'record_id' => $this->getRecord()->id,
'query' => $this->getRecord()->newQuery()->toSql()
]);
return $this->getRecord()->newQuery()->where('id', '<', $this->getRecord()->id)->first();
}
Check Cache: The package caches results per render. Clear cache if testing changes:
// In your test page
protected function resolveRecordNavigationCache(): void
{
$this->resolvedRecordCache = [];
}
Timestamp Columns:
created_at/updated_at only if all records have unique values// config/filament-record-nav.php
'order_column' => ['created_at', 'id'],
Custom Ordering:
CASE WHEN...), override both methods:
public function getPreviousRecord(): ?Model
{
return $this->getRecord()
->newQuery()
->whereRaw('
CASE
WHEN status = "draft" THEN 0
WHEN status = "published" THEN 1
END < ?
', [$this->getRecord()->getStatusValue()])
->orderByRaw('
CASE
WHEN status = "draft" THEN 0
WHEN status = "published" THEN 1
END DESC
')
->first();
}
Dynamic Button Visibility:
PreviousRecordAction::make()
->visible(fn () => auth()->user()->can('view_archived') || !$this->record->is_archived),
Custom Icons:
PreviousRecordAction::make()
->icon(fn () => $this->record->is_first ? 'heroicon-o-chevron-left' : 'heroicon-o-arrow-path')
->color(fn () => $this->record->is_first ? 'gray' : 'primary'),
Event-Based Navigation:
// In your page
protected static function getNavigationEvents(): array
{
return [
'record-navigated' => fn (Model $record) => event(new RecordNavigated($record)),
];
}
order_column (or composite columns)public function getNextRecord(): ?Model
{
return $this->getRecord()
->newQuery()
->where('tenant_id', $this->getRecord()->tenant_id)
->where('status', 'published')
->where('published_at', '>', $this->getRecord()->published_at)
->orderBy('published_at')
->first();
}
For IDE support when using the trait:
use Nben\FilamentRecordNav\Concerns\WithRecordNavigation;
use Nben\FilamentRecordNav\Contracts\HasRecordNavigation;
class ViewPost extends ViewRecord implements HasRecordNavigation
{
use WithRecordNavigation;
}
How can I help you explore Laravel packages today?