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

ofcold/nova-sortable

ofcold/nova-sortable adds drag-and-drop row sorting to Laravel Nova resource index tables. Reorder records inline, persist positions automatically, and keep admin lists organized with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and pin the version (the package is archived, so pinning protects future composer installs):

composer require ofcold/nova-sortable

Since ofcold/nova-sortable was last released in October 2019 and is archived, validate it against your Nova version before building on it. Skim the repo's README.md, src/ and the migration stub it ships first — the public surface is small, so ten minutes there will save you guessing. Anything below is the pattern common to packages in this family; confirm the exact trait namespace and field class names against the source.

Add the ordering column to the table you want to sort:

Schema::table('categories', function (Blueprint $table) {
    $table->unsignedInteger('sort_order')->default(0)->index();
});

Add the trait to your model:

use Ofcold\NovaSortable\HasSortable; // verify namespace in src/

class Category extends Model
{
    use HasSortable;

    protected $sortColumn = 'sort_order';
}

Register the field on the resource so the index page renders drag handles:

public function fields(NovaRequest $request)
{
    return [
        ID::make()->sortable(),
        Text::make('Name'),
        Sortable::make('Order'),
    ];
}

First use case: a flat, admin-managed list whose order matters — categories, menu items, FAQs, testimonials, banner slots. Load the index, drag a row, refresh the page and confirm the new order persisted.

Implementation Patterns

Flat list. The default flow — one model, one sort column, no scoping. Seed existing rows with sequential values in migration or a seeder so the first render isn't a wall of 0s (the drag library needs a stable starting order).

Grouped / parent-scoped sorting. When items live inside a parent (sections within a page, items within a category), keep separate ordering per parent by adding a group column to the model and a where clause. Verify the package exposes a hook for this (a groupBy / sortableGroupBy style property or trait method); if not, wrap the reorder call yourself and skip the built-in field entirely.

New records. Decide where new rows land — end (max(sort_order) + 1) or top. Wire this into a creating observer or boot method on the trait so a new FAQ doesn't jump to the top of the admin list.

Fallback ordering. Always keep a deterministic secondary sort on the resource (->orderBy('sort_order')->orderBy('id')) so a partially-updated table still lists predictably and so any list view that ignores the trait doesn't look random.

Related tables. The package works on the Nova index. Inside a HasMany detail view or a BelongsToMany pivot table you won't get drag handles — either fall back to a number field and manual ordering, or promote the relationship to its own resource so the sortable index applies.

Combine with front-end ordering. Expose the column on your site's queries (orderBy('sort_order')), and invalidate any cache key for the list whenever a reorder occurs. Because Nova writes go through the package, a model observer on saved/updated on the sort column is the cleanest invalidation point.

Gotchas and Tips

  • Archived ≠ broken, but frozen. No Nova 4/5 support, no PHP 8.x guarantees. If you're on modern Nova, expect to fork it; the code is MIT and tiny, so vendor-patching is cheap.
  • 0 defaults on every row. A fresh migration leaves all rows at 0, which makes the first drag reorder seemingly do nothing. Backfill with a seeder (DB::statement with a ROW_NUMBER() or a loop) before shipping.
  • Nova's own sortable() is different. Text::make('Name')->sortable() gives column-header sort, not drag sort. Don't confuse the two; they coexist but only the package's field/drag layer persists reorder.
  • Mass-reorder queries. The package likely writes one row per drag. If you ever reorder programmatically, wrap the loop in DB::transaction() and fire a single updated event, otherwise your observers/logs explode.
  • Concurrency. Two admins sorting the same list can collide and interleave values. For low-traffic CMS use this is fine; for anything busier, add a version column or re-read the max before assignment.
  • Global scope interactions. Soft-deleted or tenant-scoped rows still occupy sort_order slots; either scope the field the same way or accept gaps (usually fine, since ordering is relative not absolute).
  • Debugging drag failures. If handles render but the drop doesn't persist, check: the route is registered (php artisan route:list | grep -i sort), CSRF is satisfied by Nova's axios wrapper, and the model actually uses the trait — a missing trait silently rejects the write.
  • Extension points you care about: the sort column name property, any group/parent property, and the model's boot hooks. Those are the three places to override for non-toy cases. If the release you pin doesn't expose them, fork into app/ rather than fighting the package internals.

Given the stars, age, and archive status, treat this as a short-term accelerator, not a dependency. It saves half a day on a category-sorting screen; budget a day to fork and maintain it if the feature is load-bearing.

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
microsoft/fluentui-system-icons