Documentation
Loading State
1import { useState, useEffect, useCallback } from "react";2import {SimpleTable} from "@simple-table/react";import type { Theme, Row } from "@simple-table/react";3import { loadingStateConfig } from "./loading-state.demo-data";4import "@simple-table/react/styles.css";56const LoadingStateDemo = ({7 height = "400px",8 theme,9}: {10 height?: string | number;11 theme?: Theme;12}) => {13 const [isLoading, setIsLoading] = useState(true);14 const [data, setData] = useState<Row[]>([]);1516 const loadData = useCallback(() => {17 setIsLoading(true);18 setData([]);19 const timer = setTimeout(() => {20 setData(loadingStateConfig.rows as Row[]);21 setIsLoading(false);22 }, 2000);23 return timer;24 }, []);2526 useEffect(() => {27 const timer = loadData();28 return () => clearTimeout(timer);29 }, [loadData]);3031 return (32 <div>33 <div style={{ marginBottom: 12 }}>34 <button35 onClick={() => loadData()}36 disabled={isLoading}37 style={{38 padding: "6px 16px",39 cursor: isLoading ? "not-allowed" : "pointer",40 }}41 >42 {isLoading ? "Loading…" : "Reload Data"}43 </button>44 </div>45 <SimpleTable46 defaultHeaders={loadingStateConfig.headers}47 rows={data}48 isLoading={isLoading}49 height={height}50 theme={theme}51 />52 </div>53 );54};5556export default LoadingStateDemo;
1<script setup lang="ts">2import { ref, onMounted } from "vue";3import {SimpleTable} from "@simple-table/vue";import type { Theme, Row } from "@simple-table/vue";4import { loadingStateConfig } from "./loading-state.demo-data";5import "@simple-table/vue/styles.css";67const props = withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {8 height: "400px",9});1011const isLoading = ref(true);12const data = ref<Row[]>([]);1314function loadData() {15 isLoading.value = true;16 data.value = [];17 setTimeout(() => {18 data.value = [...loadingStateConfig.rows];19 isLoading.value = false;20 }, 2000);21}2223onMounted(() => {24 loadData();25});26</script>2728<template>29 <div>30 <div style="margin-bottom: 12px">31 <button32 @click="loadData"33 :disabled="isLoading"34 :style="{ padding: '6px 16px', cursor: isLoading ? 'not-allowed' : 'pointer' }"35 >36 {{ isLoading ? 'Loading\u2026' : 'Reload Data' }}37 </button>38 </div>39 <SimpleTable40 :default-headers="loadingStateConfig.headers"41 :rows="data"42 :is-loading="isLoading"43 :height="props.height"44 :theme="props.theme"45 />46 </div>47</template>
1import { Component, Input, OnInit, OnDestroy } from "@angular/core";2import {SimpleTableComponent} from "@simple-table/angular";import type { AngularHeaderObject, Row, Theme } from "@simple-table/angular";3import { loadingStateConfig } from "./loading-state.demo-data";4import "@simple-table/angular/styles.css";56@Component({7 selector: "loading-state-demo",8 standalone: true,9 imports: [SimpleTableComponent],10 template: `11 <div>12 <div style="margin-bottom: 12px">13 <button14 (click)="loadData()"15 [disabled]="isLoading"16 [style.padding]="'6px 16px'"17 [style.cursor]="isLoading ? 'not-allowed' : 'pointer'"18 >19 {{ isLoading ? 'Loading\u2026' : 'Reload Data' }}20 </button>21 </div>22 <simple-table23 [rows]="data"24 [defaultHeaders]="headers"25 [height]="height"26 [theme]="theme"27 [isLoading]="isLoading"28 ></simple-table>29 </div>30 `,31})32export class LoadingStateDemoComponent implements OnInit, OnDestroy {33 @Input() height: string | number = "400px";34 @Input() theme?: Theme;3536 readonly headers: AngularHeaderObject[] = loadingStateConfig.headers;37 data: Row[] = [];38 isLoading = true;39 private timer: ReturnType<typeof setTimeout> | null = null;4041 ngOnInit(): void {42 this.loadData();43 }4445 ngOnDestroy(): void {46 if (this.timer) clearTimeout(this.timer);47 }4849 loadData(): void {50 this.isLoading = true;51 this.data = [];52 if (this.timer) clearTimeout(this.timer);53 this.timer = setTimeout(() => {54 this.data = loadingStateConfig.rows as Row[];55 this.isLoading = false;56 }, 2000);57 }58}596061// loading-state.demo-data.ts62// Self-contained demo table setup for this example.63import type { AngularHeaderObject } from "@simple-table/angular";646566export const loadingStateData = [67 { id: 1, name: "Dr. Elena Vasquez", age: 42, department: "AI Research", salary: 145000, status: "Active" },68 { id: 2, name: "Kai Tanaka", age: 29, department: "UX Design", salary: 95000, status: "Active" },69 { id: 3, name: "Amara Okafor", age: 35, department: "DevOps", salary: 125000, status: "On Leave" },70 { id: 4, name: "Santiago Rodriguez", age: 27, department: "Marketing", salary: 82000, status: "Active" },71 { id: 5, name: "Priya Chakraborty", age: 33, department: "Engineering", salary: 118000, status: "Active" },72 { id: 6, name: "Magnus Eriksson", age: 38, department: "Product", salary: 110000, status: "Inactive" },73 { id: 7, name: "Zara Al-Rashid", age: 31, department: "Sales", salary: 98000, status: "Active" },74 { id: 8, name: "Luca Rossi", age: 26, department: "Marketing", salary: 75000, status: "Active" },75];7677export const loadingStateHeaders: AngularHeaderObject[] = [78 { accessor: "name", label: "Name", width: "1fr", minWidth: 120 },79 { accessor: "age", label: "Age", width: 80, type: "number" },80 { accessor: "department", label: "Department", width: 150 },81 {82 accessor: "salary",83 label: "Salary",84 width: 120,85 type: "number",86 align: "right",87 valueFormatter: ({ value }) => `$${(value as number).toLocaleString()}`,88 },89 { accessor: "status", label: "Status", width: 120 },90];9192export const loadingStateConfig = {93 headers: loadingStateHeaders,94 rows: loadingStateData,95} as const;96
1<script lang="ts">2 import {SimpleTable} from "@simple-table/svelte"; import type { Theme, Row } from "@simple-table/svelte";3 import { onMount } from "svelte";4 import { loadingStateConfig } from "./loading-state.demo-data";5 import "@simple-table/svelte/styles.css";67 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();89 let isLoading = $state(true);10 let data = $state<Row[]>([]);1112 function loadData() {13 isLoading = true;14 data = [];15 setTimeout(() => {16 data = [...loadingStateConfig.rows];17 isLoading = false;18 }, 2000);19 }2021 onMount(() => {22 loadData();23 });24</script>2526<div>27 <div style="margin-bottom: 12px">28 <button29 onclick={loadData}30 disabled={isLoading}31 style="padding: 6px 16px; cursor: {isLoading ? 'not-allowed' : 'pointer'}"32 >33 {isLoading ? "Loading\u2026" : "Reload Data"}34 </button>35 </div>36 <SimpleTable37 defaultHeaders={loadingStateConfig.headers}38 rows={data}39 {isLoading}40 {height}41 {theme}42 />43</div>
1import { createSignal, onMount, onCleanup } from "solid-js";2import {SimpleTable} from "@simple-table/solid";import type { Theme, Row } from "@simple-table/solid";3import { loadingStateConfig } from "./loading-state.demo-data";4import "@simple-table/solid/styles.css";56export default function LoadingStateDemo(props: { height?: string | number; theme?: Theme }) {7 const [isLoading, setIsLoading] = createSignal(true);8 const [data, setData] = createSignal<Row[]>([]);9 let timer: ReturnType<typeof setTimeout> | null = null;1011 const loadData = () => {12 setIsLoading(true);13 setData([]);14 if (timer) clearTimeout(timer);15 timer = setTimeout(() => {16 setData(loadingStateConfig.rows as Row[]);17 setIsLoading(false);18 }, 2000);19 };2021 onMount(() => loadData());22 onCleanup(() => { if (timer) clearTimeout(timer); });2324 return (25 <div>26 <div style={{ "margin-bottom": "12px" }}>27 <button28 onClick={loadData}29 disabled={isLoading()}30 style={{ padding: "6px 16px", cursor: isLoading() ? "not-allowed" : "pointer" }}31 >32 {isLoading() ? "Loading\u2026" : "Reload Data"}33 </button>34 </div>35 <SimpleTable36 defaultHeaders={loadingStateConfig.headers}37 rows={data()}38 isLoading={isLoading()}39 height={props.height ?? "400px"}40 theme={props.theme}41 />42 </div>43 );44}
1import { SimpleTableVanilla } from "simple-table-core";2import type { Theme, Row } from "simple-table-core";3import { loadingStateConfig } from "./loading-state.demo-data";4import "simple-table-core/styles.css";56export function renderLoadingStateDemo(7 container: HTMLElement,8 options?: { height?: string | number; theme?: Theme }9): { mount: () => void; destroy: () => void } {10 const wrapper = document.createElement("div");1112 const controls = document.createElement("div");13 controls.style.marginBottom = "12px";1415 const reloadBtn = document.createElement("button");16 reloadBtn.textContent = "Loading\u2026";17 reloadBtn.disabled = true;18 Object.assign(reloadBtn.style, { padding: "6px 16px", cursor: "not-allowed" });19 controls.appendChild(reloadBtn);2021 const tableContainer = document.createElement("div");22 wrapper.appendChild(controls);23 wrapper.appendChild(tableContainer);24 container.appendChild(wrapper);2526 let timer: ReturnType<typeof setTimeout> | null = null;2728 const table = new SimpleTableVanilla(tableContainer, {29 defaultHeaders: loadingStateConfig.headers,30 rows: [] as Row[],31 height: options?.height ?? "400px",32 theme: options?.theme,33 isLoading: true,34 });3536 function loadData() {37 reloadBtn.textContent = "Loading\u2026";38 reloadBtn.disabled = true;39 reloadBtn.style.cursor = "not-allowed";40 table.update({ rows: [] as Row[], isLoading: true });4142 if (timer) clearTimeout(timer);43 timer = setTimeout(() => {44 table.update({ rows: loadingStateConfig.rows as Row[], isLoading: false });45 reloadBtn.textContent = "Reload Data";46 reloadBtn.disabled = false;47 reloadBtn.style.cursor = "pointer";48 }, 2000);49 }5051 reloadBtn.addEventListener("click", loadData);52 loadData();5354 return {55 mount: () => table.mount(),56 destroy: () => {57 if (timer) clearTimeout(timer);58 table.destroy();59 },60 };61}626364// loading-state.demo-data.ts65// Self-contained demo table setup for this example.66import type { HeaderObject } from "simple-table-core";676869export const loadingStateData = [70 { id: 1, name: "Dr. Elena Vasquez", age: 42, department: "AI Research", salary: 145000, status: "Active" },71 { id: 2, name: "Kai Tanaka", age: 29, department: "UX Design", salary: 95000, status: "Active" },72 { id: 3, name: "Amara Okafor", age: 35, department: "DevOps", salary: 125000, status: "On Leave" },73 { id: 4, name: "Santiago Rodriguez", age: 27, department: "Marketing", salary: 82000, status: "Active" },74 { id: 5, name: "Priya Chakraborty", age: 33, department: "Engineering", salary: 118000, status: "Active" },75 { id: 6, name: "Magnus Eriksson", age: 38, department: "Product", salary: 110000, status: "Inactive" },76 { id: 7, name: "Zara Al-Rashid", age: 31, department: "Sales", salary: 98000, status: "Active" },77 { id: 8, name: "Luca Rossi", age: 26, department: "Marketing", salary: 75000, status: "Active" },78];7980export const loadingStateHeaders: HeaderObject[] = [81 { accessor: "name", label: "Name", width: "1fr", minWidth: 120 },82 { accessor: "age", label: "Age", width: 80, type: "number" },83 { accessor: "department", label: "Department", width: 150 },84 {85 accessor: "salary",86 label: "Salary",87 width: 120,88 type: "number",89 align: "right",90 valueFormatter: ({ value }) => `$${(value as number).toLocaleString()}`,91 },92 { accessor: "status", label: "Status", width: 120 },93];9495export const loadingStateConfig = {96 headers: loadingStateHeaders,97 rows: loadingStateData,98} as const;99
Simple Table provides a built-in loading state that displays skeleton loaders for all cells while your data is being fetched. This gives users immediate visual feedback and improves the perceived performance of your application.
Basic Usage
To enable the loading state, simply pass the isLoading prop and set it to true while your data is loading. When the data arrives, set it back to false.
Loading State Configuration
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
isLoadingboolean | Optional | When set to true, all table cells will render skeleton loaders instead of actual data. This provides visual feedback to users while data is being fetched. |
Customizing the Loading Skeleton
You can customize the appearance of the loading skeleton using CSS. The skeleton elements have the st-loading-skeleton class which you can target in your CSS.
CSS Variable for Customization
You can customize the skeleton background color using the CSS variable:
--st-loading-skeleton-bg-colorOr target the skeleton elements directly using the st-loading-skeleton class:
💡 Pro Tip
Combine the loading state with server-side pagination or infinite scroll to provide feedback during data fetches. See the Pagination documentation for an example of using isLoading with page changes.