@teamprodevs/appsmith-custom-table
v1.1.5
Published
[](https://www.npmjs.com/package/@teamprodevs/appsmith-custom-table) [](#license) [.
- Client-side pagination with infinite scroll support.
- Tight integration with Appsmith's data models (PostgreSQL, MongoDB, Elasticsearch, REST APIs).
- Typed configuration powered by Zod schemas for validation.
- Rich styling hooks with Tailwind-first tokenization.
- Action hooks, event callbacks, and controlled
updateModelAPIs. - Conditional row styling based on cell values.
- Multi-language support (i18n) for column headers and action labels.
- Built-in formatters for phone, date, datetime, and currency.
- Compatible with pgrest/dbtorest pagination patterns.
Table of Contents
- Quick Links
- Installation
- CDN Usage
- Quick Start
- Usage
- API Reference
- Row Actions
- Conditional Styling
- Translations (i18n)
- Customization
- Events
- Development
- Contributing
- License
- Acknowledgements
Demo
Explore the live Storybook playground: https://custom-appsmith-table.netlify.app/
Installation
npm install @teamprodevs/appsmith-custom-table
# or
yarn add @teamprodevs/appsmith-custom-tableCDN Usage
Ideal for Appsmith widgets where bundling is not available.
<script src="https://unpkg.com/@teamprodevs/appsmith-custom-table/dist/app.umd.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@teamprodevs/appsmith-custom-table/dist/styles.css" />Quick Start
- Define a schema describing each column's type and display options.
- Pass your Appsmith query data to the
ClientTablecomponent. - Wire up
triggerEventandupdateModelcallbacks to Appsmith's APIs. - Optionally add custom styles, actions, and conditional formatting.
Usage
Client-side Table
import { ClientTable } from "@teamprodevs/appsmith-custom-table";
import "@teamprodevs/appsmith-custom-table/dist/styles.css";
const data = [
{ id: 1, name: "Alice", age: 25 },
{ id: 2, name: "Bob", age: 30 },
];
const schema = {
id: { type: "text", title: { en: "ID", uz: "ID" } },
name: { type: "text", title: { en: "Name", uz: "Ism" } },
age: { type: "text", title: { en: "Age", uz: "Yosh" } },
};
const MyTable = () => (
<ClientTable
tableData={data}
schema={schema}
locale="en"
triggerEvent={(event, payload) => console.log(event, payload)}
updateModel={(model) => console.log(model)}
onModelChange={(model) => console.log(model)}
/>
);Infinite Scroll with Appsmith
The table triggers onLoadMore events for pagination. Wire this to your Appsmith query:
import { ClientTable } from "@teamprodevs/appsmith-custom-table";
// In your Appsmith custom widget
const tableModel = {
tableData: appsmith.model.queryData, // Data from your Appsmith query
schema,
locale: "en",
limit: 20,
max_count: appsmith.model.totalCount, // Total rows from your API
triggerEvent: (event, payload) => appsmith.triggerEvent(event, payload),
updateModel: (model) => appsmith.updateModel(model),
onModelChange: (model) => console.log("table state", model),
};
const UsersTable = () => <ClientTable {...tableModel} />;API Reference
TableModel Props
The ClientTable component accepts the following props (all validated via Zod schemas):
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| tableData | any[] | Yes | [] | Array of row data objects |
| schema | Schema | Yes | - | Column definitions (see Column Schema) |
| locale | string | Yes | - | Active language code for translations (e.g., "en", "uz", "ru") |
| triggerEvent | TriggerEvent | Yes | - | Callback to trigger Appsmith events |
| updateModel | UpdateModel | Yes | - | Callback to update Appsmith model state |
| onModelChange | OnModelChange | Yes | - | Callback fired when table model changes |
| limit | number | No | 20 | Number of rows per page |
| max_count | number | No | 20 | Maximum total rows (for pagination) |
| indexColumn | IndexColumn | No | - | Configuration for row index column |
| actionColumn | ActionColumn | No | - | Configuration for action buttons column |
| conditionalRowStyles | ConditionalRowStyle[] | No | - | Rules for conditional row styling |
| styles | AppsmithTableStyles | No | - | Custom styling configuration |
| rowSelectionAction | string | No | - | Event name triggered on row selection |
Column Schema
Each column in the schema object is defined with these options:
const schema = {
column_key: {
type: "text", // Column type (see Column Types)
size: "md", // Width: "xs" | "sm" | "md" | "lg"
title: { // Localized column headers
en: "English Title",
uz: "O'zbek Sarlavha",
ru: "Русский заголовок"
},
className: "custom-class" // Custom CSS classes
}
};| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| type | ColumnType | No | "text" | Data type for formatting |
| size | "xs" \| "sm" \| "md" \| "lg" | No | "md" | Column width preset |
| title | Record<string, string> | No | - | Localized headers by language code |
| className | string | No | - | Custom CSS classes for column |
Column Types
The table supports 6 column types with automatic formatting:
| Type | Description | Example Output |
|------|-------------|----------------|
| text | Plain text display (default) | "John Doe" |
| url | Clickable link (special format) | <a href="...">Display Text</a> |
| phone | Phone number formatting | 99 123 45 67 |
| date | Date formatting | 15-03-2024 |
| datetime | Date and time formatting | 15-03-2024 14:30 |
| currency | Currency with thousand separators | 1 234 567 |
URL Column Format
For URL columns, use the special format "Display Text|||https://url.com":
const data = [
{
link: "Visit Site|||https://example.com",
// Renders as clickable "Visit Site" link
}
];
const schema = {
link: { type: "url", title: { en: "Link" } }
};When clicked, triggers onRedirect event with the URL.
Row Actions
Add interactive action buttons to each row using the actionColumn configuration:
import { ClientTable } from "@teamprodevs/appsmith-custom-table";
const tableModel = {
tableData: data,
schema: schema,
locale: "en",
actionColumn: {
enable: true,
pin: "right", // "left" | "right"
type: "outline", // Button variant
icon: "MoreVertical", // Lucide icon for dropdown trigger
actions: [
{
title: { en: "View", uz: "Ko'rish", ru: "Просмотр" },
onClick: "onView", // Event name to trigger
icon: "Eye", // Lucide icon name
className: "text-blue-600"
},
{
title: { en: "Edit", uz: "Tahrirlash", ru: "Редактировать" },
onClick: "onEdit",
icon: "Pencil"
},
{
title: { en: "Delete", uz: "O'chirish", ru: "Удалить" },
onClick: "onDelete",
icon: "Trash2",
className: "text-red-600"
}
]
},
triggerEvent: (event, payload) => {
// payload contains { row: <row data> }
console.log(event, payload);
},
// ... other props
};Action Column Options
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| enable | boolean | Yes | - | Enable/disable action column |
| actions | RowAction[] | Yes | - | Array of action definitions |
| pin | "left" \| "right" | No | "right" | Pin column position |
| type | string | No | "default" | Button variant |
| icon | LucideIconName | No | - | Icon for dropdown trigger |
Button Variants
Available button variants: "default", "destructive", "outline", "secondary", "ghost", "link"
Supported Icons
All Lucide React icons are supported. Use the icon name as a string (e.g., "Eye", "Pencil", "Trash2", "Activity", "AlarmClockPlus").
Conditional Styling
Apply dynamic styles to rows based on cell values using conditionalRowStyles:
const tableModel = {
// ... other props
conditionalRowStyles: [
// Highlight rows where used_days >= 150
{
column: "used_days",
operator: ">=",
value: 150,
className: "bg-yellow-200"
},
// Red background for used_days >= 300
{
column: "used_days",
operator: ">=",
value: 300,
className: "bg-red-400"
},
// Compare two columns: highlight when payment < debt
{
column: "total_payment",
operator: "<",
value: { columnRef: "debt_amount" },
className: "text-red-800 font-bold"
},
// Green when payment >= debt
{
column: "total_payment",
operator: ">=",
value: { columnRef: "debt_amount" },
className: "text-green-800 font-bold"
}
]
};Conditional Style Options
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| column | string | Yes | Column key to evaluate |
| operator | RowStyleOperator | Yes | Comparison operator |
| value | string \| number \| boolean \| { columnRef: string } | Yes | Value to compare against (or another column) |
| className | string | Yes | Tailwind/CSS classes to apply |
Supported Operators
| Operator | Description |
|----------|-------------|
| > | Greater than |
| < | Less than |
| >= | Greater than or equal |
| <= | Less than or equal |
| === | Strict equality |
| == | Loose equality |
| !== | Strict inequality |
| != | Loose inequality |
| contains | String contains |
| startsWith | String starts with |
| endsWith | String ends with |
| isEmpty | Value is empty |
| isNotEmpty | Value is not empty |
Column-to-Column Comparison
Use { columnRef: "column_name" } to compare against another column's value:
{
column: "actual_amount",
operator: "<",
value: { columnRef: "expected_amount" },
className: "text-red-600"
}Translations (i18n)
The table supports multi-language column headers and action labels:
Column Header Translations
const schema = {
customer_name: {
title: {
en: "Customer Name",
uz: "Mijoz Ismi",
ru: "Имя клиента"
},
type: "text"
},
phone: {
title: {
en: "Phone Number",
uz: "Telefon Raqam",
ru: "Телефон номер"
},
type: "phone"
}
};
// Set active language
<ClientTable
schema={schema}
locale="uz" // Shows "Mijoz Ismi", "Telefon Raqam"
// ...
/>Action Label Translations
const actionColumn = {
enable: true,
actions: [
{
title: {
en: "View Details",
uz: "Batafsil ko'rish",
ru: "Посмотреть детали"
},
onClick: "onView",
icon: "Eye"
}
]
};Customization
Styles
Apply custom styles to different parts of the table:
import { ClientTable } from "@teamprodevs/appsmith-custom-table";
import type { AppsmithTableStyles } from "@teamprodevs/appsmith-custom-table";
const styles: AppsmithTableStyles = {
container: "bg-white shadow-md rounded-lg px-1",
table: "border border-gray-200",
head: {
body: "bg-gray-100",
row: "hover:bg-gray-200 transition-colors",
cell: "border-b border-gray-300 font-semibold"
},
body: {
body: "bg-white",
row: "odd:bg-gray-50 even:bg-white hover:bg-blue-50",
cell: "border-b border-gray-200 px-3 py-2"
}
};
const StyledTable = () => (
<ClientTable
schema={schema}
tableData={data}
locale="en"
styles={styles}
// ...other props
/>
);Style Options
| Option | Type | Description |
|--------|------|-------------|
| container | string | Wrapper container classes |
| table | string | Table element classes |
| head.body | string | Table header body classes |
| head.row | string | Header row classes |
| head.cell | string | Header cell classes |
| body.body | string | Table body classes |
| body.row | string | Body row classes |
| body.cell | string | Body cell classes |
| variables | Record<string, string> | CSS custom properties |
CSS Variables
Use CSS variables for theming with design tokens:
const styles: AppsmithTableStyles = {
head: {
body: "bg-[var(--primary)] text-[var(--primary-foreground)]",
row: "hover:bg-[var(--primary-foreground)] hover:text-[var(--primary)]",
cell: "border-b border-[var(--border)]"
},
body: {
body: "bg-[var(--card)] text-[var(--card-foreground)]",
row: "odd:bg-[var(--accent)] even:bg-[var(--card)] hover:bg-[var(--muted)]",
cell: "border-b border-[var(--border)]"
},
container: "bg-[var(--card)] shadow-md rounded-lg px-1",
variables: {
"--primary": "hsl(220 70% 50%)",
"--primary-foreground": "hsl(0 0% 100%)",
"--card": "hsl(0 0% 100%)",
"--card-foreground": "hsl(0 0% 10%)",
"--border": "hsl(0 0% 90%)",
"--accent": "hsl(43 74% 66%)",
"--muted": "hsl(0 0% 96%)"
}
};Index Column
Add a row index column:
const tableModel = {
// ...
indexColumn: {
enable: true,
pin: "left", // "left" | "right"
className: "bg-gray-100 font-mono"
}
};Events
The table triggers events through the triggerEvent callback:
| Event | Payload | Description |
|-------|---------|-------------|
| onLoadMore | { page, limit } | Infinite scroll pagination request |
| onRedirect | { url } | URL column click |
| rowSelectionAction | { row } | Row selection (if configured) |
| Custom action events | { row } | Row action button clicks |
Example Event Handling in Appsmith
// In your Appsmith custom widget
triggerEvent: (eventName, payload) => {
switch(eventName) {
case 'onView':
appsmith.triggerEvent('onRowView', { data: payload.row });
break;
case 'onEdit':
appsmith.triggerEvent('onRowEdit', { data: payload.row });
break;
case 'onDelete':
appsmith.triggerEvent('onRowDelete', { data: payload.row });
break;
case 'onLoadMore':
// Fetch next page
appsmith.triggerEvent('fetchNextPage', {
page: payload.page,
limit: payload.limit
});
break;
}
}Appsmith Integration
This table is designed for tight integration with Appsmith's ecosystem:
- Data Sources: Works with any Appsmith-supported data source (PostgreSQL, MongoDB, Elasticsearch, REST APIs, GraphQL)
- Infinite Scroll: Triggers
onLoadMoreevents that you wire to Appsmith queries for pagination - Event System: All actions flow through Appsmith's
triggerEventandupdateModelAPIs - Client-Side Rendering: All rendering happens in the browser; Appsmith handles data fetching
- pgrest/dbtorest Compatible: Pagination parameters match common REST patterns
How It Works
- Your Appsmith query fetches data from any supported source
- Data is passed to
ClientTablevia thetableDataprop - User interactions trigger events (row clicks, actions, load more)
- You handle events in Appsmith to update queries or trigger workflows
- Updated data flows back to the table
Development
- Clone and install dependencies.
- Run
npm run storybookfor live component development. - Add stories, ensure lint/tests pass, then open a PR.
git clone https://github.com/smarts-uz/appsmith-custom-table.git
cd appsmith-custom-table
npm install
npm run storybookAvailable Scripts
| Script | Description |
|--------|-------------|
| npm run storybook | Start Storybook dev server on port 6006 |
| npm run build | Build the library for production |
| npm run build-storybook | Build static Storybook site |
| npm run lint | Run ESLint |
Contributing
⚠️ Note: This project is not actively maintained. It was created as a job task to solve a specific problem. If you need new features or extended functionality, please fork this repository and develop your own version.
Forking This Project (Recommended)
Since this project is not actively maintained, forking is the recommended approach:
- Fork the repository — Click the "Fork" button on GitHub
- Clone your fork
git clone https://github.com/YOUR_USERNAME/appsmith-custom-table.git cd appsmith-custom-table - Make it your own — Add features, fix bugs, customize as needed
- Publish your fork — You can publish to npm under your own package name
Contributing Back (Critical Fixes Only)
For critical bug fixes that benefit everyone:
- Create a feature branch from your fork
- Make minimal, focused changes
- Run checks:
npm run lint && npm run build - Submit a PR with clear description
Note: Response times may be slow and PRs may not be reviewed promptly.
Attribution
If you fork and extend this project, please:
- Keep the MIT license
- Credit the original
tablecnproject for inspiration - Link back to this repository
License
MIT © Ramz001
Acknowledgements
- Thanks to the Appsmith community for the platform that inspired this widget
- Thanks to the
tablecnproject for foundational inspiration - This project was created as a job task at Smarts to demonstrate Appsmith custom widget capabilities
