Table

Tabulated data are sometimes needed, it's even better when it's responsive

To show a data table is very simple, you just need a data and a columns prop.

ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female
<template>
    <b-table :data="data" :columns="columns"></b-table>
</template>

<script>
    export default {
        data() {
            return {
                data: [
                    { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                    { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                    { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                    { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                    { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
                ],
                columns: [
                    {
                        field: 'id',
                        label: 'ID',
                        width: '40',
                        numeric: true
                    },
                    {
                        field: 'first_name',
                        label: 'First Name',
                    },
                    {
                        field: 'last_name',
                        label: 'Last Name',
                    },
                    {
                        field: 'date',
                        label: 'Date',
                        centered: true
                    },
                    {
                        field: 'gender',
                        label: 'Gender',
                    }
                ]
            }
        }
    }
</script>

# Sandbox with custom template

To show a custom row template, you should not use the columns prop, but add it within the scoped slot as a component.

Also you can add an empty named slot to show when there's no data.

ID
First Name
Last Name
Date
Gender
1JesseSimmons10/15/2016 Male
2JohnJacobs12/15/2016 Male
3TinaGilbert4/26/2016 Female
4ClarenceFlores4/10/2016 Male
5AnneLee12/6/2016 Female
Total 2 Females, 3 Males
<template>
    <section>
        <b-field grouped group-multiline>
            <div class="control">
                <b-switch v-model="isBordered">Bordered</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="isStriped">Striped</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="isNarrowed">Narrowed</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="isHoverable">Hoverable</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="isFocusable">Focusable</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="isLoading">Loading state</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="isEmpty">Empty</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="hasMobileCards">Mobile cards <small>(collapsed rows)</small></b-switch>
            </div>
        </b-field>

        <b-table
            :data="isEmpty ? [] : data"
            :bordered="isBordered"
            :striped="isStriped"
            :narrowed="isNarrowed"
            :hoverable="isHoverable"
            :loading="isLoading"
            :focusable="isFocusable"
            :mobile-cards="hasMobileCards">

            <b-table-column field="id" label="ID" width="40" :td-attrs="columnTdAttrs" numeric v-slot="props">
                {{ props.row.id }}
            </b-table-column>

            <b-table-column field="first_name" label="First Name" :td-attrs="columnTdAttrs" v-slot="props">
                {{ props.row.first_name }}
            </b-table-column>

            <b-table-column field="last_name" label="Last Name" :td-attrs="columnTdAttrs" v-slot="props">
                {{ props.row.last_name }}
            </b-table-column>

            <b-table-column field="date" label="Date" :th-attrs="dateThAttrs" :td-attrs="columnTdAttrs" centered v-slot="props">
                <span class="tag is-success">
                    {{ new Date(props.row.date).toLocaleDateString() }}
                </span>
            </b-table-column>

            <b-table-column label="Gender" :td-attrs="columnTdAttrs" v-slot="props">
                <span>
                    <b-icon
                        v-if="props.row.id !== 'Total'"
                        pack="fas"
                        :icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
                    </b-icon>
                    {{ props.row.gender }}
                </span>
            </b-table-column>

            <template #empty>
                <div class="has-text-centered">No records</div>
            </template>

        </b-table>
    </section>
</template>

<script>
    export default {
        data() {
            const data = [
                { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016/10/15 13:43:27', 'gender': 'Male' },
                { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016/12/15 06:00:53', 'gender': 'Male' },
                { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016/04/26 06:26:28', 'gender': 'Female' },
                { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016/04/10 10:28:46', 'gender': 'Male' },
                { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016/12/06 14:38:38', 'gender': 'Female' },
                { 'id': 'Total', 'gender': '2 Females, 3 Males' },
            ]

            return {
                data,
                isEmpty: false,
                isBordered: false,
                isStriped: false,
                isNarrowed: false,
                isHoverable: false,
                isFocusable: false,
                isLoading: false,
                hasMobileCards: true
            }
        },
        methods: {
            dateThAttrs(column) {
                return column.label === 'Date' ? {
                    title: 'This title is sponsored by "th-attrs" prop',
                    class: 'has-text-success'
                } : null
            },
            columnTdAttrs(row, column) {
                if (row.id === 'Total') {
                    if (column.label === 'ID') {
                        return {
                            colspan: 4,
                            class: 'has-text-weight-bold',
                            style: {
                                'text-align': 'left !important'
                            }
                        }
                    } else if (column.label === 'Gender') {
                        return {
                            class: 'has-text-weight-semibold'
                        }
                    } else {
                        return {
                            style: {display: 'none'}
                        }
                    }
                }
                return null
            }
        }
    }
</script>

# Selection

You can show a single selected row by passing the corresponding object to the selected prop. Additionally, adding the v-model modifier will make it two-way binding — selected object will mutate if user clicks on row.

Use focusable prop make table to be focused and navigable with a keyboard using arrow keys.

ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female
<template>
    <section>
        <b-field>
            <b-button
                label="Clear selected"
                type="is-danger"
                icon-left="close"
                :disabled="!selected"
                @click="selected = null" />
        </b-field>

        <b-tabs>
            <b-tab-item label="Table">
                <b-table
                    :data="data"
                    :columns="columns"
                    v-model:selected="selected"
                    focusable>
                </b-table>
            </b-tab-item>

            <b-tab-item label="Selected">
                <pre>{{ selected }}</pre>
            </b-tab-item>
        </b-tabs>
    </section>
</template>

<script>
    export default {
        data() {
            const data = [
                { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
            ]

            return {
                data,
                selected: data[1],
                columns: [
                    {
                        field: 'id',
                        label: 'ID',
                        width: '40',
                        numeric: true
                    },
                    {
                        field: 'first_name',
                        label: 'First Name',
                    },
                    {
                        field: 'last_name',
                        label: 'Last Name',
                    },
                    {
                        field: 'date',
                        label: 'Date',
                        centered: true
                    },
                    {
                        field: 'gender',
                        label: 'Gender',
                    }
                ]
            }
        }
    }
</script>

# Checkable

You can add checkboxes to rows by using the checkable prop.

To show which rows are checked, you have to pass the corresponding object array to the checked-rows prop. Adding the v-model modifier will make it two-way binding — checked object array will mutate if user clicks on checkboxes.

A slot called bottom-left is available whenever the table is checkable or paginated, you can add anything in there.

ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female
Total checked: 2
<template>
    <section>
        <b-field grouped group-multiline>
            <b-button
                label="Clear checked"
                type="is-danger"
                icon-left="close"
                class="field"
                @click="checkedRows = []" />

            <b-select v-model="checkboxPosition">
                <option value="left">Checkbox at left</option>
                <option value="right">Checkbox at right</option>
            </b-select>

            <b-select v-model="checkboxType">
                <option value="is-primary">Default</option>
                <option value="is-info">Info</option>
                <option value="is-success">Success</option>
                <option value="is-danger">Danger</option>
                <option value="is-warning">Warning</option>
            </b-select>
        </b-field>

        <b-tabs>
            <b-tab-item label="Table">
                <b-table
                    :data="data"
                    :columns="columns"
                    v-model:checked-rows="checkedRows"
                    :is-row-checkable="(row) => row.id !== 3 && row.id !== 4"
                    checkable
                    :checkbox-position="checkboxPosition"
                    :checkbox-type="checkboxType">

                    <template #bottom-left>
                        <b>Total checked</b>: {{ checkedRows.length }}
                    </template>
                </b-table>
            </b-tab-item>

            <b-tab-item label="Checked rows">
                <pre>{{ checkedRows }}</pre>
            </b-tab-item>
        </b-tabs>
    </section>
</template>

<script>
    export default {
        data() {
            const data = [
                { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
            ]

            return {
                data,
                checkboxPosition: 'left',
                checkboxType: 'is-primary',
                checkedRows: [data[1], data[3]],
                columns: [
                    {
                        field: 'id',
                        label: 'ID',
                        width: '40',
                        numeric: true
                    },
                    {
                        field: 'first_name',
                        label: 'First Name',
                    },
                    {
                        field: 'last_name',
                        label: 'Last Name',
                    },
                    {
                        field: 'date',
                        label: 'Date',
                        centered: true
                    },
                    {
                        field: 'gender',
                        label: 'Gender',
                    }
                ]
            }
        }
    }
</script>

# Searchable

You can add search filtering to rows by using the searchable prop.

This feature is not available on mobile when mobile-cards.
ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female

You can debounce search filter to avoid multiple filtering when typing.

ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female

You can also customize the search input using a scoped slot.

ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female
<template>
    <section>
        <b-table
            :data="data"
            :columns="columns">
        </b-table>
        <hr />
        <p>You can debounce search filter to avoid multiple filtering when typing.</p>
        <b-table
            :data="data"
            :columns="columns"
            :debounce-search="1000">
        </b-table>
        <p>You can also customize the search input using a scoped slot.</p>
        <b-table
            :data="data">
            <template v-for="column in columns" :key="column.id">
                <b-table-column v-bind="column">
                    <template
                        v-if="column.searchable && !column.numeric"
                        #searchable="props">
                        <b-input
                            v-model="props.filters[props.column.field]"
                            placeholder="Search..."
                            icon="magnify"
                            size="is-small" />
                    </template>
                    <template v-slot="props">
                        {{ props.row[column.field] }}
                    </template>
                </b-table-column>
            </template>
        </b-table>
    </section>
</template>

 <script>
    export default {
        data() {
            return {
                data: [
                    { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                    { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                    { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                    { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                    { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
                ],
                columns: [
                    {
                        field: 'id',
                        label: 'ID',
                        width: '100',
                        numeric: true,
                        searchable: true,
                    },
                    {
                        field: 'first_name',
                        label: 'First Name',
                        searchable: true,
                    },
                    {
                        field: 'last_name',
                        label: 'Last Name',
                        searchable: true,
                    },
                    {
                        field: 'date',
                        label: 'Date',
                        centered: true
                    },
                    {
                        field: 'gender',
                        label: 'Gender',
                    }
                ]
            }
        }
    }
</script>

# Pagination and sorting

To make a column sortable, add the sortable prop on it and specify a field name.

You can also use the default-sort prop to determine the default sort column and order. The column must be sortable to work.

The default-sort-direction prop can be set to determine the default sort column direction on the first click.

A slot called bottom-left is available whenever the table is checkable or paginated, you can add anything in there.

ID
First Name
Last Name
Date
Gender
30AlanMendoza4/22/2016 Male
57AlanEdwards3/22/2017 Male
20AlbertMendoza8/8/2016 Male
45AndreaMarshall9/4/2016 Female
8AndrewGreene11/20/2016 Male
<template>
    <section>
        <b-field grouped group-multiline>
            <b-select v-model="defaultSortDirection">
                <option value="asc">Default sort direction: ASC</option>
                <option value="desc">Default sort direction: DESC</option>
            </b-select>
            <b-select v-model="perPage" :disabled="!isPaginated">
                <option value="5">5 per page</option>
                <option value="10">10 per page</option>
                <option value="15">15 per page</option>
                <option value="20">20 per page</option>
            </b-select>
            <div class="control">
                <b-button
                    label="Set page to 2"
                    :disabled="!isPaginated"
                    @click="currentPage = 2" />
            </div>
            <div class="control is-flex">
                <b-switch v-model="isPaginated">Paginated</b-switch>
            </div>
            <div class="control is-flex">
                <b-switch v-model="isPaginationSimple" :disabled="!isPaginated">Simple pagination</b-switch>
            </div>
            <div class="control is-flex">
                <b-switch v-model="isPaginationRounded" :disabled="!isPaginated">Rounded pagination</b-switch>
            </div>
            <b-select v-model="paginationPosition" :disabled="!isPaginated">
                <option value="bottom">bottom pagination</option>
                <option value="top">top pagination</option>
                <option value="both">both</option>
            </b-select>
            <b-select v-model="sortIcon">
                <option value="arrow-up">Arrow sort icon</option>
                <option value="menu-up">Caret sort icon</option>
                <option value="chevron-up">Chevron sort icon </option>
            </b-select>
            <b-select v-model="sortIconSize">
                <option value="is-small">Small sort icon</option>
                <option value="">Regular sort icon</option>
                <option value="is-medium">Medium sort icon</option>
                <option value="is-large">Large sort icon</option>
            </b-select>
            <b-select v-model="paginationOrder">
                <option value="">default pagination order</option>
                <option value="is-centered">is-centered pagination order</option>
                <option value="is-right">is-right pagination order</option>
            </b-select>
            <div class="control is-flex">
                <b-switch v-model="hasInput">Input</b-switch>
            </div>
            <b-select v-model="inputPosition">
                <option value="">default input position</option>
                <option value="is-input-right">is-input-right</option>
                <option value="is-input-left">is-input-left</option>
            </b-select>
             <b-input type="number" placeholder="debounce (milliseconds)" v-model="inputDebounce" min="0"></b-input>
        </b-field>

        <b-table
            :data="data"
            :paginated="isPaginated"
            :per-page="perPage"
            v-model:current-page="currentPage"
            :pagination-simple="isPaginationSimple"
            :pagination-position="paginationPosition"
            :default-sort-direction="defaultSortDirection"
            :pagination-rounded="isPaginationRounded"
            :sort-icon="sortIcon"
            :sort-icon-size="sortIconSize"
            default-sort="user.first_name"
            aria-next-label="Next page"
            aria-previous-label="Previous page"
            aria-page-label="Page"
            aria-current-label="Current page"
            :page-input="hasInput"
            :pagination-order="paginationOrder"
            :page-input-position="inputPosition"
            :debounce-page-input="inputDebounce">

            <b-table-column field="id" label="ID" width="40" sortable numeric v-slot="props">
                {{ props.row.id }}
            </b-table-column>

            <b-table-column field="user.first_name" label="First Name" sortable v-slot="props">
                {{ props.row.user.first_name }}
            </b-table-column>

            <b-table-column field="user.last_name" label="Last Name" sortable v-slot="props">
                {{ props.row.user.last_name }}
            </b-table-column>

            <b-table-column field="date" label="Date" sortable centered v-slot="props">
                <span class="tag is-success">
                    {{ new Date(props.row.date).toLocaleDateString() }}
                </span>
            </b-table-column>

            <b-table-column label="Gender" v-slot="props">
                <span>
                    <b-icon pack="fas"
                        :icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
                    </b-icon>
                    {{ props.row.gender }}
                </span>
            </b-table-column>

        </b-table>
    </section>
</template>

<script>
    const data = require('@/data/sample.json')

    export default {
        data() {
            return {
                data,
                isPaginated: true,
                isPaginationSimple: false,
                isPaginationRounded: false,
                paginationPosition: 'bottom',
                defaultSortDirection: 'asc',
                sortIcon: 'arrow-up',
                sortIconSize: 'is-small',
                currentPage: 1,
                perPage: 5,
                hasInput: false,
                paginationOrder: '',
                inputPosition: '',
                inputDebounce: ''
            }
        }
    }
</script>

# Sorting multiple

Since0.8.11

To sort on additional columns, use sort-multiple is enabled

Use $refs.YOURREF.resetMultiSorting() to reset the current multi column sorting

Use sort-multiple-data prop together with backend-sorting if you want to use a custom sorting priority

Use sort-multiple-key prop if you only want to enable multi column sorting when it is in combination with a key. Use value null to have it always enabled (default if not specified)

Reset sorting
First name
Last name
Team
AbbieArcherTeam B
AbbieSmithTeam A
JonesSmithTeam C
AbbieArcherTeam A
<template>
  <div>
    <b-field grouped group-multiline>
      <div class="control is-flex">
        <b-switch v-model="multiColumnSortingEnabled" @input="resetPriority">Sort multiple rows</b-switch>
      </div>
      <div class="control is-flex">
        <span class="button" :disabled="multiColumnSortingDisabledOrUndefined" @click="resetPriority">Reset sorting</span>
      </div>
      <div class="control is-flex">
        <b-select v-model="customKey" :disabled="multiColumnSortingDisabledOrUndefined">
          <option :value="null">None</option>
          <option value="shiftKey">Shift</option>
          <option value="altKey">Alt/Option</option>
          <option value="ctrlKey">Control</option>
        </b-select>
      </div>
      <div class="control is-flex">
        <b-switch v-model="backendSortingEnabled" @input="resetPriority">Backend sorting</b-switch>
      </div>
    </b-field>
    <b-table
      :data="data"
      ref="multiSortTable"
      :backend-sorting="backendSortingEnabled"
      @sort="sortPressed"
      @sorting-priority-removed="sortingPriorityRemoved"

      :sort-multiple="multiColumnSortingEnabled"
      :sort-multiple-data="sortingPriority"
      :sort-multiple-key="customKey"
    >

        <b-table-column field="first_name" label="First name" sortable v-slot="props">
            {{ props.row.first_name }}
        </b-table-column>
        <b-table-column field="last_name" label="Last name" sortable v-slot="props">
            {{ props.row.last_name }}
        </b-table-column>
        <b-table-column field="team" label="Team" sortable v-slot="props">
            {{ props.row.team }}
        </b-table-column>

    </b-table>
  </div>
</template>

<script>
import orderBy from 'lodash/orderBy'

const dataSource = [
  { 'id': 1, 'first_name': 'Abbie', 'last_name': 'Archer', 'team': 'Team B'},
  { 'id': 2, 'first_name': 'Abbie', 'last_name': 'Smith', 'team': 'Team A'},
  { 'id': 3, 'first_name': 'Jones', 'last_name': 'Smith', 'team': 'Team C'},
  { 'id': 4, 'first_name': 'Abbie', 'last_name': 'Archer', 'team': 'Team A'}
]
  export default {
      data() {
          return {
            customKey: null,
            backendSortingEnabled: false,
            multiColumnSortingEnabled: true,
            data: [],
            sortingPriority: []
          }
      },
      computed: {
          multiColumnSortingDisabledOrUndefined() {
              // On Vue 3, setting a boolean attribute `false` does not remove
              // it. `null` or `undefined` has to be given to do so.
              return !this.multiColumnSortingEnabled || undefined
          }
      },
      methods: {
        resetPriority(){
          this.$refs.multiSortTable.resetMultiSorting()

          // reset local backend sorting
          if(this.backendSortingEnabled) {
            this.sortingPriority = []
            this.loadAsyncData()
          }
        },

        // Backend sorting
        sortingPriorityRemoved(field){
          this.sortingPriority = this.sortingPriority.filter(
            (priority) => priority.field !== field)
          this.loadAsyncData(this.sortingPriority)
        },

        sortPressed(field, order, event) {
          if(this.backendSortingEnabled) {
            if(this.multiColumnSortingEnabled){
              if((this.customKey && event[this.customKey]) || !this.customKey) {
                let existingPriority = this.sortingPriority.filter(i => i.field === field)[0]
                if(existingPriority) {
                  existingPriority.order = existingPriority.order === 'desc' ? 'asc' : 'desc'
                } else {
                  // request sorted data from backend
                  this.sortingPriority.push({field, order})
                }
                this.loadAsyncData(this.sortingPriority)
              } else {
                // request regular sorted data from backend
                this.sortingPriority = [] // [{field, order}]
                this.loadAsyncData([{field, order}])
              }
            }
          }
        },

        // "API request" for data
        async loadAsyncData(sortingPriority = []) {
          let mockdata = JSON.parse(JSON.stringify(dataSource));
          // get data already sorted from the backend using sortingPriority
          this.data = orderBy(mockdata, sortingPriority.map(i => i.field), sortingPriority.map(i => i.order))
        }
      },
      created () {
        this.data = JSON.parse(JSON.stringify(dataSource));
      }
  }
</script>

# Detailed rows

You can have detailed rows by adding a detail named scoped slot and the detailed prop.

Since0.7.2

You can also toggle row detail programmatically using toggleDetails method by ref or by default slot and :show-detail-icon="false" if you want to hide chevron icon.

ID
First Name
Last Name
Date
Gender
1JesseSimmons10/15/2016 Male

Jesse Simmons@Jesse31m
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ornare magna eros, eu pellentesque tortor vestibulum ut. Maecenas non massa sem. Etiam finibus odio quis feugiat facilisis.

2JohnJacobs12/15/2016 Male
3TinaGilbert4/26/2016 Female
4ClarenceFlores4/10/2016 Male
5AnneLee12/6/2016 Female
<template>
    <section>

        <b-field grouped group-multiline>
            <div class="control">
                <b-switch v-model="showDetailIcon">Show detail icon</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="useTransition">Use transition (fade) when toggling details</b-switch>
            </div>
        </b-field>

        <b-table
            :data="data"
            ref="table"
            paginated
            per-page="5"
            :opened-detailed="defaultOpenedDetails"
            detailed
            detail-key="id"
            :detail-transition="transitionName"
            @details-open="(row) => $buefy.toast.open(`Expanded ${row.user.first_name}`)"
            :show-detail-icon="showDetailIcon"
            aria-next-label="Next page"
            aria-previous-label="Previous page"
            aria-page-label="Page"
            aria-current-label="Current page">

            <b-table-column field="id" label="ID" width="40" numeric v-slot="props">
                {{ props.row.id }}
            </b-table-column>

            <b-table-column field="user.first_name" label="First Name" sortable v-slot="props">
                <template v-if="showDetailIcon">
                    {{ props.row.user.first_name }}
                </template>
                <template v-else>
                    <a @click="props.toggleDetails(props.row)">
                        {{ props.row.user.first_name }}
                    </a>
                </template>
            </b-table-column>

            <b-table-column field="user.last_name" label="Last Name" sortable v-slot="props">
                {{ props.row.user.last_name }}
            </b-table-column>

            <b-table-column field="date" label="Date" sortable centered v-slot="props">
                <span class="tag is-success">
                    {{ new Date(props.row.date).toLocaleDateString() }}
                </span>
            </b-table-column>

            <b-table-column label="Gender" v-slot="props">
                <span>
                    <b-icon pack="fas"
                        :icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
                    </b-icon>
                    {{ props.row.gender }}
                </span>
            </b-table-column>

            <template #detail="props">
                <article class="media">
                    <figure class="media-left">
                        <p class="image is-64x64">
                            <img src="/static/img/placeholder-128x128.png">
                        </p>
                    </figure>
                    <div class="media-content">
                        <div class="content">
                            <p>
                                <strong>{{ props.row.user.first_name }} {{ props.row.user.last_name }}</strong>
                                <small>@{{ props.row.user.first_name }}</small>
                                <small>31m</small>
                                <br>
                                Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                                Proin ornare magna eros, eu pellentesque tortor vestibulum ut.
                                Maecenas non massa sem. Etiam finibus odio quis feugiat facilisis.
                            </p>
                        </div>
                    </div>
                </article>
            </template>
        </b-table>

    </section>
</template>

<script>
    const data = require('@/data/sample.json')

    export default {
        data() {
            return {
                data,
                defaultOpenedDetails: [1],
                showDetailIcon: true,
                useTransition: false
            }
        },
        computed: {
            transitionName() {
                if (this.useTransition) {
                    return 'fade'
                }
            }
        }
    }
</script>

# Custom Detailed rows

You can add anything you like into the detail named scoped by providing the customDetailRow prop to the table.

Be cautious when using a custom detailed row and toggling the display of columns, as you will have to manage either the content within (with colspan) or the columns themselves dependent on the content displayed.
Name
Stock Sold
Stock Available
Stock Cleared
Board Games13130144%
    Monopoly5710057%
    Scrabble238427%
    Chess376161%
    Battleships145625%
Books43472160%
Jigsaws & Puzzles8816753%
<template>
    <section>
        <b-field grouped group-multiline>
            <div class="control">
                <b-checkbox v-model="showDetailIcon">Detail column</b-checkbox>
            </div>
            <div v-for="(column, index) in columnsVisible"
                 :key="index"
                 class="control">
                <b-checkbox v-model="column.display">
                    {{ column.title }}
                </b-checkbox>
            </div>
        </b-field>

        <b-table
            :data="data"
            ref="table"
            detailed
            hoverable
            custom-detail-row
            :opened-detailed="['Board Games']"
            :default-sort="['name', 'asc']"
            detail-key="name"
            @details-open="(row, index) => $buefy.toast.open(`Expanded ${row.name}`)"
            :show-detail-icon="showDetailIcon">

            <b-table-column
                field="name"
                :visible="columnsVisible['name'].display"
                :label="columnsVisible['name'].title"
                width="300"
                sortable
                v-slot="props"
            >
                <template v-if="showDetailIcon">
                    {{ props.row.name }}
                </template>
                <template v-else>
                    <a @click="toggle(props.row)">
                        {{ props.row.name }}
                    </a>
                </template>
            </b-table-column>

            <b-table-column
                field="sold"
                :visible="columnsVisible['sold'].display"
                :label="columnsVisible['sold'].title"
                sortable
                centered
                v-slot="props"
            >
                {{ props.row.sold }}
            </b-table-column>

            <b-table-column
                field="available"
                :visible="columnsVisible['available'].display"
                :label="columnsVisible['available'].title"
                sortable
                centered
                v-slot="props"
            >
                {{ props.row.available }}
            </b-table-column>

            <b-table-column
                :visible="columnsVisible['cleared'].display"
                :label="columnsVisible['cleared'].title"
                centered
                v-slot="props"
            >
                <span :class="
                        [
                            'tag',
                            {'is-danger': props.row.sold / props.row.available <= 0.45},
                            {'is-success': props.row.sold / props.row.available > 0.45}
                        ]">
                    {{ Math.round((props.row.sold / props.row.available) * 100) }}%
                </span>
            </b-table-column>

            <template v-slot:detail="props">
                <tr v-for="item in props.row.items" :key="item.name">
                    <td v-if="showDetailIcon"></td>
                    <td v-show="columnsVisible['name'].display" >&nbsp;&nbsp;&nbsp;&nbsp;{{ item.name }}</td>
                    <td v-show="columnsVisible['sold'].display" class="has-text-centered">{{ item.sold }}</td>
                    <td v-show="columnsVisible['available'].display" class="has-text-centered">{{ item.available }}</td>
                    <td v-show="columnsVisible['cleared'].display" class="has-text-centered">
                        <span :class="
                            [
                                'tag',
                                {'is-danger': item.sold / item.available <= 0.45},
                                {'is-success': item.sold / item.available > 0.45}
                            ]">
                            {{ Math.round((item.sold / item.available) * 100) }}%
                        </span>
                    </td>
                </tr>
            </template>
        </b-table>

    </section>
</template>

<script>
    export default {
        data() {
            return {
                data: [
                    {
                        name: 'Board Games',
                        sold: 131,
                        available: 301,
                        items: [
                            {
                                name: 'Monopoly',
                                sold: 57,
                                available: 100
                            },
                            {
                                name: 'Scrabble',
                                sold: 23,
                                available: 84
                            },
                            {
                                name: 'Chess',
                                sold: 37,
                                available: 61
                            },
                            {
                                name: 'Battleships',
                                sold: 14,
                                available: 56
                            }
                        ]
                    },
                    {
                        name: 'Jigsaws & Puzzles',
                        sold: 88,
                        available: 167,
                        items: [
                            {
                                name: 'World Map',
                                sold: 31,
                                available: 38
                            },
                            {
                                name: 'London',
                                sold: 23,
                                available: 29
                            },
                            {
                                name: 'Sharks',
                                sold: 20,
                                available: 44
                            },
                            {
                                name: 'Disney',
                                sold: 14,
                                available: 56
                            }
                        ]
                    },
                    {
                        name: 'Books',
                        sold: 434,
                        available: 721,
                        items: [
                            {
                                name: 'Hamlet',
                                sold: 101,
                                available: 187
                            },
                            {
                                name: 'The Lord Of The Rings',
                                sold: 85,
                                available: 156
                            },
                            {
                                name: 'To Kill a Mockingbird',
                                sold: 78,
                                available: 131
                            },
                            {
                                name: 'Catch-22',
                                sold: 73,
                                available: 98
                            },
                            {
                                name: 'Frankenstein',
                                sold: 51,
                                available: 81
                            },
                            {
                                name: 'Alice\'s Adventures In Wonderland',
                                sold: 46,
                                available: 68
                            }
                        ]
                    }
                ],
                columnsVisible: {
                    name: { title: 'Name', display: true },
                    sold: { title: 'Stock Sold', display: true },
                    available: { title: 'Stock Available', display: true },
                    cleared: { title: 'Stock Cleared', display: true },
                },
                showDetailIcon: true
            }
        },
        methods: {
            toggle(row) {
                this.$refs.table.toggleDetails(row)
            }
        }
    }
</script>

# Row status

Use the row-class prop to return a class name. It's a function that receives row and index parameters.

Note that you have to style the class yourself.

ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female
<template>
    <b-table
        :data="data"
        :columns="columns"
        :row-class="(row, index) => row.id === 1 && 'is-info'">
    </b-table>
</template>

<script>
    export default {
        data() {
            return {
                data: [
                    { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                    { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                    { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                    { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                    { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
                ],
                columns: [
                    {
                        field: 'id',
                        label: 'ID',
                        width: '40',
                        numeric: true
                    },
                    {
                        field: 'first_name',
                        label: 'First Name',
                    },
                    {
                        field: 'last_name',
                        label: 'Last Name',
                    },
                    {
                        field: 'date',
                        label: 'Date',
                        centered: true
                    },
                    {
                        field: 'gender',
                        label: 'Gender',
                    }
                ]
            }
        }
    }
</script>

<style>
    tr.is-info {
        background: #167df0;
        color: #fff;
    }
</style>

# Custom headers

By adding a scoped slot named header in table component you can customize the headers. Use the meta prop on column to pass anything you may need.

Previous scoped slot named header in table will be deprecated.

ID
First Name
Last Name
Date
Gender
1JesseSimmons10/15/2016 Male
2JohnJacobs12/15/2016 Male
3TinaGilbert4/26/2016 Female
4ClarenceFlores4/10/2016 Male
5AnneLee12/6/2016 Female
<template>
    <b-table :data="data">

        <b-table-column field="id" label="ID" width="40" numeric>
            <template v-slot:header="{ column }">
                <b-tooltip :label="column.label" append-to-body dashed>
                    {{ column.label }}
                </b-tooltip>
            </template>
            <template v-slot="props">
                {{ props.row.id }}
            </template>
        </b-table-column>

        <b-table-column field="user.first_name" label="First Name">
            <template v-slot:header="{ column }">
                <b-tooltip :label="column.label" append-to-body dashed>
                    {{ column.label }}
                </b-tooltip>
            </template>
            <template v-slot="props">
                {{ props.row.first_name }}
            </template>
        </b-table-column>

        <b-table-column field="user.last_name" label="Last Name">
            <template v-slot:header="{ column }">
                <b-tooltip :label="column.label" append-to-body dashed>
                    {{ column.label }}
                </b-tooltip>
            </template>
            <template v-slot="props">
                {{ props.row.last_name }}
            </template>
        </b-table-column>

        <b-table-column field="date" label="Date" centered v-slot="props">
            <span class="tag is-success">
                {{ new Date(props.row.date).toLocaleDateString() }}
            </span>
        </b-table-column>

        <b-table-column label="Gender" v-slot="props">
            <span>
                <b-icon pack="fas"
                    :icon="props.row.gender === 'Male' ? 'mars' : 'venus'">
                </b-icon>
                {{ props.row.gender }}
            </span>
        </b-table-column>

    </b-table>
</template>

<script>
    export default {
        data() {
            return {
                data: [
                    { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                    { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                    { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                    { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                    { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
                ]
            }
        }
    }
</script>

# Subheadings

Use the subheading prop on columns to add subheadings. This is particularly useful to display a summary when dealing with long tables.

By adding a scoped slot named subheading in table component you can customize the subheadings.

ID
Contributor
Posts
Comments
Total:
18
60
1Jesse Simmons25
2John Jacobs1142
3Tina Gilbert07
4Clarence Flores44
5Anne Lee12
<template>
    <section>
        <b-table
            :data="data"
            :columns="columns">
        </b-table>
    </section>
</template>

 <script>
    export default {
        data() {
            return {
                data: [
                    { 'id': 1, 'contributor': 'Jesse Simmons', 'posts': 2, 'comments': 5 },
                    { 'id': 2, 'contributor': 'John Jacobs', 'posts': 11, 'comments': 42 },
                    { 'id': 3, 'contributor': 'Tina Gilbert', 'posts': 0, 'comments': 7 },
                    { 'id': 4, 'contributor': 'Clarence Flores', 'posts': 4, 'comments': 4 },
                    { 'id': 5, 'contributor': 'Anne Lee', 'posts': 1, 'comments': 2 }
                ],
                columns: [
                    {
                        field: 'id',
                        label: 'ID',
                        width: '100',
                        numeric: true,
                        subheading: 'Total:'
                    },
                    {
                        field: 'contributor',
                        label: 'Contributor',
                    },
                    {
                        field: 'posts',
                        label: 'Posts',
                        subheading: 18
                    },
                    {
                        field: 'comments',
                        label: 'Comments',
                        subheading: 60
                    }
                ]
            }
        }
    }
</script>

# Sticky Headers and Columns

Use the sticky-header prop to show a scrolling table with fixed headers.

Use the sticky prop on column to show a scrolling table with a fixed column.

Since0.8.13

The default height is 300px but you can overwrite it using height prop or $table-sticky-header-height Sass variable.

ID
First Name
Last Name
Date
Gender
Column A
Column B
Column C
Column D
Column E
Column F
Column G
Column H
Column I
Column L
Column M
Column N
Column O
1JesseSimmons2016/10/15 13:43:27Male1111111111111
2JohnJacobs2016/12/15 06:00:53Male2222222222222
3TinaGilbert2016/04/26 06:26:28Female3333333333333
4ClarenceFlores2016/04/10 10:28:46Male4444444444444
5AnneLee2016/12/06 14:38:38Female5555555555555
6SaraArmstrong2016/09/23 18:50:04Female6666666666666
7AnthonyWebb2016/08/30 23:49:38Male7777777777777
8AndrewGreene2016/11/20 14:57:47Male8888888888888
9RussellWhite2016/07/13 09:29:49Male9999999999999
10LoriHunter2016/12/09 01:44:05Female10101010101010101010101010

Use checkable and sticky-checkbox to make a sticky checkbox column.

ID
First Name
Last Name
Date
Gender
Column A
Column B
Column C
Column D
Column E
Column F
Column G
Column H
Column I
Column L
Column M
Column N
Column O
1JesseSimmons2016/10/15 13:43:27Male1111111111111
2JohnJacobs2016/12/15 06:00:53Male2222222222222
3TinaGilbert2016/04/26 06:26:28Female3333333333333
4ClarenceFlores2016/04/10 10:28:46Male4444444444444
5AnneLee2016/12/06 14:38:38Female5555555555555
6SaraArmstrong2016/09/23 18:50:04Female6666666666666
7AnthonyWebb2016/08/30 23:49:38Male7777777777777
8AndrewGreene2016/11/20 14:57:47Male8888888888888
9RussellWhite2016/07/13 09:29:49Male9999999999999
10LoriHunter2016/12/09 01:44:05Female10101010101010101010101010
<template>
    <section>
        <b-field grouped group-multiline>
            <div class="control">
                <b-switch v-model="stickyHeaders">Sticky Headers</b-switch>
            </div>
            <div class="control">
                <b-switch v-model="dateSearchable">Date searchable</b-switch>
            </div>
        </b-field>
        <b-table
            :data="data"
            :columns="columns"
            :sticky-header="stickyHeaders"
        ></b-table>
        <br />
        Use <code>checkable</code> and <code>sticky-checkbox</code> to make a sticky checkbox column.
        <br />
        <br />
        <b-table
            :data="data"
            :columns="checkableColumns"
            :sticky-header="stickyHeaders"
            checkable
            sticky-checkbox
            striped
        ></b-table>
    </section>
</template>

<script>
export default {
    data() {
        return {
            data: [
                {
                    id: 1,
                    user: { first_name: "Jesse", last_name: "Simmons" },
                    date: "2016/10/15 13:43:27",
                    gender: "Male"
                },
                {
                    id: 2,
                    user: { first_name: "John", last_name: "Jacobs" },
                    date: "2016/12/15 06:00:53",
                    gender: "Male"
                },
                {
                    id: 3,
                    user: { first_name: "Tina", last_name: "Gilbert" },
                    date: "2016/04/26 06:26:28",
                    gender: "Female"
                },
                {
                    id: 4,
                    user: { first_name: "Clarence", last_name: "Flores" },
                    date: "2016/04/10 10:28:46",
                    gender: "Male"
                },
                {
                    id: 5,
                    user: { first_name: "Anne", last_name: "Lee" },
                    date: "2016/12/06 14:38:38",
                    gender: "Female"
                },
                {
                    id: 6,
                    user: { first_name: "Sara", last_name: "Armstrong" },
                    date: "2016/09/23 18:50:04",
                    gender: "Female"
                },
                {
                    id: 7,
                    user: { first_name: "Anthony", last_name: "Webb" },
                    date: "2016/08/30 23:49:38",
                    gender: "Male"
                },
                {
                    id: 8,
                    user: { first_name: "Andrew", last_name: "Greene" },
                    date: "2016/11/20 14:57:47",
                    gender: "Male"
                },
                {
                    id: 9,
                    user: { first_name: "Russell", last_name: "White" },
                    date: "2016/07/13 09:29:49",
                    gender: "Male"
                },
                {
                    id: 10,
                    user: { first_name: "Lori", last_name: "Hunter" },
                    date: "2016/12/09 01:44:05",
                    gender: "Female"
                }
            ],
            stickyHeaders: true,
            dateSearchable: false
        };
    },
    computed: {
        columns() {
            return [
                {
                    field: "id",
                    label: "ID",
                    width: "40",
                    numeric: true,
                    sticky: true,
                    headerClass: "is-sticky-column-one",
                    cellClass: "is-sticky-column-one"
                },
                {
                    field: "user.first_name",
                    label: "First Name"
                },
                {
                    field: "user.last_name",
                    label: "Last Name"
                },
                {
                    field: "date",
                    label: "Date",
                    searchable: this.dateSearchable,
                    centered: true,
                    sticky: true,
                    headerClass: "is-sticky-column-two",
                    cellClass: "is-sticky-column-two"
                },
                {
                    field: "gender",
                    label: "Gender"
                },
                {
                    field: "id",
                    label: "Column A"
                },
                {
                    field: "id",
                    label: "Column B"
                },
                {
                    field: "id",
                    label: "Column C"
                },
                {
                    field: "id",
                    label: "Column D"
                },
                {
                    field: "id",
                    label: "Column E"
                },
                {
                    field: "id",
                    label: "Column F"
                },
                {
                    field: "id",
                    label: "Column G"
                },
                {
                    field: "id",
                    label: "Column H"
                },
                {
                    field: "id",
                    label: "Column I"
                },
                {
                    field: "id",
                    label: "Column L"
                },
                {
                    field: "id",
                    label: "Column M"
                },
                {
                    field: "id",
                    label: "Column N"
                },
                {
                    field: "id",
                    label: "Column O"
                }
            ];
        },
        checkableColumns() {
            return [
                {
                    field: "id",
                    label: "ID",
                    width: "40",
                    numeric: true,
                    sticky: false,
                },
                {
                    field: "user.first_name",
                    label: "First Name"
                },
                {
                    field: "user.last_name",
                    label: "Last Name"
                },
                {
                    field: "date",
                    label: "Date",
                    searchable: this.dateSearchable,
                    centered: true,
                    sticky: false,
                },
                {
                    field: "gender",
                    label: "Gender"
                },
                {
                    field: "id",
                    label: "Column A"
                },
                {
                    field: "id",
                    label: "Column B"
                },
                {
                    field: "id",
                    label: "Column C"
                },
                {
                    field: "id",
                    label: "Column D"
                },
                {
                    field: "id",
                    label: "Column E"
                },
                {
                    field: "id",
                    label: "Column F"
                },
                {
                    field: "id",
                    label: "Column G"
                },
                {
                    field: "id",
                    label: "Column H"
                },
                {
                    field: "id",
                    label: "Column I"
                },
                {
                    field: "id",
                    label: "Column L"
                },
                {
                    field: "id",
                    label: "Column M"
                },
                {
                    field: "id",
                    label: "Column N"
                },
                {
                    field: "id",
                    label: "Column O"
                }
            ];
        },
    }
};
</script>

<style>
.is-sticky-column-one {
    background: #23d160 !important;
    color: white !important;
}
.is-sticky-column-two {
    background: #167df0 !important;
    color: white !important;
}
</style>

# Toggle columns

Always use the visible prop to hide/show columns, and NOT v-if or v-show.
ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female
<template>
    <section>
        <b-field grouped group-multiline>
            <div v-for="(column, index) in columnsTemplate"
                :key="index"
                class="control">
                <b-checkbox v-model="column.visible">
                    {{ column.title }}
                </b-checkbox>
            </div>
        </b-field>

        <b-table :data="tableDataSimple">
            <b-table-column v-for="(column, index) in columnsTemplate"
                :key="index"
                :label="column.title"
                :visible="column.visible"
                v-slot="props">
                {{ props.row[column.field] }}
            </b-table-column>
        </b-table>
    </section>
</template>

<script>
    export default {
        data() {
            const tableDataSimple = [
                { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
            ]

            return {
                tableDataSimple,
                columnsTemplate: [
                    { title: 'ID', field: 'id', visible: true },
                    { title: 'First Name', field: 'first_name', visible: true },
                    { title: 'Last Name', field: 'last_name', visible: true },
                    { title: 'Date', field: 'date', visible: true },
                    { title: 'Gender', field: 'gender', visible: true }
                ]
            }
        }
    }
</script>

ID
First Name
Last Name
Date
Gender
1JesseSimmons10/15/2016Male
2JohnJacobs12/15/2016Male
3TinaGilbert4/26/2016Female
4ClarenceFlores4/10/2016Male
5AnneLee12/6/2016Female
<template>
    <section>
        <b-field grouped group-multiline>
            <div class="control">
                <b-switch v-model="isCustom">Custom</b-switch>
            </div>
        </b-field>
        <b-table :data="tableDataSimple">

            <b-table-column label="ID" width="40" numeric v-slot="props">
                {{ props.row.id }}
            </b-table-column>

            <b-table-column label="First Name" v-slot="props">
                {{ props.row.first_name }}
            </b-table-column>

            <b-table-column label="Last Name" v-slot="props">
                {{ props.row.last_name }}
            </b-table-column>

            <b-table-column label="Date" centered v-slot="props">
                {{ new Date(props.row.date).toLocaleDateString() }}
            </b-table-column>

            <b-table-column label="Gender" v-slot="props">
                {{ props.row.gender }}
            </b-table-column>


            <!--
            If `v-slot` (i.e., #) and `v-if` were in the same template tag,
            I got a maximum recursion exceeding error. If `v-slot` was inside
            `v-if`, I got a compilation error.
            https://github.com/vuejs/vue/issues/9391#issuecomment-459101293
            -->
            <template #footer>
                <template v-if="!isCustom">
                    <div class="has-text-right">
                        Footer
                    </div>
                </template>
                <template v-else>
                    <th class="is-hidden-mobile" style="width:40px">
                        <div class="th-wrap is-numeric"> ID </div>
                    </th>
                    <th class="is-hidden-mobile">
                        <div class="th-wrap"> First Name </div>
                    </th>
                    <th class="is-hidden-mobile">
                        <div class="th-wrap"> Last Name </div>
                    </th>
                    <th class="is-hidden-mobile">
                        <div class="th-wrap is-centered"> Date </div>
                    </th>
                    <th class="is-hidden-mobile">
                        <div class="th-wrap"> Gender </div>
                    </th>
                </template>
            </template>

        </b-table>
    </section>
</template>

<script>
    export default {
        data() {
            const tableDataSimple = [
                { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016/10/15 13:43:27', 'gender': 'Male' },
                { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016/12/15 06:00:53', 'gender': 'Male' },
                { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016/04/26 06:26:28', 'gender': 'Female' },
                { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016/04/10 10:28:46', 'gender': 'Male' },
                { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016/12/06 14:38:38', 'gender': 'Female' }
            ]

            return {
                tableDataSimple,
                isCustom: false
            }
        }
    }
</script>

# Async data

Use backend-sorting and backend-pagination props to let those tasks to the backend, then manage it with page-change and sort events.

API from TMDb.

Title
Vote Average
Vote Count
Release Date
Overview
Inception8.4346117/15/2010Cobb, a skilled thief who commits corporate espionage by infiltrating the subcon...
Interstellar8.43271111/5/2014The adventures of a group of explorers who make use of a newly discovered wormho...
The Dark Knight8.5307507/16/2008Batman raises the stakes in his war on crime. With the help of Lt. Jim Gordon an...
Avatar7.62989212/15/2009In the 22nd century, a paraplegic Marine is dispatched to the moon Pandora on a ...
The Avengers7.7292314/25/2012When an unexpected enemy emerges and threatens global safety and security, Nick ...
Deadpool7.6289592/9/2016The origin story of former Special Forces operative turned mercenary Wade Wilson...
Avengers: Infinity War8.3278094/25/2018As the Avengers and their allies have continued to protect the world from threat...
Fight Club8.42735110/15/1999A ticking-time-bomb insomniac and a slippery soap salesman channel primal male a...
Guardians of the Galaxy7.9267137/30/2014Light years from Earth, 26 years after being abducted, Peter Quill finds himself...
Pulp Fiction8.5260079/10/1994A burger-loving hit man, his philosophical partner, a drug-addled gangster's mol...
Forrest Gump8.5255206/23/1994A man with a low IQ has accomplished great things in his life and been present d...
Harry Potter and the Philosopher's Stone7.92546711/16/2001Harry Potter has lived under the stairs at his aunt and uncle's house his whole ...
Iron Man7.6249384/30/2008After being held captive in an Afghan cave, billionaire engineer Tony Stark crea...
The Shawshank Redemption8.7247599/23/1994Framed in the 1940s for the double murder of his wife and her lover, upstanding ...
Django Unchained8.22475112/25/2012With the help of a German bounty hunter, a freed slave sets out to rescue his wi...
The Matrix8.2239533/30/1999Set in the 22nd century, The Matrix tells the story of a computer hacker who joi...
Avengers: Endgame8.3239374/24/2019After the devastating events of Avengers: Infinity War, the universe is in ruins...
Titanic7.92373311/18/1997101-year-old Rose DeWitt Bukater tells the story of her life aboard the Titanic,...
Joker8.22352410/1/2019During the 1980s, a failed stand-up comedian is driven insane and turns to a lif...
The Lord of the Rings: The Fellowship of the Ring8.42341912/18/2001Young hobbit Frodo Baggins, after inheriting a mysterious ring from his uncle Bi...
<template>
    <section>
        <b-table
            :data="data"
            :loading="loading"

            paginated
            backend-pagination
            :total="total"
            :per-page="perPage"
            @page-change="onPageChange"
            aria-next-label="Next page"
            aria-previous-label="Previous page"
            aria-page-label="Page"
            aria-current-label="Current page"

            backend-sorting
            :default-sort-direction="defaultSortOrder"
            :default-sort="[sortField, sortOrder]"
            @sort="onSort">

            <b-table-column field="original_title" label="Title" sortable v-slot="props">
                {{ props.row.original_title }}
            </b-table-column>

            <b-table-column field="vote_average" label="Vote Average" numeric sortable v-slot="props">
                <span class="tag" :class="type(props.row.vote_average)">
                    {{ props.row.vote_average }}
                </span>
            </b-table-column>

            <b-table-column field="vote_count" label="Vote Count" numeric sortable v-slot="props">
                    {{ props.row.vote_count }}
            </b-table-column>

            <b-table-column field="release_date" label="Release Date" sortable centered v-slot="props">
                {{ props.row.release_date ? new Date(props.row.release_date).toLocaleDateString() : 'unknown' }}
            </b-table-column>

            <b-table-column label="Overview" width="500" v-slot="props">
                {{ truncate(props.row.overview, 80) }}
            </b-table-column>

        </b-table>
    </section>
</template>

<script>
    export default {
        data() {
            return {
                data: [],
                total: 0,
                loading: false,
                sortField: 'vote_count',
                sortOrder: 'desc',
                defaultSortOrder: 'desc',
                page: 1,
                perPage: 20
            }
        },
        methods: {
            /*
        * Load async data
        */
            loadAsyncData() {
                const params = [
                    'api_key=bb6f51bef07465653c3e553d6ab161a8',
                    'language=en-US',
                    'include_adult=false',
                    'include_video=false',
                    `sort_by=${this.sortField}.${this.sortOrder}`,
                    `page=${this.page}`
                ].join('&')

                this.loading = true
                this.$http.get(`https://api.themoviedb.org/3/discover/movie?${params}`)
                    .then(({ data }) => {
                        // api.themoviedb.org manage max 1000 pages
                        this.data = []
                        let currentTotal = data.total_results
                        if (data.total_results / this.perPage > 1000) {
                            currentTotal = this.perPage * 1000
                        }
                        this.total = currentTotal
                        data.results.forEach((item) => {
                            item.release_date = item.release_date ? item.release_date.replace(/-/g, '/') : null
                            this.data.push(item)
                        })
                        this.loading = false
                    })
                    .catch((error) => {
                        this.data = []
                        this.total = 0
                        this.loading = false
                        throw error
                    })
            },
            /*
        * Handle page-change event
        */
            onPageChange(page) {
                this.page = page
                this.loadAsyncData()
            },
            /*
        * Handle sort event
        */
            onSort(field, order) {
                this.sortField = field
                this.sortOrder = order
                this.loadAsyncData()
            },
            /*
        * Type style in relation to the value
        */
            type(value) {
                const number = parseFloat(value)
                if (number < 6) {
                    return 'is-danger'
                } else if (number >= 6 && number < 8) {
                    return 'is-warning'
                } else if (number >= 8) {
                    return 'is-success'
                }
            },
            // filter is no longer supported on Vue 3
            truncate(value, length) {
                return value.length > length
                    ? value.substr(0, length) + '...'
                    : value
            }
        },
        mounted() {
            this.loadAsyncData()
        }
    }
</script>

# Draggable rows/columns

Use draggable/draggable-column prop to allow rows and columns to be draggable. Manage dragging using dragstart/columndragstart, dragover/columndragover and drop/columndrop events

ID
First Name
Last Name
Date
Gender
1JesseSimmons2016-10-15 13:43:27Male
2JohnJacobs2016-12-15 06:00:53Male
3TinaGilbert2016-04-26 06:26:28Female
4ClarenceFlores2016-04-10 10:28:46Male
5AnneLee2016-12-06 14:38:38Female
<template>
  <div>
    <b-table
      :data="data"
      :columns="columns"
      draggable
      draggable-column
      @dragstart="dragstart"
      @drop="drop"
      @dragover="dragover"
      @dragleave="dragleave"
      @columndragstart="columndragstart"
      @columndrop="columndrop"
      @columndragover="columndragover"
      @columndragleave="columndragleave">
    </b-table>
  </div>
</template>

<script>
  export default {
      data() {
          return {
              data: [
                  { 'id': 1, 'first_name': 'Jesse', 'last_name': 'Simmons', 'date': '2016-10-15 13:43:27', 'gender': 'Male' },
                  { 'id': 2, 'first_name': 'John', 'last_name': 'Jacobs', 'date': '2016-12-15 06:00:53', 'gender': 'Male' },
                  { 'id': 3, 'first_name': 'Tina', 'last_name': 'Gilbert', 'date': '2016-04-26 06:26:28', 'gender': 'Female' },
                  { 'id': 4, 'first_name': 'Clarence', 'last_name': 'Flores', 'date': '2016-04-10 10:28:46', 'gender': 'Male' },
                  { 'id': 5, 'first_name': 'Anne', 'last_name': 'Lee', 'date': '2016-12-06 14:38:38', 'gender': 'Female' }
              ],
              columns: [
                  {
                      field: 'id',
                      label: 'ID',
                      width: '40',
                      numeric: true
                  },
                  {
                      field: 'first_name',
                      label: 'First Name',
                  },
                  {
                      field: 'last_name',
                      label: 'Last Name',
                  },
                  {
                      field: 'date',
                      label: 'Date',
                      centered: true
                  },
                  {
                      field: 'gender',
                      label: 'Gender',
                  }
              ],
              draggingRow: null,
              draggingRowIndex: null,
              draggingColumn: null,
              draggingColumnIndex: null
          }
      },
      methods: {
        dragstart(payload) {
          this.draggingRow = payload.row
          this.draggingRowIndex = payload.index
          payload.event.dataTransfer.effectAllowed = 'copy'
        },
        dragover(payload) {
          payload.event.dataTransfer.dropEffect = 'copy'
          payload.event.target.closest('tr').classList.add('is-selected')
          payload.event.preventDefault()
        },
        dragleave(payload) {
          payload.event.target.closest('tr').classList.remove('is-selected')
          payload.event.preventDefault()
        },
        drop(payload) {
          payload.event.target.closest('tr').classList.remove('is-selected')
          const droppedOnRowIndex = payload.index
          this.$buefy.toast.open(`Moved ${this.draggingRow.first_name} from row ${this.draggingRowIndex + 1} to ${droppedOnRowIndex + 1}`)
        },

        columndragstart(payload) {
          this.draggingColumn = payload.column
          this.draggingColumnIndex = payload.index
          payload.event.dataTransfer.effectAllowed = 'copy'
        },
        columndragover(payload) {
          payload.event.dataTransfer.dropEffect = 'copy'
          payload.event.target.closest('th').classList.add('is-selected')
          payload.event.preventDefault()
        },
        columndragleave(payload) {
          payload.event.target.closest('th').classList.remove('is-selected')
          payload.event.preventDefault()
        },
        columndrop(payload) {
          payload.event.target.closest('th').classList.remove('is-selected')
          const droppedOnColumnIndex = payload.index
          this.$buefy.toast.open(`Moved ${this.draggingColumn.field} from column ${this.draggingColumnIndex + 1} to ${droppedOnColumnIndex + 1}`)
        }
      }
  }
</script>

# API

Table

Name
Description
Type
Values
Default
dataTable dataArray
columnsTable columnsArray (same as TableColumns props)
default-sortSets the default sort column and order — e.g. ['first_name', 'desc']String, Arrayorder: default-sort-direction prop
default-sort-directionSets the default sort column direction on the first clickStringasc, descasc
sort-iconSets the header sorting iconString-arrow-up
sort-icon-sizeSets the size of the sorting iconStringis-small, , is-medium, is-largeis-small
borderedBorder to all cellsBooleanfalse
stripedWhether table is stripedBooleanfalse
narrowedMakes the cells narrowerBooleanfalse
selectedSet which row is selected, use the v-model modifier to make it two-way bindingObject
focusableTable can be focused and user can navigate with keyboard arrows (require v-model:selected) and rows are highlighted when hoveringBooleanfalse
hoverableRows are highlighted when hoveringBooleanfalse
checkableRows can be checked (multiple), checked rows will have a .is-checked class if you want to styleBooleanfalse
checkbox-positionPosition of the checkbox (if checkable is true)Stringleft or rightleft
sticky-checkboxMake the checkbox column sticky when checkableBooleanfalse
checked-rowsSet which rows are checked, use the v-model modifier to make it two-way bindingArray
header-checkableShow check/uncheck all checkbox in table header when checkableBooleantrue
checkbox-typeType (color) of the checkbox when checkable, optionalStringis-white, is-black, is-light, is-dark, is-primary, is-info, is-success, is-warning, is-danger, and any other colors you've set in the $colors list on Sassis-primary
mobile-cardsRows appears as cards on mobile (collapse rows)Booleantrue
backend-sortingColumns won't be sorted with Javascript, use with sort event to sort in your backendBooleanfalse
backend-paginationRows won't be paginated with Javascript, use with page-change event to paginate in your backendBooleanfalse
totalTotal number of table data if backend-pagination is enabledNumber0
current-pageCurrent page of table data (if paginated)Number1
loadingLoading stateBooleanfalse
paginatedAdds pagination to the tableBooleanfalse
pagination-simpleSimple pagination (if paginated)Booleanfalse
pagination-roundedRounded pagination (if paginated)Booleanfalse
pagination-orderButtons order, optionalStringis-centered, is-right
pagination-sizePagination size (if paginated)Stringis-small, is-medium, is-large
pagination-positionPagination position (if paginated)Stringbottom, top, bothbottom
per-pageHow many rows per page (if paginated)Number20
page-inputInclude page number input.Booleanfalse
page-input-positionPage input position.Stringis-input-right, is-input-left
debounce-page-inputSets the page input debounce time (in milliseconds)Number
sort-multipleAdds multiple column sortingBooleanfalse
sort-multiple-dataUsed in combination with backend-sortingObject[{field, order}][]
sort-multiple-keyAdds a key which will be required for multi column sorting to work. Will always be enabled if null is selected (default). Requires sort-multipleStringnull, shiftKey, altKey, ctrlKeynull
row-classAdd a class to row (<tr> element) based on the returnFunction (row: Object, index: Number)
detailedAllow row details (check scoped slots documentation)Booleanfalse
custom-detail-rowAllow a custom detail rowBooleanfalse
show-detail-iconAllow chevron icon and column to be visibleBooleantrue
detail-iconIcon nameStringchevron-right
opened-detailedAllow pre-defined opened details. Ideal to open details via vue-router. (A unique key is required; check detail-key prop)Array[]
has-detailed-visibleControls the visibility of the trigger that toggles the detailed rows.Function (row: Object)true
detail-keyUse a unique key of your data Object when use detailed or opened detailed. (id recommended)String
detail-transitionTransition name to use when toggling row details.String
custom-is-checkedCustom method to verify if row is checked, works when is checkable. Useful for backend paginationFunction (a: Object, b: Object)
is-row-checkableCustom method to verify if a row is checkable, works when is checkable. Function (row: Object)true
is-row-selectableCustom method to verify if a row is selectable, works when is selected. Function (row: Object)true
icon-packIcon pack to useStringmdi, fa, fas, far, fad, falmdi
mobile-sort-placeholderText when nothing is selectedString
custom-row-keyUse a unique key of your data Object for each row. Useful if your data prop has dynamic indices. (id recommended)String--
draggableAllows rows to be draggableBooleanfalse
draggable-columnAllows columns to be draggableBooleanfalse
backend-filteringColumns won't be filtered with Javascript, use with searchable prop to the columns to filter in your backendBooleanfalse
sticky-headerShow a sticky table headerBooleanfalse
scrollableAdd a horizontal scrollbar when table is too wideBooleanfalse
heightTable fixed height in pixelsNumber, String
filters-eventAdd a native event to filterString
card-layoutRows appears as cards (collapse rows)Booleanfalse
show-headerShow table column headerBooleantrue
aria-next-labelAccessibility label for the next page link (if paginated)String
aria-previous-labelAccessibility label for the previous page link (if paginated)String
aria-page-labelAccessibility label for the page link. If passed, this text will be prepended to the number of the page (if paginated)String
aria-current-labelAccessibility label for the current page link. If passed, this text will be prepended to the current page (if paginated)String
debounce-searchSets the filtering debounce time (in milliseconds)Number

Column

Name
Description
Type
Values
Default
labelColumn header text, also used to identify column if custom-key prop is missingString
custom-keyUnique identifier, use when label is missing or there are duplicate label namesString, Numberthis.label
fieldProperty of the object the column is attributed, used for sortingString
metaMeta prop to add anything, useful when creating custom headersAny
widthColumn fixed width in any unit, or pixels when none is providedNumber, String
numericAlign the cell content to the right, sort icon on leftBooleanfalse
centeredAlign the cell content to the centerBooleanfalse
sortableWhether the column can be sortedBooleanfalse
visibleWhether the column is visibleBooleantrue
custom-sortCustom sort method, works when column is sortableFunction (a: Object, b: Object, isAsc: Boolean)
searchableAdd a input below the header to filter dataBooleanfalse
custom-searchCustom search method, works when column is searchableFunction (a: Object, input: String)
subheadingColumn subheading textString, Number
stickyShow a sticky columnBooleanfalse
header-selectableWhether the header text is selectable, works when column is sortable.Booleanfalse
header-classCSS classes to be applied on headerString-
cell-classCSS classes to be applied on cellString-
th-attrsAdds native attributes to th :th-attrs="(column)" => ({})"Function-
td-attrsAdds native attributes to td :td-attrs="(row, column)" => ({})"Function-

# Variables

You can use these variables to customize this component.

Name
Default
$table-sticky-header-height300px
Bulma variablesLink