Skip to content

Table

A responsive table component.

Prerequisites

We're going to build a table to show deployments, this is what our data looks like:

js
const rawData = [
  {
    domain: 'eihabkhan.com',
    status: 'building',
    branch: 'main',
  },
  {
    domain: 'supershop.youcan.shop',
    status: 'queued',
    branch: 'main',
  },
  {
    domain: 'vercel.com',
    status: 'ready',
    branch: 'main',
  },
  {
    domain: 'nextjs.org',
    status: 'error',
    branch: 'feat/next-conf',
  },
];

In order to work with the Table Component we have to reshape our data object so that it adheres to what the Table component is expecting, we can do that by creating a new data object that is of type TableData:

js
import type { TableData } from '@youcan/ui-vue3/dist/types/components/Table/types';

const data: TableData[] = rawData.map(item => {
  return {
    row: {
      domain: item.domain,
      status: item.status,
      branch: item.branch,
    },
  }
})

File Structure

Start by creating the following structure

text
/components
└── /deployments
    ├── helpers.ts
    └── DeploymentsTable.vue
  • helpers.ts: This is where you define your data, columns, or any other table configurations.
  • DeploymentsTable.vue: This is where you import and use the Table component, adding additional elements such as Pagination, Search, etc.

Basic Table

Let's start by building a basic table.

  1. Define Columns

    First, we'll define our columns.

    ts
    // components/deployments/helpers.ts
    import type { TableColumn } from '@youcan/ui-vue3/dist/types/components/Table/types';
    
    export const columns: TableColumn[] = [
      { label: 'Domain', accessor: 'domain' },
      { label: 'Status', accessor: 'status' },
      { label: 'Git Branch', accessor: 'branch' },
    ];
  2. <Table/> Component

    Next, we'll create a <Table /> component in DeploymentsTable.vue to render our table, we'll pass it our data and columns objects:

    vue
    <!-- components/deployments/DeploymentsTable.vue -->
    <script setup lang="ts">
      import { Table } from '@youcan/ui-vue3';
      import { columns } from './helpers.ts';
    
      const data = await fetchDeployments(); // Fetch data from API
    
      const deployments = data.map(item => {
        return {
          row: {
            domain: item.domain,
            status: item.status,
            branch: item.branch,
          },
        }
      });
    </script>
    <template>
      <Table 
        :columns="columns"
        :data="deployments"
      />
    </template>
  3. Render the Table

    Finally, we'll render the table in a desired page:

    vue
    <script setup lang="ts">
      import DeploymentsTable from '~components/deployments/DeploymentsTable.vue'
    </script>
    
    <template>
      <DeploymentsTable />
    </template>

Cell formatting

You can customize the way you want your data to be displayed inside of the <Table /> cells. You can do that by defining the variant you want to display in a given cell. The <Table /> cell supports a predefined list of variants:

  • string
  • number
  • boolean
  • TableDataComposable:
    • 'link'
    • 'thumbnail'
    • 'status'
    • 'static-status'
    • 'percentage'
    • 'button'
    • 'rating'
    • 'toggle'
    • 'counter'
    • 'input'

Using TableDataComposable requires you to handle the data property differently based on which composable you choose:

ComposableData
'link'{ label?: string, href: string }
'thumbnail'{ size: ThumbnailSize, src?: string, alt?: string }
'status'{ statuses: StatusDefinition[], modelValue: StatusDefinition }
'static-status'{ status: StaticStatusDefinition }
'percentage'{ percentage: number, type: PercentageType }
'button'{ label: string, iconName?: string, size?: ButtonSize, disabled?: boolean, iconPosition?: ButtonIconPosition, roundedFull?: boolean }
'rating'{ score: number, ceil?: number }
'toggle'{ modelValue: boolean }
'counter'{ modelValue: string, max?: number, min?: number, step?: number, id?: string, disabled?: boolean }
'input'TableDataNumberInput | TableDataGenericInput

Let's format the status cell to display color-coded statuses instead of plain text.

  1. Choose your variant

    Deside how you'd want to display your data. In our example, We'll use Static Status component to display color-coded statuses

  2. Define you static statuses

    vue
    <!-- components/deployments/DeploymentsTable.vue -->
    <script setup lang="ts">
      import { Table } from '@youcan/ui-vue3';
      import { columns } from './helpers.ts';
      import type { StaticStatusDefinition } from '@youcan/ui-vue3/dist/types/components/Status/types';
    
      const domainStatuses: { [k: string]: StaticStatusDefinition } = {
        ready: {
          color: '#52E2C0',
          label: 'Ready',
          labelColor: '#2d4236',
        },
        error: {
          color: '#EE0200',
          label: 'Error',
          labelColor: '#f8dcdc',
        },
        building: {
          color: '#ffc35c',
          label: 'Building',
          labelColor: '#574811',
        },
        queued: {
          color: '#999999',
          label: 'Queued',
          labelColor: '#fff2f2',
        },
      };
    
      const data = await fetchDeployments(); // Fetch data from API
      // ...
  3. Update your data definition

    Now that we defined our statuses, it's time to update our Data definition:

    vue
    <!-- components/deployments/DeploymentsTable.vue -->
    <script setup lang="ts">
      // ...
      const data = await fetchDeployments(); // Fetch data from API
    
      const deployments = data.map(item => {
        return {
          row: {
            domain: item.domain,
            status: {
              variant: 'static-status',
              data: {
                status: domainStatuses[item.status],
              },
            },
            branch: item.branch,
          },
        }
      });
    </script>
    <template>
      <Table 
        :columns="columns"
        :data="deployments"
      />
    </template>

Row Actions

Let's add row actions to our table.

  1. Define actions

    In the helpers.ts file, let's create an actions array of type TableActions:

    ts
    // components/deployments/helpers.ts
    import type { TableActions } from '@youcan/ui-vue3/dist/types/components/Table/types';
    
    const actions: TableActions[] = [
      {
        label: 'Redeploy',
        iconName: 'i-youcan-arrows-clockwise',
        tooltip: 'Redeploy',
        events: {
          click(row, index) {
            // Do something
          },
        },
      },
      {
        label: 'Copy URL',
        iconName: 'i-youcan-copy',
        tooltip: 'Copy URL',
        events: {
          click(row, index) {
            // Do something
          },
        },
      },
      {
        label: 'Source Code',
        iconName: 'i-youcan-code',
        tooltip: 'Source Code',
        events: {
          click(row, index) {
            // Do something
          },
        },
      },
    ];
  2. Pass actions to table

    Pass the actions you just created into the <Table /> component:

    vue
    <!-- components/deployments/DeploymentsTable.vue -->
    <script setup lang="ts">
      import { columns, actions } from './helpers.ts';
      // ...
    </script>
    <template>
      <Table 
        :columns="columns"
        :data="deployments"
        :actions="actions"
      />
    </template>

Sorting

Let's add sorting to our table:

  1. Define which columns are sortable

    The column object takes a sortable property which supports one of three values: none, desc, and asc:

    ts
    // components/deployments/helpers.ts
    import type { TableColumn } from '@youcan/ui-vue3/dist/types/components/Table/types';
    
    export const columns: TableColumn[] = [
      { label: 'Domain', accessor: 'domain', sortable: 'desc' },
      { label: 'Status', accessor: 'status' },
      { label: 'Git Branch', accessor: 'branch' },
    ];
  2. Add your sort logic to the <Table />

    Inside of DeploymentsTable.vue define your sorting logic:

    vue
    <!-- components/deployments/DeploymentsTable.vue -->
    <script setup lang="ts">
    // ... 
    const handleSort = (column: TableColumn) => {
      data.value = data.value.sort((a, b) => {
        const textA = (a.row[column.accessor] as string).toUpperCase();
        const textB = (b.row[column.accessor] as string).toUpperCase();
    
        if (textA < textB) {
          return -1;
        }
        if (textA > textB) {
          return 1;
        }
    
        return 0;
      });
    };
    </script>
    <template>
      <Table 
        :columns="columns"
        :data="deployments"
        :actions="actions"
        @sort="handleSort"
      />
    </template>

Row Selection

Let's add the ability to select rows to our table:

  1. Add the selected rows ref

    vue
    <script setup lang="ts">
    import type { TableData } from '@youcan/ui-vue3/dist/types/components/Table/types';
    // ...
    const selectedRows = ref<TableData[]>([]);
    </script>
  2. Add selection handler

    vue
    <script setup lang="ts">
    import type { TableData } from '@youcan/ui-vue3/dist/types/components/Table/types';
    // ...
    const selectedRows = ref<TableData[]>([]);
    
    const updateSelectedRows = (data: TableData[]) => {
      selectedRows.value = data;
    };
    </script>
  3. Update <Table />

    vue
    <template>
      <Table
        :columns="columns"
        :data="data"
        :actions="actions"
        :selectable="true"
        :selected-rows="selectedRows"
        @sort="handleSort"
        @update:selected-rows="updateSelectedRows"
      />
    </template>

Children

You can add child-rows to your table by updating your data object

vue
<!-- components/deployments/DeploymentsTable.vue -->
<script setup lang="ts">
  // ...
  const data = await fetchDeployments(); // Fetch data from API

  const deployments = data.map(item => {
    return {
      row: {
        domain: item.domain,
        status: {
          variant: 'static-status',
          data: {
            status: domainStatuses[item.status],
          },
        },
        branch: item.branch,
      },
      children: item.children // type: TableDataRow[]
    }
  });
</script>
<template>
  <Table 
    :columns="columns"
    :data="deployments"
    :actions="actions"
  />
</template>

API Reference