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
1 Jesse Simmons 2016-10-15 13:43:27 Male
2 John Jacobs 2016-12-15 06:00:53 Male
3 Tina Gilbert 2016-04-26 06:26:28 Female
4 Clarence Flores 2016-04-10 10:28:46 Male
5 Anne Lee 2016-12-06 14:38:38 Female
<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
1 Jesse Simmons 15/10/2016 Male
2 John Jacobs 15/12/2016 Male
3 Tina Gilbert 26/4/2016 Female
4 Clarence Flores 10/4/2016 Male
5 Anne Lee 6/12/2016 Female
<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">

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

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

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

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

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

            <template slot="empty">
                <section class="section">
                    <div class="content has-text-grey has-text-centered">
                        <p>
                            <b-icon
                                icon="emoticon-sad"
                                size="is-large">
                            </b-icon>
                        </p>
                        <p>Nothing here.</p>
                    </div>
                </section>
            </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' }
            ]

            return {
                data,
                isEmpty: false,
                isBordered: false,
                isStriped: false,
                isNarrowed: false,
                isHoverable: false,
                isFocusable: false,
                isLoading: false,
                hasMobileCards: true
            }
        }
    }
</script>

Selection

You can show a single selected row by passing the corresponding object to the selected prop. Additionally, adding the .sync 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
1 Jesse Simmons 2016-10-15 13:43:27 Male
2 John Jacobs 2016-12-15 06:00:53 Male
3 Tina Gilbert 2016-04-26 06:26:28 Female
4 Clarence Flores 2016-04-10 10:28:46 Male
5 Anne Lee 2016-12-06 14:38:38 Female
<template>
    <section>
        <button class="button field is-danger" @click="selected = null"
            :disabled="!selected">
            <b-icon icon="close"></b-icon>
            <span>Clear selected</span>
        </button>

        <b-tabs>
            <b-tab-item label="Table">
                <b-table
                    :data="data"
                    :columns="columns"
                    :selected.sync="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 .sync 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
1 Jesse Simmons 2016-10-15 13:43:27 Male
2 John Jacobs 2016-12-15 06:00:53 Male
3 Tina Gilbert 2016-04-26 06:26:28 Female
4 Clarence Flores 2016-04-10 10:28:46 Male
5 Anne Lee 2016-12-06 14:38:38 Female
Total checked: 2
<template>
    <section>
        <button class="button field is-danger" @click="checkedRows = []"
            :disabled="!checkedRows.length">
            <b-icon icon="close"></b-icon>
            <span>Clear checked</span>
        </button>

        <b-tabs>
            <b-tab-item label="Table">
                <b-table
                    :data="data"
                    :columns="columns"
                    :checked-rows.sync="checkedRows"
                    :is-row-checkable="(row) => row.id !== 3"
                    checkable>

                    <template slot="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,
                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>

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
30 Alan Mendoza 22/4/2016 Male
57 Alan Edwards 22/3/2017 Male
20 Albert Mendoza 8/8/2016 Male
45 Andrea Marshall 4/9/2016 Female
8 Andrew Greene 20/11/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">
                <button class="button" @click="currentPage = 2" :disabled="!isPaginated">Set page to 2</button>
            </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>
        </b-field>

        <b-table
            :data="data"
            :paginated="isPaginated"
            :per-page="perPage"
            :current-page.sync="currentPage"
            :pagination-simple="isPaginationSimple"
            :default-sort-direction="defaultSortDirection"
            default-sort="user.first_name"
            aria-next-label="Next page"
            aria-previous-label="Previous page"
            aria-page-label="Page"
            aria-current-label="Current page">

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

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

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

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

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

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

    export default {
        data() {
            return {
                data,
                isPaginated: true,
                isPaginationSimple: false,
                defaultSortDirection: 'asc',
                currentPage: 1,
                perPage: 5
            }
        }
    }
</script>

Detailed rows

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

New! 0.7.2

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

ID
First Name
Last Name
Date
Gender
1 Jesse Simmons 15/10/2016 Male

Jesse Simmons @Jesse 31m
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.

2 John Jacobs 15/12/2016 Male
3 Tina Gilbert 26/4/2016 Female
4 Clarence Flores 10/4/2016 Male
5 Anne Lee 6/12/2016 Female
<template>
    <section>

        <b-field grouped group-multiline>
            <div class="control">
                <b-switch v-model="showDetailIcon">Show detail icon</b-switch>
            </div>
        </b-field>

        <b-table
            :data="data"
            ref="table"
            paginated
            per-page="5"
            :opened-detailed="defaultOpenedDetails"
            detailed
            detail-key="id"
            @details-open="(row, index) => $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">

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

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

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

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

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

            <template slot="detail" slot-scope="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
            }
        },
        methods: {
            toggle(row) {
                this.$refs.table.toggleDetails(row)
            }
        }
    }
</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 Games 131 301 44%
    Monopoly 57 100 57%
    Scrabble 23 84 27%
    Chess 37 61 61%
    Battleships 14 56 25%
Books 434 721 60%
Jigsaws & Puzzles 88 167 53%
<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) => $toast.open(`Expanded ${row.name}`)"
            :show-detail-icon="showDetailIcon">

            <template slot-scope="props">
                <b-table-column
                    field="name"
                    :visible="columnsVisible['name'].display"
                    :label="columnsVisible['name'].title"
                    width="300"
                    sortable
                >
                    <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
                >
                    {{ props.row.sold }}
                </b-table-column>

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

                <b-table-column
                    :visible="columnsVisible['cleared'].display"
                    :label="columnsVisible['cleared'].title"
                    centered
                >
                    <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>

            <template slot="detail" slot-scope="props">
                <tr v-for="item in props.row.items">
                    <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
1 Jesse Simmons 2016-10-15 13:43:27 Male
2 John Jacobs 2016-12-15 06:00:53 Male
3 Tina Gilbert 2016-04-26 06:26:28 Female
4 Clarence Flores 2016-04-10 10:28:46 Male
5 Anne Lee 2016-12-06 14:38:38 Female
<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 you can customize the headers. Use the meta prop on column to pass anything you may need.

ID
FN
LN
Acc. Date
Gender
1 Jesse Simmons 2016-10-15 13:43:27 Male
2 John Jacobs 2016-12-15 06:00:53 Male
3 Tina Gilbert 2016-04-26 06:26:28 Female
4 Clarence Flores 2016-04-10 10:28:46 Male
5 Anne Lee 2016-12-06 14:38:38 Female
<template>
    <b-table :data="data" :columns="columns">
        <template slot-scope="props" slot="header">
            <b-tooltip :active="!!props.column.meta" :label="props.column.meta" dashed>
                {{ props.column.label }}
            </b-tooltip>
        </template>
    </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,
                        meta: 'Internal ID'
                    },
                    {
                        field: 'first_name',
                        label: 'FN',
                        meta: 'First Name'
                    },
                    {
                        field: 'last_name',
                        label: 'LN',
                        meta: 'Last Name'
                    },
                    {
                        field: 'date',
                        label: 'Acc. Date',
                        centered: true,
                        meta: 'Account created date'
                    },
                    {
                        field: 'gender',
                        label: 'Gender',
                        meta: ''
                    }
                ]
            }
        }
    }
</script>

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
1 Jesse Simmons 2016-10-15 13:43:27 Male
2 John Jacobs 2016-12-15 06:00:53 Male
3 Tina Gilbert 2016-04-26 06:26:28 Female
4 Clarence Flores 2016-04-10 10:28:46 Male
5 Anne Lee 2016-12-06 14:38:38 Female
<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">
            <template slot-scope="props">
                <b-table-column v-for="(column, index) in columnsTemplate"
                    :key="index"
                    :label="column.title"
                    :visible="column.visible">
                    {{ props.row[column.field] }}
                </b-table-column>
            </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,
                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>

Footer

ID
First Name
Last Name
Date
Gender
1 Jesse Simmons 15/10/2016 Male
2 John Jacobs 15/12/2016 Male
3 Tina Gilbert 26/4/2016 Female
4 Clarence Flores 10/4/2016 Male
5 Anne Lee 6/12/2016 Female
<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">

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

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

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

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

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

            <template slot="footer" v-if="!isCustom">
                <div class="has-text-right">
                    Footer
                </div>
            </template>
            <template slot="footer" 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>

        </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
Inception 8.3 21930 15/7/2010 Cobb, a skilled thief who commits corporate espionage by infiltrating the subcon...
Deadpool 7.6 19953 9/2/2016 Deadpool tells the origin story of former Special Forces operative turned mercen...
The Avengers 7.6 19391 25/4/2012 When an unexpected enemy emerges and threatens global safety and security, Nick ...
The Dark Knight 8.4 18705 16/7/2008 Batman raises the stakes in his war on crime. With the help of Lt. Jim Gordon an...
Avatar 7.4 18476 10/12/2009 In the 22nd century, a paraplegic Marine is dispatched to the moon Pandora on a ...
Interstellar 8.2 18372 5/11/2014 Interstellar chronicles the adventures of a group of explorers who make use of a...
Guardians of the Galaxy 7.9 17688 30/7/2014 Light years from Earth, 26 years after being abducted, Peter Quill finds himself...
Fight Club 8.4 16113 15/10/1999 A ticking-time-bomb insomniac and a slippery soap salesman channel primal male a...
Django Unchained 8 15526 25/12/2012 With the help of a German bounty hunter, a freed slave sets out to rescue his wi...
Iron Man 7.5 15488 30/4/2008 After being held captive in an Afghan cave, billionaire engineer Tony Stark crea...
Pulp Fiction 8.4 15037 10/9/1994 A burger-loving hit man, his philosophical partner, a drug-addled gangster's mol...
Forrest Gump 8.4 14584 6/7/1994 A man with a low IQ has accomplished great things in his life and been present d...
The Hunger Games 7.1 14447 12/3/2012 Every year in the ruins of what was once North America, the nation of Panem forc...
The Lord of the Rings: The Fellowship of the Ring 8.3 14305 18/12/2001 Young hobbit Frodo Baggins, after inheriting a mysterious ring from his uncle Bi...
Mad Max: Fury Road 7.4 14268 13/5/2015 An apocalyptic story set in the furthest reaches of our planet, in a stark deser...
The Matrix 8.1 14218 30/3/1999 Set in the 22nd century, The Matrix tells the story of a computer hacker who joi...
Titanic 7.8 14042 18/11/1997 101-year-old Rose DeWitt Bukater tells the story of her life aboard the Titanic,...
Iron Man 3 6.9 13960 18/4/2013 When Tony Stark's world is torn apart by a formidable terrorist called the Manda...
Jurassic World 6.6 13927 6/6/2015 Twenty-two years after the events of Jurassic Park, Isla Nublar now features a f...
The Dark Knight Rises 7.7 13787 16/7/2012 Following the death of District Attorney Harvey Dent, Batman assumes responsibil...
<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">

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

                <b-table-column field="vote_average" label="Vote Average" numeric sortable>
                    <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>
                     {{ props.row.vote_count }}
                </b-table-column>

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

                <b-table-column label="Overview" width="500">
                    {{ props.row.overview | truncate(80) }}
                </b-table-column>
            </template>
        </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.replace(/-/g, '/')
                            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'
                }
            }
        },
        filters: {
            /**
             * Filter to truncate string, accepts a length parameter
             */
            truncate(value, length) {
                return value.length > length
                    ? value.substr(0, length) + '...'
                    : value
            }
        },
        mounted() {
            this.loadAsyncData()
        }
    }
</script>

Draggable rows

Use draggable prop to allow rows to be draggable. Manage dragging using dragstart, dragover and drop events

ID
First Name
Last Name
Date
Gender
1 Jesse Simmons 2016-10-15 13:43:27 Male
2 John Jacobs 2016-12-15 06:00:53 Male
3 Tina Gilbert 2016-04-26 06:26:28 Female
4 Clarence Flores 2016-04-10 10:28:46 Male
5 Anne Lee 2016-12-06 14:38:38 Female
<template>
  <div>
    <b-table
      :data="data"
      :columns="columns"
      draggable
      @dragstart="dragstart"
      @drop="drop"
      @dragover="dragover"
      @dragleave="dragleave">
    </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
          }
      },
      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.$toast.open(`Moved ${this.draggingRow.first_name} from row ${this.draggingRowIndex + 1} to ${droppedOnRowIndex + 1}`)
        }
      }
  }
</script>

API

Table

Name
Description
Type
Values
Default
dataTable data Array<Object>
columnsTable columns, you can also add renderHtml: true on each column object Array<Object> (same as TableColumns props)
default-sortSets the default sort column and order — e.g. ['first_name', 'desc'] String, Array order: default-sort-direction prop
default-sort-directionSets the default sort column direction on the first click String asc, descasc
borderedBorder to all cells Boolean false
stripedWhether table is striped Boolean false
narrowedMakes the cells narrower Boolean false
selectedSet which row is selected, use the .sync modifier to make it two-way binding Object
focusableTable can be focused and user can navigate with keyboard arrows (require selected.sync) and rows are highlighted when hovering Boolean false
hoverableRows are highlighted when hovering Boolean false
checkableRows can be checked (multiple), checked rows will have a .is-checked class if you want to style Boolean false
checked-rowsSet which rows are checked, use the .sync modifier to make it two-way binding Array<Object>
mobile-cardsRows appears as cards on mobile (collapse rows) Boolean true
backend-sortingColumns won't be sorted with Javascript, use with sort event to sort in your backend Boolean false
backend-paginationRows won't be paginated with Javascript, use with page-change event to paginate in your backend Boolean false
totalTotal number of table data if backend-pagination is enabled Number 0
current-pageCurrent page of table data (if paginated), use the .sync modifier to make it two-way binding Number 1
loadingLoading state Boolean false
paginatedAdds pagination to the table Boolean false
pagination-simpleSimple pagination (if paginated) Boolean false
pagination-sizePagination size (if paginated) String is-small, is-medium, is-large
per-pageHow many rows per page (if paginated) Number 20
row-classAdd a class to row (<tr> element) based on the return Function (row: Object, index: Number)
detailedAllow row details (check scoped slots documentation) Boolean false
custom-detail-rowAllow a custom detail row Boolean false
show-detail-iconAllow chevron icon and column to be visible Boolean true
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
custom-is-checkedCustom method to verify if row is checked, works when is checkable. Useful for backend pagination Function (a: Object, b: Object)
is-row-checkableCustom method to verify if a row is disabled, works when is checkable. Function (row: Object) true
icon-packIcon pack to use String mdi, fa, fas, far, fad, falmdi
mobile-sort-placeholderText when nothing is selected String
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 draggable Boolean false
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

Column

Name
Description
Type
Values
Default
labelColumn header text, also used to identify column if custom-key prop is missing String
custom-keyUnique identifier, use when label is missing or there are duplicate label names String, Number this.label
fieldProperty of the object the column is attributed, used for sorting String
metaMeta prop to add anything, useful when creating custom headers Any
widthColumn fixed width in pixels Number
numericAlign the cell content to the right, sort icon on left Boolean false
centeredAlign the cell content to the center Boolean false
sortableWhether the column can be sorted Boolean false
visibleWhether the column is visible Boolean true
custom-sortCustom sort method, works when is sortable Function (a: Object, b: Object, isAsc: Boolean)

This page is open source. Noticed a typo or something's unclear?

Improve this page on GitHub