Documentation
Empty State
Customize what users see when the table has no rows with tableEmptyStateRenderer.
Custom empty state
Pass tableEmptyStateRenderer to replace the blank body when rows is empty (no data or filters match nothing). In Angular, put an stEmpty template inside the table tag instead.
React TSX
Copy
const EmptyState = () => (<div style={{ padding: 48, textAlign: "center", color: "#6b7280" }}>No data available</div>);<SimpleTablecolumns={columns}rows={[]}tableEmptyStateRenderer={<EmptyState />}/>
Angular
Copy
<simple-table [columns]="columns" [rows]="[]"><ng-template stEmpty><div style="padding: 48px; text-align: center; color: #6b7280">No data available</div></ng-template></simple-table>
Vue SFC
Copy
import { h } from "vue";const emptyState = h("div",{ style: { padding: "48px", textAlign: "center", color: "#6b7280" } },"No data available",);<SimpleTable:columns="columns":rows="[]":table-empty-state-renderer="emptyState"/>
Svelte
Copy
<!-- EmptyState.svelte --><div style="padding: 48px; text-align: center; color: #6b7280">No data available</div><SimpleTable{columns}rows={[]}tableEmptyStateRenderer={EmptyState}/>
Solid TSX
Copy
const EmptyState = () => (<div style={{ padding: "48px", "text-align": "center", color: "#6b7280" }}>No data available</div>);<SimpleTablecolumns={columns}rows={[]}tableEmptyStateRenderer={<EmptyState />}/>
TypeScript
Copy
const empty = document.createElement("div");empty.style.cssText = "padding:48px;text-align:center;color:#6b7280";empty.textContent = "No data available";new SimpleTableVanilla(container, {columns,rows: [],tableEmptyStateRenderer: empty,});
Loading vs empty
While fetching data, use isLoading instead — the empty renderer only applies when there are no rows and loading is off.
Example
An empty table with a custom message. Use Code or StackBlitz for the full example.
React TSX
Copy
1import {SimpleTable} from "@simple-table/react";import type { Theme } from "@simple-table/react";2import { emptyStateConfig } from "./empty-state.demo-data";3import "@simple-table/react/styles.css";45const EmptyIcon = () => (6 <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" strokeWidth="1.5">7 <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7" />8 <path d="M16 3H8L3 7h18l-5-4z" />9 <line x1="10" y1="12" x2="14" y2="12" />10 </svg>11);1213const tableEmptyState = (14 <div15 style={{16 display: "flex",17 flexDirection: "column",18 alignItems: "center",19 justifyContent: "center",20 padding: "48px 24px",21 color: "#64748b",22 gap: 1223 }}24 >25 <EmptyIcon />26 <div style={{ fontSize: 16, fontWeight: 600 }}>No data available</div>27 <div style={{ fontSize: 13 }}>Try adjusting your filters or adding new records.</div>28 </div>29);3031const EmptyStateDemo = ({32 height = "400px",33 theme34}: {35 height?: string | number;36 theme?: Theme;37}) => {38 return (39 <SimpleTable40 columns={emptyStateConfig.headers}41 rows={emptyStateConfig.rows}42 tableEmptyStateRenderer={tableEmptyState}43 height={height}44 theme={theme}45 getRowId={({ row }) => row.id}46 />47 );48};4950export default EmptyStateDemo;
Angularempty-state-demo.component.ts
Copy
1import { Component, Input } from "@angular/core";2import { SimpleTableImports } from "@simple-table/angular";3import type { AngularColumnDef, GetRowIdParams, Theme } from "@simple-table/angular";4import { emptyStateConfig } from "./empty-state.demo-data";5import "@simple-table/angular/styles.css";6import type { EmptyEmployee } from "./empty-state.demo-data";78@Component({9 selector: "empty-state-demo",10 standalone: true,11 imports: [SimpleTableImports],12 template: `13 <simple-table14 [getRowId]="getRowId"15 [rows]="rows"16 [columns]="headers"17 [height]="height"18 [theme]="theme"19 >20 <ng-template stEmpty>21 <div22 style="display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;color:#64748b;gap:12px;"23 >24 <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5">25 <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7" />26 <path d="M16 3H8L3 7h18l-5-4z" />27 <line x1="10" y1="12" x2="14" y2="12" />28 </svg>29 <div style="font-size:16px;font-weight:600;">No data available</div>30 <div style="font-size:13px;">Try adjusting your filters or adding new records.</div>31 </div>32 </ng-template>33 </simple-table>34 `,35})36export class EmptyStateDemoComponent {37 @Input() height: string | number = "400px";38 @Input() theme?: Theme;3940 readonly rows: EmptyEmployee[] = emptyStateConfig.rows;41 readonly headers: AngularColumnDef<EmptyEmployee>[] = emptyStateConfig.headers;4243 getRowId = ({ row }: GetRowIdParams<EmptyEmployee>) => row.id;44}454647// empty-state.demo-data.ts48// Self-contained demo table setup for this example.49import type { AngularColumnDef } from "@simple-table/angular";5051export interface EmptyEmployee {52 id: number;53 name: string;54 email: string;55 role: string;56 department: string;57}5859export const emptyStateData: EmptyEmployee[] = [];6061export const emptyStateHeaders: AngularColumnDef<EmptyEmployee>[] = [62 { accessor: "id", label: "ID", width: 60, type: "number" },63 { accessor: "name", label: "Name", width: 180, type: "string" },64 { accessor: "email", label: "Email", width: 220, type: "string" },65 { accessor: "role", label: "Role", width: 140, type: "string" },66 { accessor: "department", label: "Department", width: 150, type: "string" },67];6869export const emptyStateConfig = {70 headers: emptyStateHeaders,71 rows: emptyStateData,72};73
Vue SFC
Copy
1<script setup lang="ts">2import { h } from "vue";3import { SimpleTable } from "@simple-table/vue";4import type { Theme, GetRowIdParams } from "@simple-table/vue";5import { emptyStateConfig } from "./empty-state.demo-data";6import type { EmptyEmployee } from "./empty-state.demo-data";7import "@simple-table/vue/styles.css";89const getRowId = ({ row }: GetRowIdParams<EmptyEmployee>) => row.id;1011const props = withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {12 height: "400px",13});1415const tableEmptyStateRenderer = h(16 "div",17 {18 style: {19 display: "flex",20 flexDirection: "column",21 alignItems: "center",22 justifyContent: "center",23 padding: "48px 24px",24 color: "#64748b",25 gap: "12px",26 },27 },28 [29 h(30 "svg",31 {32 width: 48,33 height: 48,34 viewBox: "0 0 24 24",35 fill: "none",36 stroke: "#94a3b8",37 strokeWidth: "1.5",38 },39 [40 h("path", { d: "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7" }),41 h("path", { d: "M16 3H8L3 7h18l-5-4z" }),42 h("line", { x1: "10", y1: "12", x2: "14", y2: "12" }),43 ],44 ),45 h("div", { style: { fontSize: "16px", fontWeight: "600" } }, "No data available"),46 h(47 "div",48 { style: { fontSize: "13px" } },49 "Try adjusting your filters or adding new records.",50 ),51 ],52);53</script>5455<template>56 <SimpleTable57 :columns="emptyStateConfig.headers"58 :rows="emptyStateConfig.rows"59 :get-row-id="getRowId"60 :table-empty-state-renderer="tableEmptyStateRenderer"61 :height="props.height"62 :theme="props.theme"63 />64</template>
SvelteEmptyStateDemo.svelte
Copy
1<script lang="ts">2 import { SimpleTable } from "@simple-table/svelte";3 import type { Theme, GetRowIdParams } from "@simple-table/svelte";4 import { emptyStateConfig } from "./empty-state.demo-data";5 import type { EmptyEmployee } from "./empty-state.demo-data";6 import TableEmptyState from "./TableEmptyState.svelte";7 import "@simple-table/svelte/styles.css";89 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();1011 const getRowId = ({ row }: GetRowIdParams<EmptyEmployee>) => row.id;12</script>1314<SimpleTable15 columns={emptyStateConfig.headers}16 rows={emptyStateConfig.rows}17 getRowId={getRowId}18 tableEmptyStateRenderer={TableEmptyState}19 {height}20 {theme}21/>222324// TableEmptyState.svelte25<div26 style="display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 48px 24px; color: #64748b; gap: 12px;"27>28 <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5">29 <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7" />30 <path d="M16 3H8L3 7h18l-5-4z" />31 <line x1="10" y1="12" x2="14" y2="12" />32 </svg>33 <div style="font-size: 16px; font-weight: 600;">No data available</div>34 <div style="font-size: 13px;">Try adjusting your filters or adding new records.</div>35</div>36
Solid TSX
Copy
1import {SimpleTable} from "@simple-table/solid";import type { Theme } from "@simple-table/solid";2import { emptyStateConfig } from "./empty-state.demo-data";3import "@simple-table/solid/styles.css";45const EmptyIcon = () => (6 <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5">7 <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7" />8 <path d="M16 3H8L3 7h18l-5-4z" />9 <line x1="10" y1="12" x2="14" y2="12" />10 </svg>11);1213export default function EmptyStateDemo(props: { height?: string | number; theme?: Theme }) {14 return (15 <SimpleTable16 columns={emptyStateConfig.headers}17 getRowId={({ row }) => row.id}18 rows={emptyStateConfig.rows}19 height={props.height ?? "400px"}20 theme={props.theme}21 tableEmptyStateRenderer={22 <div style={{23 display: "flex",24 "flex-direction": "column",25 "align-items": "center",26 "justify-content": "center",27 padding: "48px 24px",28 color: "#64748b",29 gap: "12px",30 }}>31 <EmptyIcon />32 <div style={{ "font-size": "16px", "font-weight": "600" }}>No data available</div>33 <div style={{ "font-size": "13px" }}>Try adjusting your filters or adding new records.</div>34 </div>35 }36 />37 );38}
TypeScriptEmptyStateDemo.ts
Copy
1import { SimpleTableVanilla } from "simple-table-core";2import type { Theme, GetRowIdParams } from "simple-table-core";3import { emptyStateConfig } from "./empty-state.demo-data";4import type { EmptyEmployee } from "./empty-state.demo-data";5import "simple-table-core/styles.css";67const getRowId = ({ row }: GetRowIdParams<EmptyEmployee>) => row.id;89function buildEmptyStateElement(): HTMLElement {10 const root = document.createElement("div");11 Object.assign(root.style, {12 display: "flex",13 flexDirection: "column",14 alignItems: "center",15 justifyContent: "center",16 padding: "48px 24px",17 color: "#64748b",18 gap: "12px",19 });2021 const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");22 svg.setAttribute("width", "48");23 svg.setAttribute("height", "48");24 svg.setAttribute("viewBox", "0 0 24 24");25 svg.setAttribute("fill", "none");26 svg.setAttribute("stroke", "#94a3b8");27 svg.setAttribute("stroke-width", "1.5");2829 for (const d of [30 "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7",31 "M16 3H8L3 7h18l-5-4z",32 ]) {33 const path = document.createElementNS("http://www.w3.org/2000/svg", "path");34 path.setAttribute("d", d);35 svg.appendChild(path);36 }37 const line = document.createElementNS("http://www.w3.org/2000/svg", "line");38 line.setAttribute("x1", "10");39 line.setAttribute("y1", "12");40 line.setAttribute("x2", "14");41 line.setAttribute("y2", "12");42 svg.appendChild(line);4344 const title = document.createElement("div");45 Object.assign(title.style, { fontSize: "16px", fontWeight: "600" });46 title.textContent = "No data available";4748 const subtitle = document.createElement("div");49 Object.assign(subtitle.style, { fontSize: "13px" });50 subtitle.textContent = "Try adjusting your filters or adding new records.";5152 root.append(svg, title, subtitle);53 return root;54}5556export function renderEmptyStateDemo(57 container: HTMLElement,58 options?: { height?: string | number; theme?: Theme },59): SimpleTableVanilla<EmptyEmployee> {60 return new SimpleTableVanilla(container, {61 getRowId,62 columns: emptyStateConfig.headers,63 rows: emptyStateConfig.rows,64 height: options?.height ?? "400px",65 theme: options?.theme,66 tableEmptyStateRenderer: buildEmptyStateElement(),67 });68}697071// empty-state.demo-data.ts72// Self-contained demo table setup for this example.73import type { ColumnDef } from "simple-table-core";7475export interface EmptyEmployee {76 id: number;77 name: string;78 email: string;79 role: string;80 department: string;81}8283export const emptyStateData: EmptyEmployee[] = [];8485export const emptyStateHeaders: ColumnDef<EmptyEmployee>[] = [86 { accessor: "id", label: "ID", width: 60, type: "number" },87 { accessor: "name", label: "Name", width: 180, type: "string" },88 { accessor: "email", label: "Email", width: 220, type: "string" },89 { accessor: "role", label: "Role", width: 140, type: "string" },90 { accessor: "department", label: "Department", width: 150, type: "string" },91];9293export const emptyStateConfig = {94 headers: emptyStateHeaders,95 rows: emptyStateData,96};97
Props
Empty State Configuration
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
tableEmptyStateRendererReactNode | HTMLElement | string | null | Optional | Custom content shown in the table body when there are no rows. Framework adapters accept components or elements; vanilla accepts a string or DOM node. In Angular, prefer an stEmpty template on the table. |