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

Nova Id Link Laravel Package

dillingham/nova-id-link

A Laravel Nova package that turns resource ID fields into clickable links to their detail pages, making it easy to navigate between related records in the admin panel with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a single-field Nova package: it swaps the plain ID::make('ID') field for a link-capable one, so the primary key in your resource index/detail views becomes clickable instead of dead text.

Install

composer require dillingham/nova-id-link

No manual service-provider registration is needed on a standard Laravel install (package auto-discovery); if you're on an older skeleton, verify by checking vendor/dillingham/nova-id-link/composer.json for a providers entry.

Where to look first

  • vendor/dillingham/nova-id-link/src/ — the field class is tiny; read it before wiring anything up. The method that sets the link target is the whole API surface, and it tells you whether you're passing a string, a closure, or a $resource-aware callback.
  • The README's usage block — it's short and, given the package's age, the most reliable source for the exact method name.

First use case

The canonical win is making the ID row-navigable on the index. Replace your existing field in app/Nova/{Model}.php:

use Dillingham\NovaIdLink\IdLink;

public function fields(Request $request)
{
    return [
        IdLink::make('ID')->sortable(),

        Text::make('Name'),
        // ...
    ];
}

Immediately this gives you a clickable identifier in the index that jumps to wherever the field points — the detail screen, an external admin tool, or a customer-facing page. Start there, confirm it renders in your Nova version, and only then extend it to other resources.

Implementation Patterns

Index quick-navigation to an external system

The most common pattern I'd reach for: Nova is your back office, but the object also lives in a CRM, billing provider, or legacy admin. Make the ID the bridge.

IdLink::make('ID')
    ->sortable()
    ->url(fn ($resource) => "https://billing.example.com/subscriptions/{$resource->id}"),

Keep the closure pure and synchronous — it runs once per row.

Detail-view linking, forms untouched

IDs should never be editable. Scope the field so it only appears where it's useful:

IdLink::make('ID')
    ->sortable()
    ->onlyOnIndex(),      // or ->exceptOnForms()

If you need it on the detail screen but not the index, flip to ->onlyOnDetail(). On the form side, if you must show it, pair with ->readonly().

Conditional linking

Not every row has a corresponding external record (soft-deleted, imported rows, drafts). Prefer falling back to plain text over emitting a dead # href:

IdLink::make('ID')
    ->url(fn ($resource) => $resource->external_url ?: null),

Confirm in the field source how null is handled — if it emits an empty anchor, wrap the logic so the field is only used where a URL always exists, or subclass the field to short-circuit.

Signed / authenticated URLs

When the destination requires auth, generate the link in an accessor rather than inside the Nova field, so the signing logic is testable and reusable:

// In your model
public function getAdminUrlAttribute(): string
{
    return URL::temporarySignedRoute('orders.show', now()->addMinutes(10), [
        'order' => $this->getKey(),
    ]);
}

Then IdLink::make('ID')->url(fn ($resource) => $resource->admin_url). Note the short expiry: Nova is a long-lived SPA, so a link rendered at page load may be stale by the time someone clicks it. Use a generous window or don't sign at all.

Custom primary keys and hashed IDs

Always resolve via $resource->getKey() rather than $resource->id if the model overrides getRouteKeyName() or uses UUIDs/hashids. The underlying column is still id for sorting purposes, so ->sortable() continues to work as expected.

Pairing with copy-to-clipboard

In Nova 4+, support staff often want to copy the ID rather than click it. Keep a plain Text::make('ID')->copyable()->onlyOnDetail() alongside, or use the ID link on the index and a copyable text field on detail — you get navigation in the list and a copy affordance where people actually work.

Macro / subclass for house style

If half your resources link out, don't repeat the closure:

// App\Nova\Fields\ExternalIdLink.php
class ExternalIdLink extends IdLink
{
    public function __construct($name = 'ID', $attribute = null, $resolveCallback = null)
    {
        parent::__construct($name, $attribute ?? 'id', $resolveCallback);
        $this->url(fn ($resource) => config('services.admin.base_url').'/'.$resource->getKey());
    }
}

This is the cleanest extension point the package offers — one class, one default, used everywhere.

Gotchas and Tips

This package predates Nova 4. Last release was February 2019, which puts it squarely in the Nova 1/2 era — i.e. a Vue 2 field component registered through Nova::script(). Nova 4 moved to Vue 3 and Nova 5 continued that; a compiled Vue 2 component often fails to render or throws in the console on modern Nova. Verify it renders before you build anything on top of it. If it doesn't, the fallback is a one-liner with no dependency:

Text::make('ID')
    ->displayUsing(fn ($id) => "<a href=\"https://billing.example.com/{$id}\" target=\"_blank\">{$id}</a>")
    ->asHtml()
    ->sortable(),

That gives you ~90% of the value with zero maintenance risk. The tradeoff: asHtml() output is escaped-then-untrusted HTML, so never interpolate user-controlled strings into the anchor text, and you lose any built-in click handling the package provides.

Searching and filtering. Because the field still resolves to the underlying id attribute, ->searchable() and ->sortable() should behave normally. But if you switch to the asHtml() fallback, Nova's search may match against the rendered HTML string rather than the raw ID — test search on the ID column after any change here.

Watch for N+1 in your URL closure. A closure like fn ($r) => $r->customer->portal_url lazy-loads a relation per row. Eager-load it via the resource's $with property:

public static $with = ['customer'];

This bites hardest on indexes with 25–50 rows and is easy to miss because Nova indexes feel instant until they don't.

Sorting the field name vs. the column. If you alias the field (IdLink::make('Order #', 'id')), double-check sort and search still target id and not the display label. Nova handles this correctly in most versions, but it's the first thing to check when sorting silently no-ops.

Don't let it leak into forms. A link-styled field in a create/edit form is confusing and, worse, may submit a value nobody intended to change. Default to ->exceptOnForms() in your subclass and opt in explicitly where needed.

Check the target behavior. If the package doesn't expose a target option, you're stuck opening in the same tab — annoying when the destination is a full external app. Either check the source for a settable meta property, or override the field's component/displayUsing. Opening external admin tools in a new tab is almost always what support staff want.

Pin the version and document it. With no releases since 2019 and zero dependents, this is effectively unmaintained. Add it to composer.json with a comment in your README noting the fallback (Text::make()->asHtml()) so the next developer can rip it out in five minutes if a Nova upgrade breaks it. Given how small the surface area is, budget for replacement rather than for patching.

If you hit a blank or invisible field, the problem is nearly always asset registration, not your code — check the browser console for a missing component name mismatch (id-link vs. the registered name in the package's nova.mix/service provider) and confirm php artisan nova:publish has run since install.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
cgv/localized-resource