digital-creative/resource-navigation-link
A Laravel Nova package that adds configurable navigation links to your resources, letting you jump quickly between related records or external destinations from a resource's index and detail views with minimal setup.
Install the field like any Nova package:
composer require digital-creative/resource-navigation-link
Laravel's package auto-discovery registers the service provider. If your app has discovery disabled, add DigitalCreative\ResourceNavigationLink\ResourceNavigationLinkServiceProvider to config/app.php. If the package ships compiled JS you may need php artisan vendor:publish --tag=nova-assets (or nova:publish) and a npm run dev/build in your Nova app — check README.md for the exact asset tag.
Where to look first:
README.md — the supported method chain (the package is small; the field class in src/ is the source of truth).src/ResourceNavigationLink.php (or similar) — see which withMeta() keys the Vue component reads. That tells you exactly which public methods exist and what they do.resources/js/components/ — confirms how the URL and label are rendered (<a> vs. <Link>, target/rel handling).First use case — turn a raw foreign key into a clickable link in the index/detail view:
use DigitalCreative\ResourceNavigationLink\ResourceNavigationLink;
public function fields(NovaRequest $request): array
{
return [
ResourceNavigationLink::make('Customer')
->resolveUsing(fn () => $this->customer)
->to(fn () => route('nova.customers.show', [
'resource' => 'customers',
'resourceId' => $this->customer_id,
])),
];
}
Even if the method names differ slightly in your installed version, the shape is the same: a Field subclass whose value or closure becomes the link target. The first thing to get working is a link from an index row to a related Nova resource detail page — that's the 80% use case.
Link to a related Nova resource. For a belongsTo, resolve the URI at render time rather than hard-coding the resource name:
ResourceNavigationLink::make('Order')
->to(fn () => \Laravel\Nova\Nova::resourcePath($this->order->uriKey(), $this->order->getKey()))
or with a resource instance helper if the package exposes one (e.g. ->toResource(Order::class, 'order_id')). Prefer the resource class reference over a string so refactors/renames are caught by static analysis.
External navigation. Use it for outbound destinations: Stripe dashboard, CRM record, docs, S3 object presigned URL. Always set an absolute URL and make sure the component adds target="_blank" rel="noopener noreferrer". Generate signed URLs lazily in a closure so they aren't computed on list pages where the field is hidden.
Label vs. URL. Keep the displayed value meaningful (order number, customer name, status string). Use ->displayUsing() or a closure on make() to set the label, and use the package method to set the destination. Don't put HTML in the label unless the component explicitly renders raw HTML.
Conditional rendering. Combine with standard Nova field visibility: ->onlyOnIndex(), ->onlyOnDetail(), ->hideFromIndex(), ->canSee(fn () => $request->user()->can('view', $this->order)). This is the cleanest place to enforce that a link never points to a resource the user can't open.
In BelongsTo/MorphTo relationships. Use a single navigation link as a read-only presentation field next to (not instead of) the relationship field. The relationship field handles search/attach; the link field handles navigation from the index.
Subclass for repeated patterns. If you link to the same resource in many places, extract a small factory:
class OrderLink extends ResourceNavigationLink
{
public function __construct(string $attribute = 'Order')
{
parent::__construct($attribute);
$this->to(fn () => Nova::resourcePath('orders', $this->order_id));
}
}
Compose with other fields. Use a companion text/searchable field for filtering and sorting; keep the link field presentation-only. In index tables, this gives you BelongsTo (searchable) + ResourceNavigationLink (clickable shortcut) side by side, or just the link if read-only.
Test at the three surfaces. A Nova field renders differently on index, detail, and form. A link field usually makes no sense on forms — assert onlyOnIndex/onlyOnDetail in your resource tests. Snapshot-test the JSON payload with $field->jsonSerialize() in a plain PHPUnit test.
$fillable entry. Don't accidentally add it to $casts or a query. If the value is derived, add ->onlyOnIndex()/->onlyOnDetail() so Nova doesn't try to render it in create/edit forms.->to(fn () => ...) executes for every row on the index. If the closure hits the database, you've introduced an N+1 (or N+3). Eager-load the relationship in the resource's indexQuery or resolveUsing, and prefer $this->relation?->getKey() over a fresh Model::find() inside the closure.?-> and fall back to a plain string field or ->hide(). A link to nova/resources/orders/ with an empty ID is a silent 404.resourcePath URL so the front-end router can intercept it; an absolute URL forces a full page load. For external targets, require https:// explicitly — otherwise the browser may resolve it relative to your app.uriKey() at build time vs. runtime. Don't resolve Order::uriKey() in a property initializer where the resource may not be registered yet; resolve it inside the closure.canSee on the field with a Gate::check on the target policy if you want a graceful "no link" instead of a broken click.composer update, if the field renders blank or as JSON, run php artisan nova:publish (and rebuild) before debugging the field itself. Check the browser network tab for a missing resource-navigation-link.js.dd(ResourceNavigationLink::make('X')->to(fn () => '/foo')->jsonSerialize()) in a controller test, or php artisan tinker, exposes exactly which meta keys the Vue component expects — faster than guessing method names from the README.->sortable() hoping for it to work; add a separate database-backed field if sorting matters.withMeta([...]) and jsonSerialize() overrides are the intended hooks. You can attach extra meta (e.g. a title tooltip or icon) without forking, if the component reads it — check the Vue file before assuming it does.composer.json, and read src/ before upgrading a major Nova version — small Nova packages tend to break silently across Nova 4 → 5 because the Field base class and asset pipeline changed.How can I help you explore Laravel packages today?