BTable Migration
Migration notes for BTable from BootstrapVue to BootstrapVueNext.
BTable Migration
Summary
Migration notes for BTable from BootstrapVue to BootstrapVueNext.
Affected APIs
- BTable
- BTbody
- BThead
- BTfoot
- BTr
- BTh
- BTd
Breaking Change
See the v-html section for information on deprecation of the html prop.
The slot emptyfiltered has been renamed to empty-filtered for consistency.
The following features are not included in v1:
filter-included-fields and filter-ignored-fields have been replaced by a single filterable prop.
filter-debounce has been replaced by debounce.
no-sort-reset is deprecated. Use must-sort. By default, sortability can be reset by clicking (3) times [asc => desc => undefined => asc...]
selected-variant has been renamed to selection-variant for internal consistency.
Sorting has been significantly reworked. Read the sorting section of our documentation. Some specific changes include the following:
sort-changedevent is replaced by theupdate:sort-byevent.sort-directionhas been renamedinitial-sort-directionfor clarity.- The sort icons have been changed.
- The internal
sort-compareroutine has been simplified, if you need to customize sorting for localization, the documentation on custom sort comparers for details. multi-sortfunctionality has been implemented.
table-variant is replaced with variant for consistency.
The slot scope for table-colgroup slot now only contains the fields prop, with the columns prop removed.
BootstrapVue used the main v-model binding to expose a readonly version of the displayed items. This is deprecated. Instead, use the exposed function displayedItems as demonstrated in the documentation.
The semantics of the row-selected event have changed. row-selected is now emitted for each selected row and sends the single row's item as it's parameter. There is a new matching event called row-unselected that is emitted for each row that is unselected. There is also a named model selectedItems that behaves like the BSV row-selected event, emitting an array of all seleted rows. An example of this is available in the documentation
All row-level events (row-clicked, row-dblclicked, row-hovered, row-unhovered, row-contextmenu, row-middle-clicked) now emit a single payload object with {item, index, event} instead of positional arguments. The head-clicked event likewise now emits {key, field, event, isFooter} as one object payload.
BootstrapVue adds utility classes to the <table> including b-table-select-single,b-table-select-multi, and b-table-select-range, these have been deprecated, as the functionality should be easily replicated by the developer without adding to the API surface.
Helper components (BTbody, BThead, BTfoot, BTr, BTh, BTd) use semantic HTML elements that provide implicit ARIA roles. BTh automatically calculates the scope attribute based on colspan and rowspan props.
The filtered event has a single argument Item[] rather than two arguments with an array and length. The semantics haven't changed.
Heading and data row accessibility is implemented via keyboard navigation (tab and arrow keys for sortable headers and selectable rows) and proper semantic HTML structure.
Row Expansion (formerly Row Details)
Terminology changes: BootstrapVue used "details" terminology for expanding rows, which has been changed to "expansion" for clarity and linguistic correctness. The following changes have been made:
- Scoped slot variable
detailsShowingis nowexpansionShowing - Scoped slot function
toggleDetailsis nowtoggleExpansion - The concept of "detailed items" is now "expanded items"
v-model instead of object property: The expansion state is no longer tracked using a property on item objects. Instead, use the v-model:expanded-items binding to manage which rows are expanded.
Before (BootstrapVue):
| Name | Full Details |
|---|---|
| Item 1 | Full details for Item 1 |
| Item 2 | Full details for Item 2 |
<template>
<BTable :items="items">
<!-- @ts-expect-error - BSV pattern no longer supported -->
<template #cell(show_details)="row">
<!-- @ts-expect-error - BSV pattern no longer supported -->
<BButton @click="row.toggleDetails">
<!-- @ts-expect-error - BSV pattern no longer supported -->
{{ row.detailsShowing ? 'Hide' : 'Show' }} Details
</BButton>
</template>
<!-- @ts-expect-error - BSV slot name no longer supported -->
<template #row-details="row">
<BCard>{{ row.item.fullDetails }}</BCard>
</template>
</BTable>
</template>
<script setup lang="ts">
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck -- BSV example demonstrating deprecated patterns
const items = [
{name: 'Item 1', fullDetails: 'Full details for Item 1'},
{name: 'Item 2', fullDetails: 'Full details for Item 2'},
]
// Expansion was controlled via _showDetails property on items
</script>After (BootstrapVueNext):
| Name | Full Details |
|---|---|
| Item 1 | Full details for Item 1 |
Full details for Item 1 | |
| Item 2 | Full details for Item 2 |
<template>
<BTable v-model:expanded-items="expandedItems" :items="items">
<template #cell(show_details)="row">
<BButton @click="row.toggleExpansion">
{{ row.expansionShowing ? 'Hide' : 'Show' }} Details
</BButton>
</template>
<template #row-expansion="row">
<BCard>{{ row.item?.fullDetails }}</BCard>
</template>
</BTable>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const items = [
{ name: 'Item 1', fullDetails: 'Full details for Item 1' },
{ name: 'Item 2', fullDetails: 'Full details for Item 2' },
]
// Expansion state managed via v-model
const expandedItems = ref([items[0]]) // Expand first item by default
</script>Using with Primary Key:
When using a primary-key, expansion state persists across item array updates (like pagination or "Load more"). To set default expanded items with a primary-key, you must use the table's template ref expansion.get() function:
| Id | Name |
|---|---|
| 1 | Item 1 |
| 2 | Item 2 |
<template>
<BTable
ref="tableRef"
v-model:expanded-items="expandedItems"
:items="items"
primary-key="id"
>
<!-- ... -->
</BTable>
</template>
<script setup lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import {onMounted, ref} from 'vue'
const tableRef = ref()
const items = [
{id: 1, name: 'Item 1'},
{id: 2, name: 'Item 2'},
]
const expandedItems = ref<any[]>([])
// Set default expanded items after mount
onMounted(() => {
expandedItems.value.push(tableRef.value.expansion.get(items[1]))
})
</script>The slot name remains row-expansion (changed from row-details in earlier versions).
Template Ref API
BREAKING: Template ref API reorganized into namespaced structure
The BTable template ref API has been reorganized from a flat structure to a namespaced structure with expansion and selection properties. This improves organization and makes it clearer which methods and properties relate to which feature.
Selection API changes:
Methods and properties related to row selection are now accessed via ref.selection.*:
clearSelected()→selection.clearSelected()selectAll()→selection.selectAll()toggleSelectAll()→selection.toggleSelectAll()selectedItems→selection.selectedItems
Expansion API changes:
Methods and properties related to row expansion are now accessed via ref.expansion.*:
expandedItems→expansion.expandedItems- New methods available:
expansion.expandAll(),expansion.collapseAll(),expansion.toggleExpandAll()
Before (flat structure):
| Name |
|---|
| Item 1 |
| Item 2 |
<template>
<BTable
ref="tableRef"
:items="items"
selectable
>
<BButton @click="handleClearSelection">Clear Selection</BButton>
<BButton @click="handleSelectAll">Select All</BButton>
</BTable>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const tableRef = ref()
const items = [{name: 'Item 1'}, {name: 'Item 2'}]
const handleClearSelection = () => {
tableRef.value.clearSelected()
}
const handleSelectAll = () => {
tableRef.value.selectAll()
}
// Access selected items
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getSelectedItems = () => tableRef.value.selectedItems
</script>After (namespaced structure):
| Name |
|---|
| Item 1 |
| Item 2 |
<template>
<BTable
ref="tableRef"
:items="items"
selectable
>
<BButton @click="handleClearSelection">Clear Selection</BButton>
<BButton @click="handleSelectAll">Select All</BButton>
</BTable>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const tableRef = ref()
const items = [{name: 'Item 1'}, {name: 'Item 2'}]
const handleClearSelection = () => {
tableRef.value.selection.clearSelected()
}
const handleSelectAll = () => {
tableRef.value.selection.selectAll()
}
// Access selected items
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getSelectedItems = () => tableRef.value.selection.selectedItems
</script>Expansion API example:
| Name |
|---|
| Item 1 |
| Item 2 |
<template>
<BTable
ref="tableRef"
v-model:expanded-items="expandedItems"
:items="items"
>
<BButton @click="handleExpandAll">Expand All</BButton>
<BButton @click="handleCollapseAll">Collapse All</BButton>
</BTable>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const tableRef = ref()
const expandedItems = ref([])
const items = [{name: 'Item 1'}, {name: 'Item 2'}]
const handleExpandAll = () => {
tableRef.value.expansion.expandAll()
}
const handleCollapseAll = () => {
tableRef.value.expansion.collapseAll()
}
</script>Item Provider Functions
To use an items provider, set the provider prop to a provider function and leave the items prop undefined (unlike in BootstrapVue, where the items prop was overloaded). See our documentation for details.
The items provider function ctx parameter now contains sortBy array rather than sortBy and sortDesc fields - see the sorting docs for details
The table prop api-url and the items provider function ctx parameter apiUrl field are both deperecdated as they are easily replaced by direct management of the api call by the user.
The items provider no longer includes an optional callback parameter, use the async method of calling instead.
Field Definitions
BREAKING: field.key no longer supports nested paths
In BootstrapVue, the field.key property could be set to nested string paths like name.firstName to access nested properties. This is no longer supported. The key property must now be a simple string identifier used only for column identification and slot names.
New accessor property for data access
To access nested or computed data, use the new optional accessor property:
- For root-level properties: The
accessorcan be a string matching a root property name (e.g.,'email') - For nested or computed values: The
accessorshould be a function that receives the row item and returns the value - If omitted, the
keyproperty is used by default (for root-level properties only)
Before (BootstrapVue):
const fields = [
{ key: 'name.first', label: 'First Name' },
{ key: 'name.last', label: 'Last Name' },
{ key: 'age', label: 'Age' },
]After (BootstrapVueNext):
const fields = [
{
key: 'firstName',
label: 'First Name',
accessor: (item: any) => item.name.first,
},
{
key: 'lastName',
label: 'Last Name',
accessor: (item: any) => item.name.last,
},
{ key: 'age', label: 'Age' }, // Simple root property works as before
]BREAKING: Function signatures changed to use single parameter objects
The following TableField properties now accept a single parameter object instead of multiple positional parameters:
formatter: Now receives{value, key, item}instead of(value, key, item)tdAttr: Now receives{value, key, item}instead of(value, key, item)thAttr: Now receives{value, key, item, type}instead of(value, key, item, type)
Before (BootstrapVue):
import type { TableField } from 'bootstrap-vue-next'
const fields: TableField[] = [
{
key: 'status',
formatter: (value) => String(value.value).toUpperCase(),
tdAttr: (value) => ({
class: value.value === 'active' ? 'text-success' : '',
}),
},
]After (BootstrapVueNext):
const fields = [
{
key: 'status',
formatter: ({ value, key, item }: { value: any; key: any; item: any }) => value.toUpperCase(),
tdAttr: ({ value, key, item }: { value: any; key: any; item: any }) => ({
class: value === 'active' ? 'text-success' : '',
}),
},
]formatter Only the callback function value for this field is implemented, adding the name of a method in the component is deprecated.
sortKey and sortDirection are deprecated, use the table's sortBy model as documented here instead.
filterByFormatted is implemented, but does not take a format function as an argument.
Migration Notes
- Extracted from the canonical BootstrapVue → BootstrapVueNext migration guide.
- Review related migrations for shared prop, event, and slot changes.
Safe Automatic Rewrite
No. This entry includes behavioral or structural changes and should be reviewed manually before applying automated transforms.