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

Slick Data Tables Laravel Package

itul/slick-data-tables

A Laravel package for building slick, interactive data tables with server-side processing, search, sorting, and pagination. Render responsive tables from Eloquent models or query builders with minimal configuration and clean, customizable markup.

View on GitHub
Deep Wiki
Context7

Getting Started

Because this package lives on Bitbucket, has no Packagist presence to speak of, and has zero dependents, treat onboarding as a source audit first, integration second.

1. Add the repository

{
  "repositories": [
    {
      "type": "vcs",
      "url": "[email protected]:bmooreitul/slickdatatables.git"
    }
  ],
  "require": {
    "itul/slick-data-tables": "dev-master"
  }
}

You will likely need SSH keys or Bitbucket app-password credentials configured on CI, since there is no public dist archive to fall back on.

2. Read these files before writing any code

  • composer.json — check the illuminate/* constraints. This is the single biggest compatibility signal; if it caps at Laravel 8/9 you already know the answer for a modern app.
  • src/ — look for a ServiceProvider, a facade, and any DataTable/Builder/Column classes. The public surface is usually tiny.
  • config/*.php — default options (per-page, search mode, etc.).
  • tests/ — if absent, assume nothing is verified.

3. Publish and register

php artisan vendor:publish --provider="Itul\\SlickDataTables\\SlickDataTablesServiceProvider"
php artisan config:clear

If auto-discovery is not declared in composer.json, register the provider manually in config/app.php.

4. First use case — one AJAX endpoint, one screen

Start with the smallest possible vertical slice: a users list with server-side processing.

// routes/web.php
Route::get('/users/data', [UserDataController::class, 'data'])->name('users.data');

// app/Http/Controllers/UserDataController.php
public function data()
{
    return datatable(User::query())   // or the package's equivalent entry point
        ->addColumn('actions', fn ($u) => view('users.actions', ['user' => $u])->render())
        ->rawColumns(['actions'])
        ->toJson();
}
<table id="users-table" class="display">
  <thead><tr><th>ID</th><th>Name</th><th>Email</th><th></th></tr></thead>
</table>

<script>
$('#users-table').DataTable({
    processing: true,
    serverSide: true,
    ajax: '{{ route('users.data') }}',
    columns: [
        { data: 'id' }, { data: 'name' }, { data: 'email' },
        { data: 'actions', orderable: false, searchable: false },
    ],
});
</script>

If that renders and paginates, you have validated the package's core loop and can safely expand. If it does not, you have lost an afternoon rather than a sprint.

Implementation Patterns

Thin controller, fat query object. Keep the controller to a single return statement and push filters into a dedicated query class or scoped Eloquent builder. Slick-style packages usually accept any Builder/EloquentBuilder, so the query object stays testable independently of the DataTable layer.

public function data(UserIndexQuery $query)
{
    return datatable($query->builder())
        ->editColumn('created_at', fn ($u) => $u->created_at->toIso8601String())
        ->toJson();
}

Column whitelisting, always. Server-side processing means the client sends columns[i][data], search[value], order[0][column]. Never interpolate those into SQL. Most packages map them internally, but confirm by grepping the source for orderBy( and where( to see whether the column name is validated against the declared column set.

A base resource controller. If you have more than two or three tables, define an abstract DataTableController with protected array $columns, protected array $searchable, protected array $orderable, and a resolveQuery() hook. This gives consistent behaviour across screens and a single place to patch if the package misbehaves.

Eager loading is part of the pattern, not an optimisation. Any column derived from a relation must be loaded up front:

User::with(['team:id,name', 'roles:id,name'])

Blade component for the table shell. Wrap the <table> markup and JS init in <x-data-table :id :route :columns>. It keeps the DataTables JS config out of every view and makes column changes a one-line diff.

Feature-test the JSON contract. Hit the data route and assert structure, not values:

$this->getJson(route('users.data'))
     ->assertOk()
     ->assertJsonStructure(['data' => [['id', 'name', 'email']], 'draw', 'recordsTotal', 'recordsFiltered']);

This is the cheapest insurance against a package update silently changing the envelope shape.

Migration path. Because this package is effectively unsupported, structure your code so the DataTable call sits behind one interface (DataTableResponder) with a Slick implementation and, if needed later, a yajra/laravel-datatables implementation. Swapping the dependency then becomes a one-class change rather than a grep-and-replace across dozens of controllers.

Gotchas and Tips

The dependency itself is the first gotcha. Zero stars, zero dependents, Bitbucket-only, no public issue tracker. That means no security advisories, no CVE feed, no upgrade guides. Pin the commit hash ("dev-master#abc1234") rather than floating on dev-master, or a force-push to that repo will change your production code without warning. Vendor a copy into packages/ with a path repository if you need long-term stability.

No Packagist metadata. Expect Composer to resolve this over VCS every time; there is no dist zip, so CI installs are slower and depend on Bitbucket availability and credentials. Mirror it to your own Git host if CI flakiness becomes a problem.

config:cache and unpublished config. If the package registers a config file with mergeConfigFrom, a published copy in config/ will silently override defaults — and a stale bootstrap/cache/config.php will override both. Whenever behaviour looks inexplicable after a deploy, run php artisan config:clear before debugging anything else.

The count query is the usual performance cliff. recordsTotal and recordsFiltered run separate COUNT(*) queries. On large tables with joins this dominates response time. Check for indexes on every column in WHERE, ORDER BY, and any join key; if the package runs count() on the un-filtered base query, consider caching that number per model.

Search across relations needs explicit handling. A naive WHERE name LIKE ? will not reach team.name. Most packages offer a nested-column syntax (team.name) but it often generates an implicit join or fails silently. Verify with a query log:

DB::listen(fn ($q) => Log::debug($q->sql, $q->bindings));

rawColumns is an XSS footgun. Anything listed there bypasses escaping. Only mark columns raw when the value is generated from Blade (already escaped) or from trusted markup — never from user input.

Date and timezone drift. Server sends UTC, DataTables renders as a string. Either format in the editColumn callback or use a render function client-side, but pick one. Mixed approaches produce timestamps that are correct in one timezone and wrong in another.

Ordering on derived columns. addColumn values do not exist in SQL, so ORDER BY actions will error or be ignored. Mark them orderable: false in the JS config and, if sorting is genuinely needed, use orderColumn/->orderColumn('total', ...) with a subquery expression.

CSRF on POST-based AJAX. If you configure the table with type: 'POST', remember the X-CSRF-TOKEN header or DataTables will receive a 419 and render an empty table with no console error. Prefer GET for read-only tables.

Extension points worth knowing. Look for overridable methods on the builder — typically filterColumn, globalSearch, prepareQuery, and a before/after hook. These are where you add tenant scoping or soft-delete handling without forking. If the source exposes none, subclassing is your only lever, which is itself a reason to wrap it behind your own interface.

Debugging an empty table. Work outward in this order: hit the data route directly in the browser (is it valid JSON?), check the Laravel log for exceptions swallowed by the AJAX handler, verify the columns[].data keys exactly match the JSON keys (case-sensitive), then confirm Content-Type: application/json. DataTables fails silently on a malformed envelope, so the network tab is more informative than the page.

Upgrade risk assessment. Before investing further, diff the package against yajra/laravel-datatables, which is the de facto standard with active maintenance. If Slick's API is a thin wrapper, the migration cost is low and the long-term payoff is substantial; if it adds genuinely unique behaviour, document that behaviour in your own tests so you are not locked into an unmaintained implementation for something you could reproduce in fifty lines.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package