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

Laravel Datatables Ui Laravel Package

yajra/laravel-datatables-ui

yajra/laravel-datatables-ui provides ready-to-use UI components for Laravel DataTables, making it easier to build searchable, sortable tables with Blade views and minimal configuration. It integrates with Yajra DataTables to speed up admin and dashboard interfaces.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites, in order. This package is a presentation layer only — installing it before the server side works guarantees a confusing first hour. Confirm three things first:

  1. yajra/laravel-datatables (the parent) is installed and can return JSON from a route. A working DataTables::of(User::query())->toJson() response is the base you build on.
  2. Your Laravel/PHP versions satisfy the package's composer.json constraints — this project is small and lightly maintained, so check the constraint string directly rather than assuming composer require will resolve.
  3. You know which jQuery DataTables version your app actually serves (1.x vs 2.x). The preset's bundled assets may not match; note the discrepancy now, not during debugging.

Install and publish.

composer require yajra/laravel-datatables-ui
php artisan vendor:publish   # interactive — pick this package, note the tags it offers

Run vendor:publish interactively the first time so you can see exactly which tags exist (typically views, assets, sometimes config). Publish once, then git add the output. From that moment the files are yours; the Composer requirement is scaffolding you can drop before merge.

Where to look first, in this order:

  • The published Blade view — this is the whole deliverable. Read it end to end; it is short and it defines the markup contract (table id, wrapper classes, where the @push('scripts') block sits).
  • The published JS/CSS assets — note the DataTables core version they were built against.
  • Any published config — usually a handful of layout/column keys, nothing load-bearing.
  • The parent package's docs, not this one. Column definitions, filtering and rawColumns all belong to yajra/laravel-datatables; this package only draws the table around them.

First use case: one read-only list screen. Take your simplest admin table — users, orders, anything with a plain index. Wire it as three pieces:

Route + controller

Route::get('/admin/users', [UserController::class, 'index'])->name('admin.users.index');
Route::get('/admin/users/data', [UserController::class, 'data'])->name('admin.users.data');

public function data()
{
    return DataTables::of(User::query())
        ->addColumn('actions', fn ($u) => view('admin.users.actions', ['user' => $u])->render())
        ->rawColumns(['actions'])
        ->toJson();
}

Blade — extend the published view (or copy its markup into your own layout):

@extends('layouts.admin')

@section('content')
  @include('vendor.datatables.table', ['id' => 'users-table', 'ajax' => route('admin.users.data')])
@endsection

JS — initialise with serverSide: true, and keep the client columns array index-aligned with the server side:

$('#users-table').DataTable({
  processing: true,
  serverSide: true,
  ajax: { url: "{{ route('admin.users.data') }}", type: 'GET' },
  columns: [
    { data: 'id',        name: 'id' },
    { data: 'name',      name: 'name' },
    { data: 'email',     name: 'email' },
    { data: 'actions',   name: 'actions', orderable: false, searchable: false },
  ],
});

If the table renders with a spinner that never resolves, the problem is almost always in that triple: route 404/500, CSRF on POST, or a column-count mismatch. Everything else in this package is cosmetic.

Implementation Patterns

Treat the published view as a template, not a base class. The value is the markup and wiring; the moment you need a second table you will want your own wrapper. Copy the published Blade into resources/views/components/datatable.blade.php and make it a Blade component with $id, $ajaxUrl, $columns and $options props. From then on, screens are one-liners and the preset disappears from your mental model.

Generate both column lists from one source of truth. The most common long-term bug in DataTables integrations is client/server column drift. Define columns once in PHP:

// config/tables/users.php  (or a static method on the controller)
return [
  ['data' => 'id',      'name' => 'id',      'title' => 'ID'],
  ['data' => 'name',    'name' => 'name',    'title' => 'Name', 'searchable' => true],
  ['data' => 'actions', 'name' => 'actions', 'title' => '', 'orderable' => false, 'searchable' => false],
];

Feed the same array to DataTables::of()->columns(...) server-side and @json($columns) in the script block. orderable/searchable flags then cannot diverge, and adding a column is a one-line change.

Asset wiring: move the published assets into your build. Serving from public/vendor/... bypasses your manifest, caching and SRI story. Copy the published JS/CSS into resources/js / resources/css, import them from your entrypoint, and let Vite/Mix hash and version them. Pin the DataTables core version explicitly in package.json at the same time — do not let two copies of DataTables exist on the page.

Row actions. Render action buttons with editColumn/addColumn + rawColumns (as above) and mark the column non-orderable and non-searchable on both sides. Prefer route() inside a small partial so the buttons carry policy checks (@can) — the preset will not help here and you do not want authorisation logic in a JS string.

Filters and search. Put the filter form above the table and funnel everything through a single ajax.data callback so state has one serialisation path:

ajax: {
  url: "{{ route('admin.users.data') }}",
  data: d => { d.status = $('#filter-status').val(); d.role = $('#filter-role').val(); }
}

On the server, implement custom filters with filterColumn() rather than hand-rolling where() clauses around the query builder — that is where yajra/laravel-datatables expects them and where global search and column search both route through.

Exports. Client-side buttons are cheap but only export the current page under serverSide: true. Real exports need the sibling yajra/laravel-datatables-buttons package and a server-side export route. Decide this per screen; do not bolt it on later as an afterthought, because the button config lives inside the same initialiser you just standardised.

Integration tips.

  • Extend the published view through @section/@push before you fork it — the layout is usually @extends-friendly, and staying in a child view keeps the diff small.
  • If you use Tailwind, keep the preset's table classes but move colour/border decisions into your own CSS layer; editing the vendor markup for styling is a losing battle.
  • Test the contract, not the markup: an HTTP test asserting draw, recordsTotal, recordsFiltered and a data array catches 90% of regressions when you refactor the view.
  • For i18n/RTL, pass a language object/JSON URL in the initialiser rather than editing published views — DataTables handles the labels, your layout handles direction.

Gotchas and Tips

vendor:publish overwrites your edits. Every subsequent run of the same tag replaces customized views without warning (unless you omit --force, in which case it silently skips — equally surprising). Since the whole point of this package is "publish once, own it," inline the output into your own namespaces before you start editing, and drop the Composer requirement in the same PR. If you keep the dependency, document loudly that re-publishing is forbidden.

Version drift is the failure mode you will actually hit. The bundle contains front-end assets built against a specific jQuery DataTables version. If your app serves a different major version, symptoms are subtle: sorting/paging silently no-ops, dom doesn't work (renamed to layout in DataTables 2.x), Ajax parameter names change. Reconcile and pin the version in your manifest as an explicit exit criterion of the pilot, not a "later" item.

"No data available" with a 200-free network tab. The server returned an HTML page — a login redirect (session expired), a 419 (missing CSRF on POST), or a 500 with app.debug off. Check the raw response in the Network tab and hit the data route directly in the browser; a JSON {message: ...} response from the parent package's exception handler is your stack trace.

CSRF on POST. The preset may initialise with type: 'POST'. Add the header or switch to GET:

headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content }

Column count mismatch. Fewer columns in JS than the server returns shifts every cell by one — IDs appear in the name column, buttons render as text. It looks like a rendering bug but it is a definition bug. Assert the counts match in a test.

HTML escaped in cells. Any column emitting markup needs rawColumns(['actions', ...]) server-side. Forgetting it produces literal <a href=...> text in the cell — the classic first-hour mistake.

Query performance, not front-end performance. The preset adds no runtime cost. The cost is in your controller: N+1s from addColumn closures touching relations without eager loading, missing indexes on the columns you marked searchable or orderable, and LIKE '%term%' global search over large tables. ->with() your relations, index the order columns, and consider restricting global search to a handful of columns via ->filterColumn or searchable(false).

Dates and nulls. Dates need editColumn + Carbon formatting, and nulls_last/search.case_insensitive live in the parent package's published config/dataTables.php — that is your extension point for behavioural tweaks, not the UI package.

Debugging routine that works. Reproduce the data route in isolation (curl it or open it directly), confirm the JSON shape (draw, recordsTotal, recordsFiltered, data), then confirm the JS initialiser matches it column for column. Console errors like "Cannot read properties of undefined (reading 'length')" or "Incorrect column count" are the client telling you the shape is wrong — trust it over the rendered table.

Extension points, ranked. (1) Your own Blade component wrapping the published markup — covers 95% of needs. (2) Overriding views via resources/views/vendor/<namespace> if you keep the dependency. (3) The parent package's config file for search/null-sorting behaviour. (4) Forking the published assets for DataTables version bumps — acceptable precisely because the surface is tiny.

Treat upstream as frozen. With ~10 stars and no dependents, expect no fixes for anything you find. That is fine if you inlined; it is a latent upgrade blocker if you didn't. Never let a dependency on this package gate a Laravel major upgrade.

Accessibility and RTL are not included. If the screen is customer-facing or must be WCAG-audited, budget to rebuild the table shell — the preset gives you a starting point, not a compliant one. Add caption, scope="col" on headers and a visible label for the search box as a minimum.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib