Implement editable grids with custom input types and cell actions using TableGX.

Install

mkdir -p .claude/skills/tablegx-editing && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/11163" && unzip -o skill.zip -d .claude/skills/tablegx-editing && rm skill.zip

Installs to .claude/skills/tablegx-editing

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Inline editing with EditableTable: editableColumnIds, meta.editable, inputType (text|number|boolean|select), onSaveEdit return false to keep editor open, singleClickEdit, columnGroups, CellAction buttons, selectColumn/booleanColumn. Use when implementing editable grids, cell action buttons, or column meta.
307 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Enable inline editing
  • Define column metadata
  • Set input types
  • Implement save callbacks

How it works

It provides utilities for defining column metadata and input types to enable inline editing in TableGX.

Inputs & outputs

You give it
Table data
You get back
Editable grid

When to use tablegx-editing

  • Enable inline editing in TableGX
  • Define custom table columns
  • Add cell action buttons
  • Set up input types for grids

About this skill

@twentygx/tablegx — Editing & Columns

Setup

import {
  EditableTable,
  textColumn,
  numberColumn,
  booleanColumn,
  selectColumn,
} from '@twentygx/tablegx'

type Row = { id: string; dba: string; beds: number; isActive: boolean; state: string }

const STATE_OPTIONS = ['TX', 'CA', 'NY'].map((s) => ({ label: s, value: s }))

<EditableTable<Row>
  data={data}
  getRowId={(r) => r.id}
  editableColumnIds={['dba', 'beds', 'isActive', 'state']}
  onSaveEdit={async (row, columnId, value) => {
    const ok = await api.patch(row.id, { [columnId]: value })
    return ok
  }}
  singleClickEdit
  columns={[
    textColumn('dba', 'DBA', { editable: true, inputType: 'text' }),
    numberColumn('beds', 'Beds', { editable: true, inputType: 'number', footerAggregate: 'sum' }),
    booleanColumn('isActive', 'Active', { editable: true, inputType: 'boolean' }),
    selectColumn('state', 'State', STATE_OPTIONS, { editable: true }),
  ]}
/>

Core Patterns

TableColumnMeta (via column meta)

KeyPurpose
editable, inputType, selectOptionsInline editing
measureText(row)Auto-width string for custom/non-text cells
fixedMeasureWidthFixed px width (icon/action columns)
measureWidth(row)Exact per-row content width (px) when width isn't a function of any text (sparkline, chips, image grid); takes precedence over the other two
maxColumnWidthPer-column auto-size clamp
footerAggregate, footerFormat, footerLabelFooter row
actionsDeclarative cell action buttons (or custom-rendered controls)
renderCell(ctx)Full control of cell content (non-truncating, flexible)
onCellClick(ctx, e)Make the whole cell clickable, isolated from selection/expand/edit
disableTruncateOpt the value area out of single-line truncation
searchableSet false to exclude the column from the built-in global search (enableGlobalSearch); see tablegx-advanced

Module augmentation: ColumnMeta extends TableColumnMeta.

Cell actions

{
  id: 'actions',
  header: '',
  enableSorting: false,
  enableColumnFilter: false,
  meta: {
    fixedMeasureWidth: 96,
    actions: [
      {
        id: 'delete',
        icon: <TrashIcon />,
        ariaLabel: 'Delete',
        variant: 'destructive',
        confirm: { title: 'Delete row?', confirmLabel: 'Delete' },
        onClick: async (row) => { await api.delete(row.id) },
        isHidden: (row) => row.isLocked,
        isDisabled: (row) => !row.canDelete,
      },
    ],
  },
}

Clicks stop propagation before onClick. Icon-only buttons require ariaLabel.

For anything the declarative button can't express (popover triggers, menus), use a custom action — { id, render: (row) => <Control /> } in the same actions array. The slot click-isolates the control automatically (no selection/expand/edit leak).

Edit keyboard / commit

  • Enter commits (Shift+Enter newline in text)
  • Escape cancels
  • blur commits
  • Tab / Shift+Tab commits and moves to adjacent editable cell
  • singleClickEdit: boolean cells use interactive checkboxes directly

Detecting editability from outside the table

[data-tgx-editable] reflects effective, per-user editability, not just the editable prop. On a single table it's present only when editable is true AND at least one currently-visible column is actually editable — the same gating cells use, so a column that's nominally editable but excluded by editableColumnIds or hidden via the visibility picker doesn't count. On TabbedTable/IndependentTabbedTable the attribute lives on the tabbed container and is present if any tab is editable, even while the active tab is read-only. Use document.querySelector('[data-tgx-editable]') (or plain CSS) to detect "this user can edit something here" without threading editability state through your own app.

Source: README.md

Row height

rowHeight (number | 'auto' | (row) => number, default fixed 56px) is a shared table prop that applies to EditableTable too. Use 'auto' when edited values need to wrap to multiple lines (cells top-align and wrap, with 56px as the floor); a number or (row) => number sets explicit per-row heights with no measurement. See tablegx-advanced/SKILL.md → Row height for the full behavior and virtualization tradeoffs.

Source: src/types.ts

Column factories

textColumn, numberColumn, booleanColumn, selectColumn, dateColumn, badgeColumn, customColumn — each enables filtering with tgxFilterFn by default.

Custom cell rendering

customColumn(id, header, render, meta?) (or meta.renderCell) takes a typed CellRenderContext ({ row, value, columnId, column, table, isEditing }) and renders into a non-truncating, horizontally-flexible container — multiple badges, wrapping content, or interactive controls sit side by side instead of being clipped:

import { customColumn, CellOverflowList, cellInteractionProps } from '@twentygx/tablegx'

customColumn<Row>('tags', 'Tags', ({ row }) => (
  <CellOverflowList>
    {row.tags.map((t) => <Badge key={t}>{t}</Badge>)}
  </CellOverflowList>
), { measureText: (row) => row.tags.join(' ') })
  • Custom content has no inferable text — always pair with measureText / fixedMeasureWidth for auto-sizing.
  • CellOverflowList shows as many inline items as fit, collapsing the rest into a +N pill (DOM-measured, re-measures on resize).
  • For interactive children inside a custom cell, spread cellInteractionProps (or call isolateCellEvent) so their clicks don't trigger selection/expand/edit.
  • meta.onCellClick(ctx, e) makes the whole cell clickable; on an editable column it does NOT also auto-enter edit.

Common Mistakes

CRITICAL meta.editable without editableColumnIds whitelist

Wrong:

columns={[textColumn('name', 'Name', { editable: true })]}
<EditableTable data={data} columns={columns} onSaveEdit={save} editableColumnIds={[]} />

Correct:

<EditableTable
  editableColumnIds={['name']}
  columns={[textColumn('name', 'Name', { editable: true, inputType: 'text' })]}
  onSaveEdit={save}
/>

Both meta.editable: true and editableColumnIds must include the column id.

Source: README.md

HIGH onSaveEdit swallows errors

Wrong:

onSaveEdit={async () => {
  await api.patch(...).catch(console.error)
  return true
}}

Correct:

onSaveEdit={async (row, col, val) => {
  try {
    await api.patch(row.id, { [col]: val })
    return true
  } catch {
    return false
  }
}}

Returning false keeps the editor open for retry.

Source: src/types.ts

HIGH Custom render without width hints

Wrong:

{
  id: 'status',
  cell: ({ getValue }) => <Badge>{String(getValue())}</Badge>,
}

Correct:

badgeColumn('status', 'Status')
// or meta: { measureText: (row) => String(row.status) }
// or meta: { fixedMeasureWidth: 80 } for icon columns

Auto column widths use pre-paint text measurement; custom cells need measureText or fixedMeasureWidth.

Source: README.md

See also: tablegx-advanced/SKILL.md — editable TabbedTable tabs

When not to use it

  • Non-TableGX grids
  • Simple read-only tables

Prerequisites

tablegx

Limitations

  • Requires TableGX library

How it compares

It offers declarative editing patterns, unlike manual input handling.

Compared to similar skills

tablegx-editing side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
tablegx-editing (this skill)01moNo flagsIntermediate
Onboarding Flow Optimizer04moNo flagsAdvanced
email-template-builder04moReviewIntermediate
ui-ux-pro-max1,9095moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

Onboarding Flow Optimizer

jamiojala

Reshape onboarding sequences around progressive disclosure and measurable activation rather than generic first-run screens.

00

email-template-builder

LeoYeAI

Email Template Builder

00

ui-ux-pro-max

nextlevelbuilder

UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient.

1,9092,023

nextjs-developer

zenobi-us

Expert Next.js developer mastering Next.js 14+ with App Router and full-stack features. Specializes in server components, server actions, performance optimization, and production deployment with focus on building fast, SEO-friendly applications.

328531

screenshot-to-code

OneWave-AI

Convert UI screenshots into working HTML/CSS/React/Vue code. Detects design patterns, components, and generates responsive layouts. Use this when users provide screenshots of websites, apps, or UI designs and want code implementation.

204389

zustand

lobehub

Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.

113434

Search skills

Search the agent skills registry