resources/js/Components/DataTable/Index.vue

Package: TanStack Table

Introduction

comes with built-in DataTable module built upon TanStack Table.

Available features:

  • Data from query builder (Laravel Eloquent)
  • Row actions (CRUD and custom actions)
  • Sorting, pagination and search
  • Filtering
  • Render as badge and as status
  • Light and dark modes
  • Easy to customize
  • Bulk actions (coming soon)
  • Export to CSV and PDF (coming soon)

To generate DataTable, you should first render Vue file component using Inertia::render() from your Controller.

Properties data and columns are required, while filters prop is optional.

UserController.php
public function index(): Response
{
    return Inertia::render('Admin/Users/Index', [
        'data' => User::get(),
        'columns' => [
            Column::make('id'),
            Column::make('name'),
            Column::make('email'),
            ActionColumn::make(route: 'admin.users')->render(),
        ],
    ]);
}

Then in your file component, for example in Admin/Users/Index, define the props (using DataTableProps type) and render the DataTable component.

<script setup lang="ts">
import { DataTable as DataTableProps } from "@/types";

const props = defineProps<DataTableProps>();
</script>

<template>
    <DataTable v-bind="props" />
</template>

Columns

Go to app/Http/Controllers/Admin/UserController.php, or any similar Controller, and check out index method to see how columns are generated.

Columns are generated via Column::class, as seen in the code example above.
Each column should be generated using make method. All other methods are optional.

Create column

First prop is name/ID of the column and should be unique between columns.
Second prop is column’s title (table head) and it’s optional.
When title is not provided, it will be generated from name/ID.
. For example, auto-generated title for name column will be Name, for email will be Email, and so on.

Column::make('name', __('Full name'))

Disable sorting

Columns are sortable by default. Chain this method to disable sorting for the column.

Column::make('name')->disableSorting()

Disable hiding

Column visibility toggle is enabled by default. Visiblity toggle for this column will be disabled and column will be always visible.

Column::make('name')->disableHiding()

Status (true/false)

Render column as check icon for true and x icon for false.

Column::make('verified')->status()

Badge

Render column as badge.

Column::make('tags')->badge()

Thumbnail

Render column as thumbnail image.

Column::make('image')->thumbnail()

Class

Add class to the value cell.

If class is not generated, safelist it, since it’s dynamically generated and Tailwind can’t recognize such classes on its own.
Just in case, you can safelist all clasess you plan to use here.

You can safelist classes in tailwind.config.js under safelist array.

Column::make('name')->class('font-bold whitespace-nowrap')
Column::make('email')->class('truncate max-w-16')

Cell width

Set width of the cell in pixels to prevent auto-sizing or specific width.

Size equal to 1 will make cell’s width as minimum as possible.

Column::make('name')->size(1)

Center

Center table header and value.

Column::make('name')->centered()

Make column hidden

Useful when you need to filter by some value, but don’t want to show the column. For example, when Model is trashed (soft-deleted).
Since filtering is done via TanStack Table it should have access to the data.

Column::make('trashed')->hidden()

Customize

To update content rendering in DataTables go to file resources/js/lib/utils.ts and update function mapColumns as you wish.

You can check by accessorKey (which is mapped from column name/ID) for specific columns, or you can define new renderAs method for common columns.

Some of specifically rendered columns are id session_id thumb badge and status.

To update or add Column properties go to file app/Services/DataTable/Column.php.

Actions

Actions should be defined as the last item in columns array.
Actions are generated similar as columns, using chained methods.

Create action

First prop is column name and should be unique between actions.
Second prop is the action’s label, and it’s optional
When label is not provided, it will be generated from name. Label for show will be Show, for edit will be Edit, etc.

Action::make('show', __('Show order'))
    ->route('admin.orders.show')

Action route

Set the route name of the action. By default, routes should be GET and will use model ID as param.

Action::make('name')->route('admin.orders.show')

Method and params

Set the route method and params when method is different than GET and there is multiple params or different params than ID.

Action::make('resolve')
    ->route('admin.roadmap.status.update')
    ->method('patch')
    ->params([
        // placeholder to use prop from column (:id will use column id value, :name will use column name value, etc.)
        'post' => ':id',
        // extra param to update status of roadmap post in this specific case
        'status' => 'resolved',
    ])

Confirm dialog

Trigger confirmation dialog before submitting the action.

Default actions delete and restore are confirmable by default.

Action::make('impersonate', __('Login as user'))->confirm()

Conditional actions

Show action under specified condition.
This action will be available only when trashed column value is false.

Action::make('impersonate', __('Login as user'))->when('trashed', false)

Separate actions (in dropdown view)

Separate action with border in dropdown box.

Action::make('name')->separated()

Actions can be rendered as dropdown.

Dropdown actions are recommended when there are more than 3 actions.
Check the code below to see how to render dropdown actions.

Inline actions

Actions can also be rendered as inline, instead of dropdown.

Inline actions are recommended when there are less than 3 actions.
Check the code below to see how to render dropdown actions.

Default actions

Each DataTable comes with default actions which you can use along the custom ones or completely override. Default actions are show, edit, delete and restore (when Model can be soft-deleted).

If you need to update your default actions, you can do it here: app/Services/DataTable/ActionColumn.php in method private function getDefaults(): array.

Render as dropdown

make() method accepts two optional parameters: renderAs and route.
When renderAs is null it will be rendered as dropdown.
When route is null you should define custom actions.

You should set the route prefix for default actions. For example, if you are using resource controller, then the actions will be: admin.users.show, admin.users.edit, etc. and it’s recommended way to name your CRUD routes in this way even when not using resource controller.

ActionColumn::make(route: 'admin.users')->render()

Render as inline

You should set the route prefix for default actions. For example, if you are using resource controller, then the actions will be: admin.users.show, admin.users.edit, etc.

ActionColumn::make('inline', 'admin.users')->render()

Custom actions

To define custom actions, you should pass your actions in render() method of 1.

ActionColumn::make('inline')
    ->render(
        Action::make('show')
            ->route('admin.orders.show'),

        Action::make('edit')
            ->route('admin.orders.edit'),
    ),

Custom actions via slots

To define custom actions via Vue slot, you should define the type of actions: inline or dropdown, without the route prop.

Rendering actions via slots is useful when you want customized UI for your actions, for example to open dialogs/popups/sheets for editing, instead of dedicated pages.

// Dropdown
ActionColumn::make()->render(),

// Inline
ActionColumn::make('inline')->render(),

And then define your actions in Vue file.

<template v-slot:actions="{ row }">
    <Button type="button" variant="link" @click="() => console.log('Edit')">{{ $t("Edit") }}</Button>
    <Button type="button" variant="link" class="text-destructive" @click="() => console.log('Delete')">{{ $t("Delete") }}</Button>
</template>

app/Http/Controllers/Admin/FaqController.php is using such custom actions.
Slot example for custom FAQ actions can be found here: resources/js/Pages/Admin/Faq/Index.vue

Filters

Filter key must be equal to the column name/ID, so in this case you should have columns verified, blocked and trashed.

Create filter using make() method which accepts one prop for filter title.

'filters' => [
    'verified' => Filter::make(__('Email verified'))
        ->trueFalse(true),

    'blocked' => Filter::make(__('Blocked'))
        ->trueFalse(false),

    'trashed' => Filter::make(__('Trashed'))
        ->trueFalse(false),
],

Status (true/false)

Filter DataTable by true and false value.

// Without default fitering
Filter::make(__('Email verified'))->trueFalse()

// Filter by true on init
Filter::make(__('Email verified'))->trueFalse(true)

// Filter by false on init
Filter::make(__('Email verified'))->trueFalse(false)

Value

Filter DataTable by selected values.

Data of available filters should be Collection or array of arrays with value and label props.

$tags = Tag::get()->map(fn ($tag) => [
    'value' => $tag->slug,
    'label' => $tag->name,
]);

Filter::make(__('Tags'))->data($tags)

By default, all filters can be searched and filtered inside dedicated dropdown.

To disable search, use this method. Disabling search is recommended when there are few filters so search would be redundant.

Filter::make(__('Tags'))->data($tags)->disableSearch()

Pre-selected filters

Similar as with true/false, you can set default filters.

Default value can be an array, string or boolean.

// This example is taken from Helpdesk to filter tickets by currently logged in admin and all unassigned tickets
Filter::make(__('Admins'))->data($admins)->default([auth()->user()->name, __('Unassigned')])

Docs coming soon…

Pagination

Each datatable comes with default pagination which shows 10 rows per page and you can select to show 10, 20 , 30, 40, 50 or 100 rows at once when viewing the data.

To change global defaults go to resources/js/Components/DataTable/Index.vue file and update default values for rowsPerPage and pageSize props.

To update defaults for specific datatable you can pass those props via controller:

return Inertia::render('SomePageWithDataTable', [
    // other props here
    'rowsPerPage' => [5, 10, 20],
    'pageSize' => 5,
]);

This will set select options to 5, 10 and 20, and 5 rows per page by default.

You can find sample code in AdminTestimonialController.php controller file.