Documentation
Empty State
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
Customize what users see when your table has no data to display. Whether it's an empty dataset, filtered results returning nothing, or data that hasn't loaded yet—provide helpful context instead of a blank table.
Basic Usage
Pass any React component to tableEmptyStateRenderer to display custom content when the table has no rows:
Empty State Configuration
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
tableEmptyStateRendererReactNode | Optional | Custom content to display in the table body when there are no rows to display. This can occur when filters return no results or when no data is provided. |
Common Use Cases
- No search results— Show a "No results found" message with a button to clear filters
- Empty dataset— Display onboarding guidance like "Add your first item" with a call-to-action
- Permission restricted— Inform users they don't have access to view this data
- Error state — Show an error message with a retry button when data fetching fails
💡 Pro Tip
This prop only affects the table body when there are no rows. For loading states while data is being fetched, use the isLoading prop instead.