Documentation
Themes
Built-in themes and flags for table appearance.
Apply a theme
Pass theme with light, dark, neutral, modern-light, or modern-dark. Use custom with Custom Theme for your own tokens.
React TSX
Copy
<SimpleTabletheme="modern-dark"columns={columns}rows={rows}/>
Vue SFC
Copy
<SimpleTabletheme="modern-dark":columns="columns":rows="rows"/>
Angular
Copy
<simple-tabletheme="modern-dark"[columns]="columns"[rows]="rows"></simple-table>
Svelte
Copy
<SimpleTabletheme="modern-dark"{columns}{rows}/>
Solid TSX
Copy
<SimpleTabletheme="modern-dark"columns={columns}rows={rows()}/>
TypeScript
Copy
new SimpleTableVanilla(container, {theme: "modern-dark",columns,rows,});
Styling flags
Toggle row hover, zebra striping, and borders. hoverRowBackground and oddEvenRowBackground default to on; columnBorders and oddColumnBackground default to off.
React TSX
Copy
<SimpleTabletheme="light"hoverRowBackgroundoddEvenRowBackgroundcolumnBordersoddColumnBackgroundcolumns={columns}rows={rows}/>
Vue SFC
Copy
<SimpleTabletheme="light":hover-row-background="true":odd-even-row-background="true":column-borders="true":odd-column-background="true":columns="columns":rows="rows"/>
Angular
Copy
<simple-tabletheme="light"[hoverRowBackground]="true"[oddEvenRowBackground]="true"[columnBorders]="true"[oddColumnBackground]="true"[columns]="columns"[rows]="rows"></simple-table>
Svelte
Copy
<SimpleTabletheme="light"hoverRowBackground={true}oddEvenRowBackground={true}columnBorders={true}oddColumnBackground={true}{columns}{rows}/>
Solid TSX
Copy
<SimpleTabletheme="light"hoverRowBackgroundoddEvenRowBackgroundcolumnBordersoddColumnBackgroundcolumns={columns}rows={rows()}/>
TypeScript
Copy
new SimpleTableVanilla(container, {theme: "light",hoverRowBackground: true,oddEvenRowBackground: true,columnBorders: true,oddColumnBackground: true,columns,rows,});
Column cell classes
Set cellClass on a ColumnDef to add a CSS class to every body cell in that column. Space-separated names work. Style with selectors like .amount-col.
React TSX
Copy
{accessor: "amount",label: "Amount",width: 120,type: "number",cellClass: "amount-col",}/* CSS */.amount-col { font-variant-numeric: tabular-nums; }
Vue SFC
Copy
{accessor: "amount",label: "Amount",width: 120,type: "number",cellClass: "amount-col",}/* CSS */.amount-col { font-variant-numeric: tabular-nums; }
Angular
Copy
{accessor: "amount",label: "Amount",width: 120,type: "number",cellClass: "amount-col",}/* CSS */.amount-col { font-variant-numeric: tabular-nums; }
Svelte
Copy
{accessor: "amount",label: "Amount",width: 120,type: "number",cellClass: "amount-col",}/* CSS */.amount-col { font-variant-numeric: tabular-nums; }
Solid TSX
Copy
{accessor: "amount",label: "Amount",width: 120,type: "number",cellClass: "amount-col",}/* CSS */.amount-col { font-variant-numeric: tabular-nums; }
TypeScript
Copy
{accessor: "amount",label: "Amount",width: 120,type: "number",cellClass: "amount-col",}/* CSS */.amount-col { font-variant-numeric: tabular-nums; }
Conditional row classes
Use getRowClass to highlight rows by data (e.g. a search jump target). Classes apply to each body .st-cell — style with selectors like .jump-row.
React TSX
Copy
const [jumpId, setJumpId] = useState(null);<SimpleTablecolumns={columns}rows={rows}getRowClass={({ row }) =>jumpId && row.id === jumpId ? "jump-row" : undefined}/>/* CSS */.jump-row { background-color: #fef3c7; }
Vue SFC
Copy
<SimpleTable:columns="columns":rows="rows":get-row-class="({ row }) =>jumpId && row.id === jumpId ? 'jump-row' : undefined"/>/* CSS */.jump-row { background-color: #fef3c7; }
Angular
Copy
<simple-table[columns]="columns"[rows]="rows"[getRowClass]="getRowClass"></simple-table>/* getRowClass = ({ row }) => jumpId && row.id === jumpId ? 'jump-row' : undefined *//* CSS */.jump-row { background-color: #fef3c7; }
Svelte
Copy
<SimpleTable{columns}{rows}getRowClass={({ row }) =>jumpId && row.id === jumpId ? "jump-row" : undefined}/>/* CSS */.jump-row { background-color: #fef3c7; }
Solid TSX
Copy
<SimpleTablecolumns={columns}rows={rows()}getRowClass={({ row }) =>jumpId() && row.id === jumpId() ? "jump-row" : undefined}/>/* CSS */.jump-row { background-color: #fef3c7; }
TypeScript
Copy
new SimpleTableVanilla(container, {columns,rows,getRowClass: ({ row }) =>jumpId && row.id === jumpId ? "jump-row" : undefined,});/* CSS */.jump-row { background-color: #fef3c7; }
Example
Switch themes below. Code or StackBlitz has the full example.
Light
React TSX
Copy
1import { useState } from "react";2import {SimpleTable} from "@simple-table/react";import type { Theme } from "@simple-table/react";3import { themesConfig, AVAILABLE_THEMES } from "./themes.demo-data";4import "@simple-table/react/styles.css";56const ThemesDemo = ({7 height = "400px",8 theme,9}: {10 height?: string | number;11 theme?: Theme;12}) => {13 const [selectedTheme, setSelectedTheme] = useState<Theme>(theme ?? "light");1415 return (16 <div>17 <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: 12 }}>18 {AVAILABLE_THEMES.map((t) => (19 <button20 key={t.value}21 onClick={() => setSelectedTheme(t.value)}22 style={{23 padding: "6px 14px",24 borderRadius: 6,25 border: selectedTheme === t.value ? "2px solid #3b82f6" : "1px solid #d1d5db",26 background: selectedTheme === t.value ? "#eff6ff" : "#fff",27 color: selectedTheme === t.value ? "#1d4ed8" : "#374151",28 fontWeight: selectedTheme === t.value ? 600 : 400,29 cursor: "pointer",30 fontSize: 13,31 }}32 >33 {t.label}34 </button>35 ))}36 </div>37 <SimpleTable38 columns={themesConfig.headers}39 rows={themesConfig.rows}40 height={height}41 theme={selectedTheme}42 />43 </div>44 );45};4647export default ThemesDemo;
Vue SFC
Copy
1<template>2 <div>3 <div style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px">4 <button5 v-for="t in AVAILABLE_THEMES"6 :key="t.value"7 @click="selectedTheme = t.value"8 :style="{9 padding: '6px 14px',10 borderRadius: '6px',11 border: selectedTheme === t.value ? '2px solid #3b82f6' : '1px solid #d1d5db',12 background: selectedTheme === t.value ? '#eff6ff' : '#fff',13 color: selectedTheme === t.value ? '#1d4ed8' : '#374151',14 fontWeight: selectedTheme === t.value ? 600 : 400,15 cursor: 'pointer',16 fontSize: '13px',17 }"18 >19 {{ t.label }}20 </button>21 </div>22 <SimpleTable23 :columns="themesConfig.headers"24 :rows="themesConfig.rows"25 :height="height"26 :theme="selectedTheme"27 />28 </div>29</template>3031<script setup lang="ts">32import { ref } from "vue";33import {SimpleTable} from "@simple-table/vue";import type { Theme } from "@simple-table/vue";34import { themesConfig, AVAILABLE_THEMES } from "./themes.demo-data";35import "@simple-table/vue/styles.css";3637const props = withDefaults(defineProps<{ height?: string | number; theme?: Theme }>(), {38 height: "400px",39});4041const selectedTheme = ref<Theme>(props.theme ?? "light");42</script>
Angularthemes-demo.component.ts
Copy
1import { Component, Input } from "@angular/core";2import { NgFor } from "@angular/common";3import {SimpleTableComponent} from "@simple-table/angular";import type { AngularColumnDef, Row, Theme } from "@simple-table/angular";4import { themesConfig, AVAILABLE_THEMES } from "./themes.demo-data";5import "@simple-table/angular/styles.css";67@Component({8 selector: "themes-demo",9 standalone: true,10 imports: [SimpleTableComponent, NgFor],11 template: `12 <div>13 <div style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px">14 <button15 *ngFor="let t of themes"16 (click)="selectedTheme = t.value"17 [style.padding]="'6px 14px'"18 [style.borderRadius]="'6px'"19 [style.border]="selectedTheme === t.value ? '2px solid #3b82f6' : '1px solid #d1d5db'"20 [style.background]="selectedTheme === t.value ? '#eff6ff' : '#fff'"21 [style.color]="selectedTheme === t.value ? '#1d4ed8' : '#374151'"22 [style.fontWeight]="selectedTheme === t.value ? 600 : 400"23 [style.cursor]="'pointer'"24 [style.fontSize]="'13px'"25 >26 {{ t.label }}27 </button>28 </div>29 <simple-table30 [rows]="rows"31 [columns]="headers"32 [height]="height"33 [theme]="selectedTheme"34 ></simple-table>35 </div>36 `,37})38export class ThemesDemoComponent {39 @Input() height: string | number = "400px";40 @Input() theme?: Theme;4142 readonly rows: Row[] = themesConfig.rows;43 readonly headers: AngularColumnDef[] = themesConfig.headers;44 readonly themes = AVAILABLE_THEMES;45 selectedTheme: Theme = "light";4647 ngOnInit() {48 if (this.theme) this.selectedTheme = this.theme;49 }50}515253// themes.demo-data.ts54// Self-contained demo table setup for this example.55import type { AngularColumnDef, Theme } from "@simple-table/angular";565758export const AVAILABLE_THEMES: { value: Theme; label: string }[] = [59 { value: "light", label: "Light" },60 { value: "dark", label: "Dark" },61 { value: "modern-light", label: "Modern Light" },62 { value: "modern-dark", label: "Modern Dark" },63 { value: "neutral", label: "Neutral" },64];6566export const themesHeaders: AngularColumnDef[] = [67 { accessor: "id", label: "ID", width: 80, type: "number" },68 { accessor: "name", label: "Name", minWidth: 100, width: "1fr", type: "string" },69 { accessor: "email", label: "Email", width: 220, type: "string" },70 { accessor: "department", label: "Department", width: 150, type: "string" },71 { accessor: "status", label: "Status", width: 120, type: "string" },72];7374export const themesData = [75 { id: 1, name: "Dr. Sarah Mitchell", email: "s.mitchell@cityhospital.org", department: "Cardiology", status: "Active" },76 { id: 2, name: "Nurse Emily Chen", email: "e.chen@cityhospital.org", department: "Emergency", status: "Active" },77 { id: 3, name: "Dr. Marcus Williams", email: "m.williams@cityhospital.org", department: "Neurology", status: "Active" },78 { id: 4, name: "Therapist Ana Rodriguez", email: "a.rodriguez@cityhospital.org", department: "Physical Therapy", status: "Active" },79 { id: 5, name: "Dr. Yuki Tanaka", email: "y.tanaka@cityhospital.org", department: "Pediatrics", status: "On Call" },80 { id: 6, name: "Technician Omar Hassan", email: "o.hassan@cityhospital.org", department: "Radiology", status: "Active" },81 { id: 7, name: "Dr. Priya Patel", email: "p.patel@cityhospital.org", department: "Oncology", status: "Active" },82 { id: 8, name: "Coordinator Lisa Kim", email: "l.kim@cityhospital.org", department: "Patient Care", status: "Active" },83 { id: 9, name: "Dr. Giovanni Rossi", email: "g.rossi@cityhospital.org", department: "Surgery", status: "Active" },84 { id: 10, name: "Pharmacist David Lee", email: "d.lee@cityhospital.org", department: "Pharmacy", status: "Active" },85 { id: 11, name: "Nurse Zara Singh", email: "z.singh@cityhospital.org", department: "ICU", status: "Active" },86 { id: 12, name: "Dr. Felix Martinez", email: "f.martinez@cityhospital.org", department: "Orthopedics", status: "Active" },87];8889export const themesConfig = {90 headers: themesHeaders,91 rows: themesData,92} as const;93
Svelte
Copy
1<script lang="ts">2 import {SimpleTable} from "@simple-table/svelte"; import type { Theme } from "@simple-table/svelte";3 import { themesConfig, AVAILABLE_THEMES } from "./themes.demo-data";4 import "@simple-table/svelte/styles.css";56 let { height = "400px", theme }: { height?: string | number; theme?: Theme } = $props();7 let userThemePick = $state<Theme | undefined>(undefined);8 const selectedTheme: Theme = $derived(userThemePick ?? theme ?? "light");9</script>1011<div>12 <div style="display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px">13 {#each AVAILABLE_THEMES as t}14 <button15 onclick={() => (userThemePick = t.value)}16 style="padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; border: {selectedTheme === t.value ? '2px solid #3b82f6' : '1px solid #d1d5db'}; background: {selectedTheme === t.value ? '#eff6ff' : '#fff'}; color: {selectedTheme === t.value ? '#1d4ed8' : '#374151'}; font-weight: {selectedTheme === t.value ? 600 : 400}"17 >18 {t.label}19 </button>20 {/each}21 </div>22 <SimpleTable23 columns={themesConfig.headers}24 rows={themesConfig.rows}25 {height}26 theme={selectedTheme}27 />28</div>
Solid TSX
Copy
1import { createSignal, For } from "solid-js";2import {SimpleTable} from "@simple-table/solid";import type { Theme } from "@simple-table/solid";3import { themesConfig, AVAILABLE_THEMES } from "./themes.demo-data";4import "@simple-table/solid/styles.css";56export default function ThemesDemo(props: { height?: string | number; theme?: Theme }) {7 const [selectedTheme, setSelectedTheme] = createSignal<Theme>(props.theme ?? "light");89 return (10 <div>11 <div style={{ display: "flex", "flex-wrap": "wrap", gap: "8px", "margin-bottom": "12px" }}>12 <For each={AVAILABLE_THEMES}>13 {(t) => (14 <button15 onClick={() => setSelectedTheme(t.value)}16 style={{17 padding: "6px 14px",18 "border-radius": "6px",19 border: selectedTheme() === t.value ? "2px solid #3b82f6" : "1px solid #d1d5db",20 background: selectedTheme() === t.value ? "#eff6ff" : "#fff",21 color: selectedTheme() === t.value ? "#1d4ed8" : "#374151",22 "font-weight": selectedTheme() === t.value ? 600 : 400,23 cursor: "pointer",24 "font-size": "13px",25 }}26 >27 {t.label}28 </button>29 )}30 </For>31 </div>32 <SimpleTable33 columns={themesConfig.headers}34 rows={themesConfig.rows}35 height={props.height ?? "400px"}36 theme={selectedTheme()}37 />38 </div>39 );40}
TypeScriptThemesDemo.ts
Copy
1import { SimpleTableVanilla } from "simple-table-core";2import type { Theme } from "simple-table-core";3import { themesConfig, AVAILABLE_THEMES } from "./themes.demo-data";4import "simple-table-core/styles.css";56export function renderThemesDemo(7 container: HTMLElement,8 options?: { height?: string | number; theme?: Theme }9): SimpleTableVanilla {10 let selectedTheme: Theme = options?.theme ?? "light";1112 const wrapper = document.createElement("div");1314 const buttonRow = document.createElement("div");15 buttonRow.style.cssText = "display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px";1617 const tableContainer = document.createElement("div");18 wrapper.appendChild(buttonRow);19 wrapper.appendChild(tableContainer);20 container.appendChild(wrapper);2122 const table = new SimpleTableVanilla(tableContainer, {23 columns: themesConfig.headers,24 rows: themesConfig.rows,25 height: options?.height ?? "400px",26 theme: selectedTheme,27 });2829 function renderButtons() {30 buttonRow.innerHTML = "";31 for (const t of AVAILABLE_THEMES) {32 const btn = document.createElement("button");33 btn.textContent = t.label;34 const active = selectedTheme === t.value;35 btn.style.cssText = `padding:6px 14px;border-radius:6px;cursor:pointer;font-size:13px;border:${active ? "2px solid #3b82f6" : "1px solid #d1d5db"};background:${active ? "#eff6ff" : "#fff"};color:${active ? "#1d4ed8" : "#374151"};font-weight:${active ? 600 : 400}`;36 btn.addEventListener("click", () => {37 selectedTheme = t.value;38 table.update({ theme: selectedTheme });39 renderButtons();40 });41 buttonRow.appendChild(btn);42 }43 }4445 renderButtons();46 return table;47}484950// themes.demo-data.ts51// Self-contained demo table setup for this example.52import type { ColumnDef, Theme } from "simple-table-core";535455export const AVAILABLE_THEMES: { value: Theme; label: string }[] = [56 { value: "light", label: "Light" },57 { value: "dark", label: "Dark" },58 { value: "modern-light", label: "Modern Light" },59 { value: "modern-dark", label: "Modern Dark" },60 { value: "neutral", label: "Neutral" },61];6263export const themesHeaders: ColumnDef[] = [64 { accessor: "id", label: "ID", width: 80, type: "number" },65 { accessor: "name", label: "Name", minWidth: 100, width: "1fr", type: "string" },66 { accessor: "email", label: "Email", width: 220, type: "string" },67 { accessor: "department", label: "Department", width: 150, type: "string" },68 { accessor: "status", label: "Status", width: 120, type: "string" },69];7071export const themesData = [72 { id: 1, name: "Dr. Sarah Mitchell", email: "s.mitchell@cityhospital.org", department: "Cardiology", status: "Active" },73 { id: 2, name: "Nurse Emily Chen", email: "e.chen@cityhospital.org", department: "Emergency", status: "Active" },74 { id: 3, name: "Dr. Marcus Williams", email: "m.williams@cityhospital.org", department: "Neurology", status: "Active" },75 { id: 4, name: "Therapist Ana Rodriguez", email: "a.rodriguez@cityhospital.org", department: "Physical Therapy", status: "Active" },76 { id: 5, name: "Dr. Yuki Tanaka", email: "y.tanaka@cityhospital.org", department: "Pediatrics", status: "On Call" },77 { id: 6, name: "Technician Omar Hassan", email: "o.hassan@cityhospital.org", department: "Radiology", status: "Active" },78 { id: 7, name: "Dr. Priya Patel", email: "p.patel@cityhospital.org", department: "Oncology", status: "Active" },79 { id: 8, name: "Coordinator Lisa Kim", email: "l.kim@cityhospital.org", department: "Patient Care", status: "Active" },80 { id: 9, name: "Dr. Giovanni Rossi", email: "g.rossi@cityhospital.org", department: "Surgery", status: "Active" },81 { id: 10, name: "Pharmacist David Lee", email: "d.lee@cityhospital.org", department: "Pharmacy", status: "Active" },82 { id: 11, name: "Nurse Zara Singh", email: "z.singh@cityhospital.org", department: "ICU", status: "Active" },83 { id: 12, name: "Dr. Felix Martinez", email: "f.martinez@cityhospital.org", department: "Orthopedics", status: "Active" },84];8586export const themesConfig = {87 headers: themesHeaders,88 rows: themesData,89} as const;90
Props
Theme Configuration
| Property | Required | Description | Example |
|---|---|---|---|
Property | Required | Description | Example |
theme | Optional | Built-in theme for the table. Options: light dark neutral modern-light modern-dark custom | |
hoverRowBackgroundboolean | Optional | Highlight the row under the pointer. Defaults to true. | |
oddEvenRowBackgroundboolean | Optional | Alternate row background colors. Defaults to true. | |
| Optional | Return CSS class name(s) for a row. Applied to each body cell — style with `.st-cell.yourClass`. | ||
oddColumnBackgroundboolean | Optional | Alternate column background colors. Defaults to false. | |
columnBordersboolean | Optional | Show vertical borders between columns. Defaults to false. |