Skip to content

Table

Show design guidelines

Use certara-table to easily build out static tables with support for search, sort, and pagination. This component is built off of Grid.js.

As a cautionary note, this component is best used for static table. If the data within the table changes frequently (by user action, or async request, etc.) this component may not be the most performant. This is due to the way Grid.js re-renders the table when the data changes. In order to change the data in a cell, the entire table needs to be re-rendered to calculate the height and width of the table and all of its cells. The best use case for this component is a drop in replacement for datatables.

HTML React Angular
Study Project Licenses Actions
ABC-123 ABC 2 Edit
ABC-456 ABC 10 Edit
DEF-123 DEF 102 Edit
<certara-table full-height="false" sort="true">
<table>
<thead>
<tr>
<th>Study</th>
<th>Project</th>
<th>Licenses</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>ABC-123</td>
<td>ABC</td>
<td>2</td>
<td>
<certara-link href="#">Edit</certara-link>
</td>
</tr>
<tr>
<td>ABC-456</td>
<td>ABC</td>
<td>10</td>
<td>
<certara-link href="#">Edit</certara-link>
</td>
</tr>
<tr>
<td>DEF-123</td>
<td>DEF</td>
<td>102</td>
<td>
<certara-link href="#">Edit</certara-link>
</td>
</tr>
</tbody>
</table>
</certara-table>
import { CertaraLink, CertaraTable } from '@certara/certara-ui-react';
<CertaraTable fullHeight={false} sort={true}>
<table>
<thead>
<tr>
<th>Study</th>
<th>Project</th>
<th>Licenses</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>ABC-123</td>
<td>ABC</td>
<td>2</td>
<td>
<CertaraLink href="#">Edit</CertaraLink>
</td>
</tr>
<tr>
<td>ABC-456</td>
<td>ABC</td>
<td>10</td>
<td>
<CertaraLink href="#">Edit</CertaraLink>
</td>
</tr>
<tr>
<td>DEF-123</td>
<td>DEF</td>
<td>102</td>
<td>
<CertaraLink href="#">Edit</CertaraLink>
</td>
</tr>
</tbody>
</table>
</CertaraTable>
// In your module (e.g., app.module.ts)
import { CertaraUiAngularModule } from '@certara/certara-ui-angular';
@NgModule({
imports: [CertaraUiAngularModule]
})
// In your template:
<certara-table full-height="false" sort="true">
<table>
<thead>
<tr>
<th>Study</th>
<th>Project</th>
<th>Licenses</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>ABC-123</td>
<td>ABC</td>
<td>2</td>
<td>
<certara-link href="#">Edit</certara-link>
</td>
</tr>
<tr>
<td>ABC-456</td>
<td>ABC</td>
<td>10</td>
<td>
<certara-link href="#">Edit</certara-link>
</td>
</tr>
<tr>
<td>DEF-123</td>
<td>DEF</td>
<td>102</td>
<td>
<certara-link href="#">Edit</certara-link>
</td>
</tr>
</tbody>
</table>
</certara-table>

It’s possible to add actions to the table, such as print, copy, and download, by utilizing the right-actions slot. The certara-table component comes equipped with printTable, copyTableToClipboard, and downloadTable methods that can be called to perform these common actions. Below is a simple example of how these actions can be added to the table in React, although the same principles apply to other frameworks, including vanilla javascript.

CertaraTableActionsExample.jsx
import {useRef} from 'react';
import {CertaraButton, CertaraTable} from '@certara/certara-ui-react';
import {faker} from '@faker-js/faker';
const data = Array(3)
.fill()
.map(() => ({
id: faker.string.uuid(),
name: faker.person.fullName(),
address: faker.location.streetAddress(),
city: faker.location.city(),
state: faker.location.state(),
}));
const columns = [
{
name: 'Name',
data: (row) => row.name,
search: true,
copy: true,
download: true,
},
{
name: 'Address',
data: (row) => row.address,
search: true,
copy: true,
download: true,
},
{
name: 'City',
data: (row) => row.city,
search: true,
copy: true,
download: true,
},
{
name: 'State',
data: (row) => row.state,
search: true,
copy: true,
download: true,
},
];
export function CertaraTableActionsExample() {
const tableRef = useRef(null);
return (
<CertaraTable id="certara-table-actions-example" ref={tableRef} data={data} columns={columns} fullHeight={false} resizable sort search>
<div slot="right-actions">
<div className="btn-group" role="group">
<CertaraButton
leftIcon="print"
size="sm"
color="secondary"
onCertaraClick={() => {
tableRef.current.printTable();
}}>
Print
</CertaraButton>
<CertaraButton
leftIcon="copy"
size="sm"
color="secondary"
onCertaraClick={() => {
tableRef.current.copyTableToClipboard();
}}>
Copy
</CertaraButton>
<CertaraButton
leftIcon="download"
size="sm"
color="secondary"
onCertaraClick={() => {
tableRef.current.downloadTable();
}}>
Download
</CertaraButton>
</div>
</div>
</CertaraTable>
);
}

The columns prop is typed as TableColumns ((string | TableColumn)[]) in @certara/certara-ui — see packages/certara-ui/src/components/table/table-interface.ts.

FormWhen to useNotes
Array of strings e.g. ['Name', 'Email']Simple grids; data is often an array of arrays in the same column orderHeader text only. No per-column copy / download flags.
Array of TableColumn objectsObject-shaped data, formatters, sort/search, or copy/downloadUses Grid.js options (id, name, data, formatter, …). Certara adds copy and download: set to true to include the column in clipboard / CSV actions, or pass (cellData) => string to control the exported value.

Full column options follow Grid.js column configuration. The generated API table under Properties reflects the Stencil prop; this section describes the TypeScript shape in more detail.

To configure the table actions, you can set the copy, download properties on the column object. When these properties are set to true, the column data will be captured for the action. Sometimes you may need to format the data before capturing it. To do this, you can provide a callback function to the property. The callback function will receive the cell data as an argument and should return the formatted data. The printTable method takes a snapshot of the table view for printing; it does not use copy / download column flags.

When you use <CertaraTable> from @certara/certara-ui-react, you typically drive the grid with data and columns props instead of putting a static <table> like the above examples.

Grid.js is bundled with Certara UI. Do not install gridjs or gridjs-react in your app — import what you need from our packages so your version stays aligned with <certara-table>:

NeedImport from
React JSX in cell formatters (_)@certara/certara-ui-react (uses the same Grid.js runtime as <certara-table>)
HTML cells (html), Preact VDOM (h), Grid, plugins, etc.@certara/certara-ui (also re-exported from @certara/certara-ui-react)
import { CertaraTable, CertaraBadge, _ } from '@certara/certara-ui-react';

To render React components inside cells, use a column formatter and wrap your JSX with the _() helper:

ExampleTable
import {faker} from '@faker-js/faker';
import {CertaraTable, CertaraButton, CertaraBadge, CertaraDropdown, CertaraDropdownItem, CertaraIcon, CertaraDropdownCheckbox, _} from '@certara/certara-ui-react';
const data = Array(25)
.fill()
.map(() => ({
id: faker.string.uuid(),
name: faker.person.fullName(),
status: faker.helpers.arrayElement(['active', 'draft']),
}));
const columns = [
{
name: 'Name',
data: (row) => row.name,
search: true,
},
{
name: 'Status',
data: (row) => row.status,
formatter: (cell, row) => _(<CertaraBadge key={`status-${row.id}`}>{cell === 'active' ? 'Active' : 'Draft'}</CertaraBadge>),
search: true,
},
{
name: 'Actions',
sort: false,
formatter: (cell, row) =>
_(
<CertaraDropdown buttonVariant="link" showOpenerCaret={false}>
<CertaraIcon name="ellipsis" variant="solid" size="18" slot="button-label" aria-label="Actions" />
<CertaraDropdownItem
value="open"
onCertaraDropdownItemClick={() => {
// handle open
}}>
Open
</CertaraDropdownItem>
<CertaraDropdownItem
value="delete"
className="text-danger"
onCertaraDropdownItemClick={() => {
// handle delete
}}>
Delete
</CertaraDropdownItem>
</CertaraDropdown>,
),
},
];
export function ExampleTable() {
return (
<CertaraTable data={data} columns={columns} fullHeight={false} resizable sort search pagination={{limit: 10}}>
<div slot="left-actions">
<CertaraButton
leftIcon="plus"
size="sm"
onCertaraClick={() => {
// handle create
}}>
Create
</CertaraButton>
</div>
<div slot="filters">
<CertaraIcon name="filter" variant="solid" className="text-gray-300" size="14" />
<CertaraDropdown buttonSize="sm">
<span slot="button-label">Status</span>
<CertaraDropdownCheckbox
checked={false}
onCertaraChange={(e) => {
// handle custom filter change
}}>
Draft
</CertaraDropdownCheckbox>
<CertaraDropdownCheckbox
checked={false}
onCertaraChange={(e) => {
// handle custom filter change
}}>
Active
</CertaraDropdownCheckbox>
</CertaraDropdown>
</div>
</CertaraTable>
);
}

Grid.js uses Preact to render cell content. _ is the React/Preact bridge that lets you return JSX from a formatter. See the Grid.js docs for more information.

When applying custom jsx rendering in certara-table like the above example, there are two different “pieces” to consider: the presentation, and the searchable data.

What the user see’s is based on what the column’s formatter function returns, whether it is jsx, string, or anything else. This isn’t necissarily “the data inside of the cell” from a conceptual standpoint. That means, what is returned from the formatter function isn’t sortable, or searchable, it is purly presentation.

The data layer, or what is returned from the column’s data function, is what is used for sorting and searching. If you’d like your fancy react columns to have some specific, sortable and searchable data, you’ll need to return that from the column’s data function.

For an advanced usage example, see the TFL’s project

While data loads, show certara-skeleton-table in an absolutely positioned layer over the same area where the table will appear. Give the wrapper position-relative and a min-height so the skeleton has space before the real table mounts.

CertaraTableLoadingExample.jsx
import {useCallback, useState} from 'react';
import {CertaraButton, CertaraTable, CertaraSkeletonTable} from '@certara/certara-ui-react';
/** Simulates an API that returns table rows after a short delay */
function fetchRows() {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{id: '1', name: 'Alice Chen', role: 'Admin'},
{id: '2', name: 'Bob Smith', role: 'Editor'},
{id: '3', name: 'Carol Jones', role: 'Viewer'},
]);
}, 1200);
});
}
const columns = [
{name: 'Name', data: (row) => row.name, search: true},
{name: 'Role', data: (row) => row.role, search: true},
];
export function CertaraTableLoadingExample() {
const [rows, setRows] = useState(columns);
const [loading, setLoading] = useState(false);
const loadData = useCallback(() => {
setLoading(true);
fetchRows().then((data) => {
setRows(data);
setLoading(false);
});
}, []);
return (
<div>
<div className="mb-2">
<CertaraButton size="sm" disabled={loading} onCertaraClick={loadData}>
Reload data
</CertaraButton>
</div>
<div className="position-relative" style={{minHeight: '200px'}}>
{loading && (
<div>
<CertaraSkeletonTable className="position-absolute w-100 h-100" />
</div>
)}
{!loading && rows.length > 0 && (
<div className="pointer-events-auto animation-duration-500 fadeIn">
<CertaraTable data={rows} columns={columns} fullHeight={false} sort search />
</div>
)}
</div>
</div>
);
}

autoRefresh

Attributeauto-refresh
Typeboolean
Defaulttrue
Requiredfalse
DescriptionAutomatically refresh the table when the data prop changes

bordered

Attributebordered
Typeboolean
Defaultfalse
Requiredfalse
DescriptionPuts borders between table data cells

columns

Attributeundefined
Type(string | TableColumn)[]
Defaultundefined
Requiredfalse
DescriptionColumn names (strings) or column config objects. See https://gridjs.io/docs/config/columns. Set with JS when the component is used in an HTML context. \nAdditionally, copy and download properties on the column object can be set to true or a callback function (cellData) => string for the copyTableToClipboard and downloadTable methods.

data

Attributeundefined
Typeany[]
Defaultundefined
Requiredfalse
DescriptionCan be an array of arrays, or an array of objects. This needs to be set with JS when the component is in an HTML context

fixedHeader

Attributefixed-header
Typeboolean
Defaultfalse
Requiredfalse
DescriptionFixed headers also require a set height

from

Attributeundefined
TypeHTMLTableElement
Defaultundefined
Requiredfalse
DescriptionReference to an existing HTML <table> element to use as the data source. GridJS will read the data from this table. Alternatively, place a <table> element as a child of <certara-table> and it will be auto-detected. See: https://gridjs.io/docs/examples/from

fullHeight

Attributefull-height
Typeboolean
Defaulttrue
Requiredfalse
DescriptionThe table container will stretch to the full height of the viewport

height

Attributeheight
Typestring
Defaultundefined
Requiredfalse
DescriptionSet height with CSS units (e.g. 400px)

offsetY

Attributeoffset-y
Typestring
Defaultundefined
Requiredfalse
DescriptionOffset to stretch table to fill available vertical space aside from reserved space for toolbars, etc. (e.g., 230px)

pagination

Attributepagination
Typeboolean | object
Defaultundefined
Requiredfalse
DescriptionSet to true to show pagination, configure extended options with the JS object (Docs)

paginationSummaryOnly

Attributepagination-summary-only
Typeboolean
Defaultfalse
Requiredfalse
DescriptionWhen pagination is enabled, show only the footer summary (record counts) and hide page navigation (.gridjs-pages: numbered pages and prev/next).

resizable

Attributeresizable
Typeboolean
Defaultfalse
Requiredfalse
DescriptionAllows columns to be resized
Attributesearch
Typeboolean
Defaultfalse
Requiredfalse
DescriptionShows a built-in table filter search field

searchPlaceholder

Attributesearch-placeholder
Typestring
Default'Search'
Requiredfalse
DescriptionPlaceholder text for built-in table search input

sort

Attributesort
Typeboolean
Defaultfalse
Requiredfalse
DescriptionAllows simple sorting on columns. More complex data needs to have custom rules in the columns.sort.compare object

tableClassName

Attributetable-class-name
Typestring
Defaultundefined
Requiredfalse
DescriptionAdds a className specifically to the rendered <table> element vs. the component wrapper

There are no events available for this component

NameSignatureDescription
copyTableToClipboard({ columnDelimiter, rowDelimiter }?: { columnDelimiter?: string; rowDelimiter?: string; }) => Promise<void>
downloadTable(filename: string) => Promise<void>
printTable({ pageTitle, css }?: { pageTitle?: string; css?: string; }) => Promise<void>
refresh() => Promise<void>Force a re-render of the table
NameDescription
Default slot for an optional HTML <table> element to use as the data source
filtersSlot for center-aligned toolbar content
left-actionsSlot for left-aligned toolbar content
right-actionsSlot for right-aligned toolbar content