mtrdesign/krait
Krait is a Laravel package from mtrdesign that provides lightweight, framework-native utilities and reusable components to reduce boilerplate and streamline common backend development tasks, helping teams build PHP applications faster with a simple, Laravel-friendly API.
Krait is a small, opinionated package that pairs a Laravel endpoint with a Vue table component, so the fastest path is to get one server-side table rendering before touching styling or filters.
Install both halves. Krait ships a Composer package and front-end assets/component. Expect something like:
composer require mtrdesign/krait
npm install krait # or the published asset path documented in the README
Publish config/assets if offered.
php artisan vendor:publish --provider="Mtrdesign\Krait\KraitServiceProvider"
Check config/krait.php (or similar) for route prefix, middleware, and default page length.
Read these first, in order: the README usage example, the Vue component's props, and the backend request handler class in src/. The package is small (10 stars), so the source is the real documentation — confirm exact facade/method names there rather than trusting blog posts.
Build one endpoint + one component. A minimal working slice:
// routes/web.php
Route::get('/users/table', [UserTableController::class, 'data'])
->middleware('auth');
// app/Http/Controllers/UserTableController.php
public function data()
{
return Krait::table(User::query()) // verify facade name in src/
->columns([
['data' => 'id', 'name' => 'id', 'orderable' => true],
['data' => 'name', 'name' => 'name'],
['data' => 'email', 'name' => 'email'],
]);
}
<!-- resources/js/pages/users/Index.vue -->
<template>
<krait-table url="/users/table" :columns="columns" />
</template>
First real use case: an admin listing page (users, orders, logs) with server-side pagination, global search, and sortable columns. That is exactly the workload Krait targets — if your table fits in memory, plain Vue + v-for is simpler.
Verify the response shape early: DataTables clients expect { draw, recordsTotal, recordsFiltered, data }. If the endpoint returns that, the component will work; if not, inspect src/ for the serializer.
One controller method per table. Keep query construction in the controller (or a dedicated UserTable class) and treat the Krait call as the boundary between HTTP and SQL. This makes it easy to reuse the same query for CSV export later.
Whitelist columns explicitly. Define columns as a literal array, and use name for the database column while data is the JSON key. Never accept order[0][column] or search values and splice them into SQL yourself — let the package map indices back to your declared list.
Scope before you hand off. Authorization and tenancy belong in the query, not the Vue side:
$query = Order::query()->where('tenant_id', auth()->user()->tenant_id);
return Krait::table($query)->columns($this->columns());
Eager load anything rendered. If a column reads user.name or an accessor that touches a relation, add ->with('user') to the base query. DataTables will request 10–50 rows per page, and an N+1 there is immediately visible in query logs.
Custom cells via scoped slots. Use the component's slot API for action buttons, status badges, and links:
<krait-table url="/orders/table" :columns="columns">
<template #actions="{ row }">
<a :href="`/orders/${row.id}`">View</a>
</template>
</krait-table>
For backend-rendered HTML, keep a tight allowlist of tags; raw HTML columns plus unescaped user input is the classic DataTables XSS bug.
Extra filters as query params. Pass filter state from Vue through the component's ajax/params option (or an axios interceptor), then read it in the controller with $request->input('status') and apply ->when(...). This keeps filtering server-side and survives pagination.
Wrap the component once. Create AppDataTable.vue that sets your defaults — page length, language strings, loading state, error toast on failed request — and use that everywhere. When Krait is upgraded or replaced, you change one file.
Reuse the definition for exports. Extract columns() and baseQuery() into a small class; the Krait controller and a CSV/Excel endpoint then share one source of truth.
composer.json, read the changelog before upgrading, and budget for the possibility of forking. Vendor the Vue component if you need stability.vue version and with Vite vs Laravel Mix before wiring it into a real page — asset publishing is the most common first failure.get() instead of a query, you lose pagination and search. The whole point is that Krait runs count(), offset(), and limit() for you.full_name but has no name mapping will silently fall back or error on sort.LIKE against the base table won't match joined data unless you add the join or a custom filter.POST, include the CSRF token in the ajax headers. A 419 in the network tab is the usual culprit when the table stays empty.draw, recordsTotal, recordsFiltered, and data. If recordsFiltered is missing, pagination controls will misbehave even though rows render.DB::listen, Telescope, or ->toSql() on the base query) to catch missing where clauses from tenant scoping and to spot N+1s.vendor/; if you need behavior changes, subclass the controller/table class or fork the component./users/table with a draw/start/length payload catches authorization leaks and response-shape regressions far faster than clicking through the table.How can I help you explore Laravel packages today?