digital-creative/nova-data-table
A Laravel Nova package that adds a DataTable component for displaying model records in sortable, searchable, paginated tables inside Nova. Configure columns, filters, and actions to build rich tabular views without writing custom Vue code.
1. Confirm compatibility before you composer require. The last tagged release is from December 2019, which places it in the Nova 2.x / Nova 3.x, Vue 2, PHP 7.x era. Open the repo and check composer.json for the laravel/nova constraint and the package.json for the Vue version. If you are on Nova 4+ (Vue 3 / Tailwind 2+), assume it will not work out of the box and budget time for a fork.
2. Install and register.
composer require digital-creative/nova-data-table
Then look at the package's service provider (typically src/NovaDataTableServiceProvider.php) to see whether it registers a Card, a Tool, or a Field. That distinction drives everything else:
NovaServiceProvider::cards()NovaServiceProvider::tools() and it appears in the sidebarfields() method3. Publish assets.
php artisan vendor:publish --provider="DigitalCreative\NovaDataTable\NovaDataTableServiceProvider"
php artisan nova:publish
The nova:publish step is easy to forget and is the cause of most "component renders blank" reports.
4. Look here first, in this order:
| Location | What you're looking for |
|---|---|
README.md |
The one usage example — copy it verbatim before improvising |
src/*.php |
The class extending Card/Tool/Field, its constructor and public setters |
resources/js/components/*.vue |
The props array — this is the real API contract |
dist/js/*.js |
Whether compiled assets shipped (if yes, you may never need npm) |
config/ |
Any publishable config and its defaults |
First use case: drop a read-only reporting table onto the Nova dashboard — e.g. "latest signups," "open invoices by status," or a KPI breakdown — so admins stop exporting CSVs to answer routine questions. This plays to the component's strength (presentation) without forcing it to do complex CRUD, which it was not built for in 2019.
Dashboard card with server-supplied rows. The most reliable pattern: build the data in PHP, hand it to the component as a plain array. This keeps queries, authorization, and formatting testable in Laravel rather than in Vue.
// NovaServiceProvider::cards()
(new DataTable())
->title('Recent Orders')
->columns([
['key' => 'reference', 'label' => 'Ref'],
['key' => 'customer', 'label' => 'Customer'],
['key' => 'total', 'label' => 'Total'],
])
->rows(Order::latest()->take(20)->get()->map(fn ($o) => [
'reference' => $o->reference,
'customer' => $o->customer->name,
'total' => '$'.number_format($o->total, 2),
])->all()),
Confirm the exact setter names against the source — but the shape (fluent setters called from cards()) is the convention across dcasia Nova packages.
Extract a base card. If you end up with more than two tables, create App\Nova\Cards\BaseDataTable that sets shared columns, styling, and a ->canSee() guard, then subclass per dashboard. This is the single highest-leverage refactor with an unmaintained package: it gives you one place to patch when Nova upgrades break the component.
Authorize at the data layer, not the UI. Wrap every query in a policy check or Gate::allows() inside the card before returning rows. Do not rely on hiding the card — the compiled JS and any endpoint URL remain reachable.
public function authorizedToSee($request)
{
return $request->user()->can('viewAny', Order::class);
}
If there is an AJAX/endpoint mode, keep the endpoint in your app (a small invokable controller) rather than in the vendor package. Route it under Nova's middleware so authentication and Nova's authorization are applied automatically:
Route::middleware(['nova'])->get('/nova-api/reports/orders', OrdersReportController::class);
Pair with Nova filters/metrics rather than reinventing them. Use Nova's built-in filters for date ranges and have the card read request()->query() — you get consistent UX and avoid re-implementing filter UI inside a table component that has no filter support.
Testing. Feature-test the card class by asserting the rows array it produces (easy, no browser needed). Only reach for Dusk if the Vue component itself is doing client-side sorting or pagination you depend on.
Treat it as unmaintained. One star, zero dependents, and no release since 2019 is a strong signal. Fork it into packages/ or vendor the class into your app on day one, so a Nova upgrade doesn't strand you. Do not pin dev-master in production without committing the lockfile.
Nova 4/5 is the cliff. Nova 4 moved to Vue 3 and a new asset pipeline. Prebuilt dist/ bundles from 2019 will fail to register or render under Nova 4. If you must use it there, plan to rebuild the Vue component yourself against Nova 4's tooling (nova:tool scaffolding) and treat the PHP class as reference only.
The compiled-asset trap. If dist/ is shipped and committed, editing resources/js does nothing until you rebuild. There is usually no package.json build configured for consumers, so run npm install && npm run prod inside the package directory — or copy the .vue file into your app and register it as your own component. The latter is often faster.
nova:publish in deploy scripts. Add it to your deploy pipeline after composer install. Missing assets after deploy is the #1 cause of a blank card, and php artisan optimize:clear will not fix it. Symptom in the browser console: a failed request for /nova-vendor/.../*.js or a Vue.component not found error.
Large datasets will hurt. There is no evidence of server-side pagination in a 2019 table component. Passing Model::all() into rows() serializes the entire result into the card's JSON payload on every dashboard load. Always ->take(50) or equivalent, and prefer aggregate queries over row-level detail.
Don't hand it Eloquent models. Pass arrays of scalars. Serializing models leaks every column (including ones excluded by $hidden at your peril) and breaks as soon as a relationship is lazy-loaded in a card context where with() was forgotten.
Escape before you render. If the Vue template uses v-html for any cell, sanitize user-controlled values server-side. Older table components frequently do.
Debugging order:
php artisan nova:publish then hard-refreshphp artisan route:list | grep nova-vendor to confirm the endpoint existsjsonSerialize() output to verify the prop shape matches the .vue props arrayNovaServiceProvider registration — a card registered in tools() renders nowhereExtension points worth knowing: subclass the card class, override component() to point at your own Vue component, publish views/config if available, and use ->canSee() / ->onlyOnDetail() for placement. If the package exposes no setter you need, override jsonSerialize() in a subclass rather than forking — it's the cleanest seam and survives package updates.
Have an exit plan. If the table is business-critical, evaluate actively maintained Nova table packages or build a plain Nova Metric/custom resource index instead. The cost of migrating off a dead dependency mid-Nova-upgrade is usually higher than the cost of not adopting it.
How can I help you explore Laravel packages today?