Skip to content

Migrating from UITable to HighRise DataTable

HighRise HLDataTable is built on TanStack Table and pairs with HLDataTableWrapper for header controls (search, column toggles, layouts, pagination container). API shape changes around columns, selection, search, and pagination.

Component Implementation Changes

Import Changes

diff
- import { UITable } from '@gohighlevel/ghl-ui'
+ import { HLDataTable, HLDataTableWrapper } from '@platform-ui/highrise'

Key Differences

  • Columns use id, header, accessorKey, and cellFormatter(row) instead of key / title / render.
  • Selection: checkedRowKeysselectedRows (array of IDs). rowUniqueId defaults to "id".
  • Search: use HLDataTableWrapper with show-header + show-global-search and handle @update:global-filter (no enableTextSearch).
  • Pagination: drive HLPagination from TanStack state (page size/index) inside the wrapper footer slot.
  • Empty slot: emptyno-data.
  • Reordering/resizing via props (rowReordering, columnReordering, columnResizing) and emits (update:row-reordered, update:column-order).

Props Mapping

ghl-ui Prop / PatternHighRise EquivalentNotes
columns: [{ key, title, render }]columns: [{ id, header, accessorKey, cellFormatter, sortingFn?, filterFn? }]header accepts string/VNode/fn; cellFormatter receives TanStack row
checkedRowKeysselectedRowsPair with rowUniqueId if your key is not id
enableTextSearchshow-header + show-global-search on wrapperEmits update:global-filter
@update:searchText@update:global-filter (wrapper)Receives search string
showSizePicker, pageSizes, page, pageCountDrive HLPagination manuallyUse TanStack state: table?.getState().pagination
empty slotno-data slotUse HLEmpty or custom content
Row click via @row-click@table-row-clickedEmits (rowData)

Events (commonly used)

  • HLDataTableWrapper: update:global-filter, update:column-checked, update:column-freeze, update:page-size, update:current-layout, update:table-layout, update:table-layout-as, update:column-reorder, update:table-height-selected-option.
  • HLDataTable: update:cell-value, update:column-order, update:row-reordered, table-row-clicked, scroll, no-data.

Examples

Basic DataTable

vue
<template>
  <HLDataTable
    :columns="columns"
    :data="rows"
    :row-height="'auto'"
    :selected-rows="selectedRows"
    row-unique-id="email"
    @table-row-clicked="handleRowClick"
  />
</template>

<script setup>
import { h, ref } from 'vue'
import { HLDataTable } from '@platform-ui/highrise'

const selectedRows = ref([])
const rows = [
  { email: '[email protected]', name: 'Alice' },
  { email: '[email protected]', name: 'Bob' },
]

const columns = [
  {
    id: 'name',
    header: 'Name',
    accessorKey: 'name',
    cellFormatter: row => row.row.original.name,
  },
  {
    id: 'email',
    header: () => h('span', { class: 'text-gray-500' }, 'Email'),
    accessorKey: 'email',
    cellFormatter: row => row.row.original.email,
  },
]

const handleRowClick = row => {
  console.log('clicked', row)
}
</script>

With Search and Pagination

vue
<template>
  <HLDataTableWrapper
    id="users-table"
    :show-header="true"
    :show-global-search="true"
    :responsive-column-width="true"
    max-width="100%"
    @update:global-filter="val => (globalFilter = val)"
  >
    <template #footer>
      <HLPagination
        :current-page="table?.getState().pagination.pageIndex + 1"
        :per-page="table?.getState().pagination.pageSize"
        :item-count="table?.getFilteredRowModel().rows.length || 0"
        :show-per-page-drop-down="true"
        @update:page="page => table?.setPageIndex(page - 1)"
        @update:per-page="pageSize => table?.setPageSize(pageSize)"
      />
    </template>

    <HLDataTable
      ref="tableRef"
      :columns="columns"
      :data="rows"
      :selected-rows="selectedRows"
      row-unique-id="id"
      @update:row-reordered="handleReorder"
    >
      <template #no-data>
        <HLEmpty description="No rows match the current filters" />
      </template>
    </HLDataTable>
  </HLDataTableWrapper>
</template>

<script setup>
import { ref, computed } from 'vue'
import { HLDataTable, HLDataTableWrapper, HLPagination, HLEmpty } from '@platform-ui/highrise'

const tableRef = ref(null)
const table = computed(() => tableRef.value?.table)
const selectedRows = ref([])
const globalFilter = ref('')
const rows = ref([])

const handleReorder = payload => {
  console.log('reordered rows', payload)
}
</script>

Best Practices

  1. Always include both id and accessorKey on columns; keep them unique.
  2. Keep row IDs in rowUniqueId aligned with your data shape; pass selectedRows using that key.
  3. Use HLDataTableWrapper to wire search/header controls; connect update:global-filter to your data source.
  4. Drive pagination from TanStack state; avoid mixing legacy page/pageCount props.
  5. Provide no-data and footer slots to keep empty states and pagination consistent.