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).
React TSX
Copy
const EmptyState = () => (<div style={{ padding: 48, textAlign: "center", color: "#6b7280" }}>No data available</div>);<SimpleTablecolumns={columns}rows={[]}tableEmptyStateRenderer={<EmptyState />}/>
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"/>
Angularempty-state.component.ts — template: "No data available"
Copy
emptyStateEl = wrapAngularRenderer(EmptyStateComponent,this.appRef,this.envInjector,)({});<simple-table[columns]="columns"[rows]="[]"[tableEmptyStateRenderer]="emptyStateEl"></simple-table>
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: 12,23 }}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 theme,34}: {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 />46 );47};4849export default EmptyStateDemo;
Vue SFC
Copy
1<script setup lang="ts">2import { h } from "vue";3import { SimpleTable } from "@simple-table/vue";4import type { Theme } from "@simple-table/vue";5import { emptyStateConfig } from "./empty-state.demo-data";6import "@simple-table/vue/styles.css";78const props = withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {9 height: "400px",10});1112const tableEmptyStateRenderer = h(13 "div",14 {15 style: {16 display: "flex",17 flexDirection: "column",18 alignItems: "center",19 justifyContent: "center",20 padding: "48px 24px",21 color: "#64748b",22 gap: "12px",23 },24 },25 [26 h(27 "svg",28 {29 width: 48,30 height: 48,31 viewBox: "0 0 24 24",32 fill: "none",33 stroke: "#94a3b8",34 strokeWidth: "1.5",35 },36 [37 h("path", { d: "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7" }),38 h("path", { d: "M16 3H8L3 7h18l-5-4z" }),39 h("line", { x1: "10", y1: "12", x2: "14", y2: "12" }),40 ],41 ),42 h("div", { style: { fontSize: "16px", fontWeight: "600" } }, "No data available"),43 h(44 "div",45 { style: { fontSize: "13px" } },46 "Try adjusting your filters or adding new records.",47 ),48 ],49);50</script>5152<template>53 <SimpleTable54 :columns="emptyStateConfig.headers"55 :rows="emptyStateConfig.rows"56 :table-empty-state-renderer="tableEmptyStateRenderer"57 :height="props.height"58 :theme="props.theme"59 />60</template>
Angularempty-state-demo.component.ts
Copy
1import { ApplicationRef, Component, EnvironmentInjector, Input, inject } from "@angular/core";2import { SimpleTableComponent, wrapAngularRenderer } from "@simple-table/angular";3import type { AngularColumnDef, Row, Theme } from "@simple-table/angular";4import { emptyStateConfig } from "./empty-state.demo-data";5import { TableEmptyStateComponent } from "./table-empty-state.component";6import "@simple-table/angular/styles.css";78@Component({9 selector: "empty-state-demo",10 standalone: true,11 imports: [SimpleTableComponent],12 template: `13 <simple-table14 [rows]="rows"15 [columns]="headers"16 [height]="height"17 [theme]="theme"18 [tableEmptyStateRenderer]="emptyStateEl"19 ></simple-table>20 `,21})22export class EmptyStateDemoComponent {23 @Input() height: string | number = "400px";24 @Input() theme?: Theme;2526 private readonly appRef = inject(ApplicationRef);27 private readonly envInjector = inject(EnvironmentInjector);2829 readonly rows: Row[] = emptyStateConfig.rows;30 readonly headers: AngularColumnDef[] = emptyStateConfig.headers;31 readonly emptyStateEl = wrapAngularRenderer(32 TableEmptyStateComponent,33 this.appRef,34 this.envInjector,35 )({});36}373839// empty-state.demo-data.ts40// Self-contained demo table setup for this example.41import type { AngularColumnDef, Row } from "@simple-table/angular";424344export const emptyStateData: Row[] = [];4546export const emptyStateHeaders: AngularColumnDef[] = [47 { accessor: "id", label: "ID", width: 60, type: "number" },48 { accessor: "name", label: "Name", width: 180, type: "string" },49 { accessor: "email", label: "Email", width: 220, type: "string" },50 { accessor: "role", label: "Role", width: 140, type: "string" },51 { accessor: "department", label: "Department", width: 150, type: "string" },52];5354export const emptyStateConfig = {55 headers: emptyStateHeaders,56 rows: emptyStateData,57} as const;585960// table-empty-state.component.ts61import { Component } from "@angular/core";6263@Component({64 selector: "demo-table-empty-state",65 standalone: true,66 template: `67 <div68 style="display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;color:#64748b;gap:12px;"69 >70 <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5">71 <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7" />72 <path d="M16 3H8L3 7h18l-5-4z" />73 <line x1="10" y1="12" x2="14" y2="12" />74 </svg>75 <div style="font-size:16px;font-weight:600;">No data available</div>76 <div style="font-size:13px;">Try adjusting your filters or adding new records.</div>77 </div>78 `,79})80export class TableEmptyStateComponent {}81
SvelteEmptyStateDemo.svelte
Copy
1<script lang="ts">2 import { SimpleTable } from "@simple-table/svelte";3 import type { Theme } from "@simple-table/svelte";4 import { emptyStateConfig } from "./empty-state.demo-data";5 import TableEmptyState from "./TableEmptyState.svelte";6 import "@simple-table/svelte/styles.css";78 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();9</script>1011<SimpleTable12 columns={emptyStateConfig.headers}13 rows={emptyStateConfig.rows}14 tableEmptyStateRenderer={TableEmptyState}15 {height}16 {theme}17/>181920// TableEmptyState.svelte21<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
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 rows={emptyStateConfig.rows}18 height={props.height ?? "400px"}19 theme={props.theme}20 tableEmptyStateRenderer={21 <div style={{22 display: "flex",23 "flex-direction": "column",24 "align-items": "center",25 "justify-content": "center",26 padding: "48px 24px",27 color: "#64748b",28 gap: "12px",29 }}>30 <EmptyIcon />31 <div style={{ "font-size": "16px", "font-weight": "600" }}>No data available</div>32 <div style={{ "font-size": "13px" }}>Try adjusting your filters or adding new records.</div>33 </div>34 }35 />36 );37}
TypeScriptEmptyStateDemo.ts
Copy
1import { SimpleTableVanilla } from "simple-table-core";2import type { Theme } from "simple-table-core";3import { emptyStateConfig, buildEmptyStateElement } from "./empty-state.demo-data";4import "simple-table-core/styles.css";56export function renderEmptyStateDemo(7 container: HTMLElement,8 options?: { height?: string | number; theme?: Theme }9): SimpleTableVanilla {10 const table = new SimpleTableVanilla(container, {11 columns: [...emptyStateConfig.headers],12 rows: emptyStateConfig.rows,13 height: options?.height ?? "400px",14 theme: options?.theme,15 tableEmptyStateRenderer: buildEmptyStateElement(),16 });17 return table;18}192021// empty-state.demo-data.ts22// Self-contained demo table setup for this example.23import type { ColumnDef, Row } from "simple-table-core";242526export const emptyStateData: Row[] = [];2728export const emptyStateHeaders: ColumnDef[] = [29 { accessor: "id", label: "ID", width: 60, type: "number" },30 { accessor: "name", label: "Name", width: 180, type: "string" },31 { accessor: "email", label: "Email", width: 220, type: "string" },32 { accessor: "role", label: "Role", width: 140, type: "string" },33 { accessor: "department", label: "Department", width: 150, type: "string" },34];3536export const emptyStateConfig = {37 headers: emptyStateHeaders,38 rows: emptyStateData,39} as const;4041export function buildEmptyStateElement(): HTMLElement {42 const wrapper = document.createElement("div");43 Object.assign(wrapper.style, {44 display: "flex",45 flexDirection: "column",46 alignItems: "center",47 justifyContent: "center",48 padding: "48px 24px",49 color: "#64748b",50 gap: "12px",51 });5253 const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");54 svg.setAttribute("width", "48");55 svg.setAttribute("height", "48");56 svg.setAttribute("viewBox", "0 0 24 24");57 svg.setAttribute("fill", "none");58 svg.setAttribute("stroke", "#94a3b8");59 svg.setAttribute("stroke-width", "1.5");6061 const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");62 path1.setAttribute("d", "M3 7v10a2 2 0 002 2h14a2 2 0 002-2V7");63 svg.appendChild(path1);6465 const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");66 path2.setAttribute("d", "M16 3H8L3 7h18l-5-4z");67 svg.appendChild(path2);6869 const line = document.createElementNS("http://www.w3.org/2000/svg", "line");70 line.setAttribute("x1", "10");71 line.setAttribute("y1", "12");72 line.setAttribute("x2", "14");73 line.setAttribute("y2", "12");74 svg.appendChild(line);7576 wrapper.appendChild(svg);7778 const title = document.createElement("div");79 Object.assign(title.style, { fontSize: "16px", fontWeight: "600" });80 title.textContent = "No data available";81 wrapper.appendChild(title);8283 const sub = document.createElement("div");84 Object.assign(sub.style, { fontSize: "13px" });85 sub.textContent = "Try adjusting your filters or adding new records.";86 wrapper.appendChild(sub);8788 return wrapper;89}90
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. |