| # | Name | Category | Price | Stock |
|---|---|---|---|---|
| 1 | iPhone 13 | Electronics | $799.00 | 150 |
| 2 | Galaxy S21 | Electronics | $699.00 | 200 |
| 3 | MacBook Pro | Computers | $1,299.00 | 80 |
| 4 | Dell XPS 13 | Computers | $999.00 | 50 |
| 5 | Sony WH-1000XM4 | Headphones | $349.00 | 120 |
| 6 | AirPods Pro | Headphones | $249.00 | 180 |
| 7 | Fitbit Charge 5 | Wearables | $179.00 | 75 |
npx shadcn@latest add https://sdk-components.thesqd.com/r/simple-table.jsonimport { SimpleTable } from "@/components/ui/simple-table";import { SimpleTable } from "@/components/ui/simple-table";
export function Example() {
return (
<SimpleTable
columns={[
{ key: "name", label: "Name", primary: true },
{ key: "price", label: "Price", format: "currency", align: "right" },
]}
rows={[
{ id: "1", name: "iPhone 13", price: 799 },
{ id: "2", name: "MacBook Pro", price: 1299 },
]}
/>
);
}SimpleTable (single component — columns + rows as JSON)
├── card-surface wrapper
│ ├── header (only when title / description / action / searchable)
│ │ ├── title + description
│ │ └── search field + action
│ └── horizontal scroller (scroll-fade-x; maxHeight adds a vertical scroll + sticky header)
│ └── <table>
│ ├── <thead> (checkbox column? · one <th> per column · actions column?)
│ └── <tbody> (one <tr> per visible row · nested rows indent under a caret)
│ (empty state row when nothing to show)
│
├── columns={[{ key, label?, align?, width?, sortable?, format?, currency?, badges?, primary?, captionKey?, render?, className? }]}
├── rows={[{ id, …fields, children?: [...] }]}
└── props: title · description · action · size · striped · grid · sortable · defaultSort
searchable · searchPlaceholder · selectable · selectedKeys · defaultSelectedKeys
onSelectionChange · rowActions · onRowClick · maxHeight · headerCaps · onCellChange
loading · emptyTitle · emptyDescriptionThis field is required
| Name | Role | Status | Joined | |
|---|---|---|---|---|
| Justice Larkin | justice.larkin@example.com | Admin | Active | Jan 15, 2022 |
| Megan Smith | megan.smith@example.com | Editor | Active | Mar 12, 2022 |
| Daniel Wu | daniel.wu@example.com | Viewer | Inactive | Apr 8, 2022 |
| Sophia Hernandez | sophia.hernandez@example.com | Admin | Active | May 25, 2022 |
| Liam Johnson | liam.johnson@example.com | Editor | Suspended | Jun 14, 2022 |
| Emily Brown | emily.brown@example.com | Viewer | Active | Jul 9, 2022 |
| 3 | MacBook Pro | Computers | $1,299.00 | 80 |
| 4 | Dell XPS 13 | Computers | $999.00 | 50 |
| 1 | iPhone 13 | Electronics | $799.00 | 150 |
| 2 | Galaxy S21 | Electronics | $699.00 | 200 |
| 5 | Sony WH-1000XM4 | Headphones | $349.00 | 120 |
| 6 | AirPods Pro | Headphones | $249.00 | 180 |
| 7 | Fitbit Charge 5 | Wearables | $179.00 | 75 |
| # | Name | Category | Price | Stock | |
|---|---|---|---|---|---|
| 1 | iPhone 13 | Electronics | $799.00 | 150 | |
| 2 | Galaxy S21 | Electronics | $699.00 | 200 | |
| 3 | MacBook Pro | Computers | $1,299.00 | 80 | |
| 4 | Dell XPS 13 | Computers | $999.00 | 50 | |
| 5 | Sony WH-1000XM4 | Headphones | $349.00 | 120 | |
| 6 | AirPods Pro | Headphones | $249.00 | 180 | |
| 7 | Fitbit Charge 5 | Wearables | $179.00 | 75 |
Nothing selected.
| Name | Role | Status | Joined | ||
|---|---|---|---|---|---|
| Justice Larkin | justice.larkin@example.com | Admin | Active | Jan 15, 2022 | |
| Megan Smith | megan.smith@example.com | Editor | Active | Mar 12, 2022 | |
| Daniel Wu | daniel.wu@example.com | Viewer | Inactive | Apr 8, 2022 | |
| Sophia Hernandez | sophia.hernandez@example.com | Admin | Active | May 25, 2022 | |
| Liam Johnson | liam.johnson@example.com | Editor | Suspended | Jun 14, 2022 | |
| Emily Brown | emily.brown@example.com | Viewer | Active | Jul 9, 2022 |
This field is required
| Project | Owner | Status | Progress |
|---|---|---|---|
Website redesign | Emma Taylor | In progress | 72% |
Mobile app onboarding | Sophia Brown | Blocked | 39% |
Security audit | Charlotte Allen | Planned | 12% |
| # | Name | Category | Price | Stock |
|---|---|---|---|---|
| 1 | iPhone 13 | Electronics | $799.00 | 150 |
| 2 | Galaxy S21 | Electronics | $699.00 | 200 |
| 3 | MacBook Pro | Computers | $1,299.00 | 80 |
| 4 | Dell XPS 13 | Computers | $999.00 | 50 |
| 5 | Sony WH-1000XM4 | Headphones | $349.00 | 120 |
| 6 | AirPods Pro | Headphones | $249.00 | 180 |
| 7 | Fitbit Charge 5 | Wearables | $179.00 | 75 |
| # | Name | Category | Price | Stock |
|---|---|---|---|---|
| 1 | iPhone 13 | Electronics | $799.00 | 150 |
| 2 | Galaxy S21 | Electronics | $699.00 | 200 |
| 3 | MacBook Pro | Computers | $1,299.00 | 80 |
| 4 | Dell XPS 13 | Computers | $999.00 | 50 |
| 5 | Sony WH-1000XM4 | Headphones | $349.00 | 120 |
| 6 | AirPods Pro | Headphones | $249.00 | 180 |
| 7 | Fitbit Charge 5 | Wearables | $179.00 | 75 |
| 1b | iPhone 13 | Electronics | $799.00 | 150 |
| 2b | Galaxy S21 | Electronics | $699.00 | 200 |
| 3b | MacBook Pro | Computers | $1,299.00 | 80 |
| 4b | Dell XPS 13 | Computers | $999.00 | 50 |
| 5b | Sony WH-1000XM4 | Headphones | $349.00 | 120 |
| 6b | AirPods Pro | Headphones | $249.00 | 180 |
| 7b | Fitbit Charge 5 | Wearables | $179.00 | 75 |
| Name | Updated | |
|---|---|---|
Easter seriesSermon graphic | Apr 2, 2026 | |
Fall retreatEvent flyer | Sep 18, 2026 | |
Kids campSocial post | Jun 11, 2026 |
| Size | Design | Bkg |
|---|---|---|
Screen1920 × 1080 px | ||
IG Feed1080 × 1350 px | ||
Story1080 × 1920 px | ||
Square1080 × 1080 px | ||
Portrait1200 × 1800 px | ||
Letter8.5 × 11 in |
This field is required
| # | Name | Category | Price | Stock | |
|---|---|---|---|---|---|
| # | Name | Category | Price | Stock |
|---|---|---|---|---|
No products yet Add a product and it will show up here. | ||||
SimpleTable
| Prop | Type | Default | Description |
|---|---|---|---|
columns | Column[] | — | Column definitions — see Column below. |
rows | Record<string, unknown>[] | — | Plain JSON rows. Each field is read by the column with the matching `key`. |
rowKey | string | "id" | Row field used as the React key and selection id. Falls back to the row index. |
childrenKey | string | "children" | Row field holding nested rows. Rows with children get an expand caret on the first column. |
title | ReactNode | — | Card header title. |
description | ReactNode | — | Muted line under the title. |
action | ReactNode | — | Right side of the header row (a Button, say). |
size | "sm" | "md" | "md" | `sm` tightens row padding for dense data. |
striped | boolean | false | Zebra rows instead of row dividers. |
grid | boolean | false | Vertical dividers between columns. |
sortable | boolean | false | Makes every column sortable. A column's own `sortable: false` opts out. Clicking cycles asc → desc → off. |
defaultSort | { key: string; direction: "asc" | "desc" } | — | Initial sort. |
searchable | boolean | false | Adds a search field to the header. Filters rows (a parent stays when a child matches) and highlights matches. |
searchPlaceholder | string | "Search" | Placeholder + accessible label for the search field. |
selectable | boolean | false | Leading checkbox column with a select-all header (selects every visible row). |
selectedKeys | string[] | — | Controlled selection. Nested row keys are `parent/child`. |
defaultSelectedKeys | string[] | — | Uncontrolled initial selection. |
onSelectionChange | (keys: string[]) => void | — | Fires with the full selected key list. |
rowActions | Action[] | — | Trailing ⋮ menu on each row — see Action below. |
onRowClick | (row) => void | — | Makes rows clickable (pointer + hover tint). |
maxHeight | number | string | — | Caps the table height; rows scroll under a sticky header. |
headerCaps | boolean | false | Small uppercase header labels. |
onCellChange | (row, key: string, value: boolean) => void | — | Fires when a `checkbox` cell is toggled. Cells keep their own state, so it's optional. |
loading | boolean | number | — | First-load state. While true AND `rows` is empty, the body shows skeleton rows shaped like each column — a pill for a badge, a short bar for a number or date, a square for an image or checkbox (`true` = 5 rows; a number sets the count). A response faster than 300ms never shows a skeleton; one that appears stays at least 500ms, then the rows replace it with a cross-fade and blur. With rows already on screen it changes nothing, so a refetch keeps the stale rows readable. The empty message never shows while loading; the select-all checkbox is disabled; search stays usable. |
emptyTitle | string | "No data found" | Shown when there are no rows (or no search matches). |
emptyDescription | string | "No information is currently available in this section." | Line under the empty title. |
className | string | — | Class names on the card wrapper. |
Column
| Prop | Type | Default | Description |
|---|---|---|---|
key | string | — | Row field this column reads. |
label | string | — | Header text. Defaults to `key`. |
align | "left" | "center" | "right" | "left" | Text alignment for header and cells. |
width | number | string | — | Fixed column width (px or any CSS length). |
sortable | boolean | — | Sort on this column (overrides the table-level `sortable`). |
format | "text" | "number" | "currency" | "percent" | "date" | "badge" | "image" | "checkbox" | "text" | How the value renders. Number formats use tabular figures; `percent` expects a 0–1 fraction; `date` parses any `Date`-readable string; `image` draws a URL as a thumbnail; `checkbox` draws a boolean as a toggle (a string value draws a thumbnail instead). |
currency | string | "USD" | ISO currency code for `format: "currency"`. |
badges | Record<string, BadgeColor> | — | Value → Badge color map for `format: "badge"`. Unmapped values render muted. |
primary | boolean | — | Medium-weight foreground text — use on the row's name column. |
captionKey | string | — | Row field rendered as a small muted second line under the cell value. |
render | (value, row) => ReactNode | — | Escape hatch for cells JSON can't express. Rendered cells are skipped by search. |
className | string | — | Class names on each body cell. |
Action
| Prop | Type | Default | Description |
|---|---|---|---|
"separator" | string | — | A divider between menu items. |
label | string | — | Menu item text. |
icon | ReactNode | — | Leading icon. |
destructive | boolean | — | Tints the item red. |
onSelect | (row) => void | — | Called with the row the menu belongs to. |