Extending

MonkeyTab is extensible through props passed directly to <MonkeyTable>. All extensions are registered on mount and cleaned up on unmount — no global state, no side effects outside the component.


Custom computed functions

Add your own functions to the formula builder. They appear in the picker under your chosen category.

import { MonkeyTable, type FunctionDef } from '@datasketch/monkeytab';

const functions: FunctionDef[] = [
  {
    name:        'percent',
    category:    'number',
    description: 'Percentage of a value relative to a total',
    inputTypes:  ['Number', 'Number'],
    params: [
      { name: 'decimals', type: 'number', default: 1 },
    ],
    compute: ([value, total], params) => {
      const v = Number(value), t = Number(total);
      if (!t || isNaN(v) || isNaN(t)) return null;
      const d = Number(params?.decimals ?? 1);
      return Math.round((v / t) * 100 * 10 ** d) / 10 ** d;
    },
  },
];

<MonkeyTable functions={functions} columns={columns} rows={rows} />

The FunctionDef interface:

Field Type Description
name string Unique function name
category 'text' | 'number' | 'date' | 'logic' Groups in the picker
description string Shown in the formula builder
inputTypes FieldType[] Expected column types, in order
params Array<{name, type, default?}> Optional named parameters
compute (inputs, params?) => Value The computation

Custom field constraints

Add domain-specific validators. Constraints appear in the column options panel and show inline errors on invalid values.

import { type FieldTypeConstraint } from '@datasketch/monkeytab';

const constraints: FieldTypeConstraint[] = [
  {
    name:        'postalCode',
    label:       'Postal Code',
    description: 'Must be a 5-digit postal code',
    appliesTo:   ['Text'],
    validate: (value) =>
      /^\d{5}$/.test(String(value ?? ''))
        ? null                              // valid
        : '5-digit code required',          // error message
  },
  {
    name:        'futureDate',
    label:       'Future date',
    description: 'Date must be in the future',
    appliesTo:   ['Date'],
    validate: (value) =>
      value && new Date(String(value)) > new Date()
        ? null
        : 'Must be a future date',
  },
];

<MonkeyTable constraints={constraints} columns={columns} rows={rows} />

The validate function receives:

  • value — the current cell value
  • params — parameter values if the constraint has params
  • field{ id: string, type: FieldType } — the field being validated

Return null for valid, a string for an error message.


Custom type-level renderers

Override the built-in renderer for a field type. Applies to all columns of that type in this <MonkeyTable> instance.

import { type CellRendererProps } from '@datasketch/monkeytab';

function HeartRating({ value }: CellRendererProps) {
  const n = typeof value === 'number' ? Math.max(0, Math.min(5, value)) : 0;
  return (
    <span style={{ fontSize: 14, letterSpacing: 1 }}>
      {'❤️'.repeat(n)}{'🤍'.repeat(5 - n)}
    </span>
  );
}

<MonkeyTable
  renderers={{ Rating: HeartRating }}
  columns={columns}
  rows={rows}
/>

CellRendererProps:

Prop Type Description
value Value The cell value
field FieldSpec The column definition (id, type, options)
rowId string The row's id
cellHeight number | undefined Current cell height in px

Custom type-level editors

Override the built-in editor for a field type.

import { type CellEditorProps } from '@datasketch/monkeytab';

function SliderEditor({ value, field, onSave, onCancel }: CellEditorProps) {
  const opts = field.options as { min?: number; max?: number } | undefined;
  const [val, setVal] = useState(typeof value === 'number' ? value : 0);

  return (
    <div style={{ padding: 12, background: '#fff', border: '1px solid #e5e7eb', borderRadius: 8 }}>
      <input
        type="range"
        min={opts?.min ?? 0}
        max={opts?.max ?? 100}
        value={val}
        onChange={(e) => setVal(Number(e.target.value))}
        style={{ width: '100%' }}
      />
      <div style={{ textAlign: 'center', marginTop: 4 }}>{val}</div>
      <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
        <button onClick={() => onSave(val)}>Save</button>
        <button onClick={onCancel}>Cancel</button>
      </div>
    </div>
  );
}

<MonkeyTable
  editors={{ Number: SliderEditor }}
  columns={columns}
  rows={rows}
/>

CellEditorProps:

Prop Type Description
value Value Current cell value
field FieldSpec The column definition
rowId string The row's id
onSave (value: Value) => void Call to commit the new value
onCancel () => void Call to discard changes
onUpload (file, fieldType) => Promise<string> Forwarded onUpload prop (for file-type editors)

Per-column custom renderer

To render a single column differently (without overriding the whole type), use the render prop on the column definition:

<MonkeyTable
  columns={[
    { id: 'Name' },
    {
      id: 'Health',
      type: 'Number',
      render: (value, row, fieldId) => {
        const n = typeof value === 'number' ? value : 0;
        const color = n > 80 ? '#16a34a' : n > 50 ? '#d97706' : '#dc2626';
        return (
          <span style={{ color, fontWeight: 600 }}>
            {n}%
          </span>
        );
      },
    },
  ]}
  rows={rows}
/>

The render function signature: (value: Value, row: Record<string, Value>, fieldId: string) => ReactNode.


Custom translations (i18n)

Override individual UI strings via the translations prop. All ~288 string keys are typed as I18nStrings:

<MonkeyTable
  locale="es-CO"
  language="es"
  translations={{
    'toolbar.addRow':              'Nueva fila',
    'toolbar.search.placeholder':  'Buscar…',
    'toolbar.filter':              'Filtrar',
    'column.delete':               'Eliminar columna',
    'fieldType.SingleSelect':      'Selección única',
  }}
  columns={columns}
  rows={rows}
/>

Built-in languages: English (en) and Spanish (es). For other languages, provide the full translations object.


Lifecycle notes

  • All functions, constraints, renderers, and editors are registered when the component mounts and unregistered when it unmounts.
  • If two <MonkeyTable> instances register the same function/constraint name, the second wins while it's mounted; the first is restored when the second unmounts.
  • Registry extensions are scoped to the component instance — multiple tables on the same page are fully independent.

Built-in function catalog

27 functions are built in:

Category Functions
Text concat, upper, lower, trim, length, slice, replace, startsWith, endsWith, contains
Number round, floor, ceil, abs, percent, sum, avg
Date dateFormat, dateDiff, dateAdd, now
Logic if, and, or, not, coalesce

Built-in constraint catalog

9 constraints are built in: required, minLength, maxLength, min, max, pattern, email, url, enum.