| name | ag-grid-patterns |
| description | AG-Grid v34 integration patterns for TMNL. Invoke when implementing data grids, custom cell renderers, themes, or grid-based UI. Provides canonical file locations and pattern precedents. |
| model_invoked | true |
| triggers | ["AG-Grid","ag-grid","data grid","DataGrid","TmnlDataGrid","cell renderer","column definition","rowData"] |
AG-Grid Patterns for TMNL
Critical: AG-Grid v34 Module Registration
Without this, grid renders blank. No exceptions.
import { ModuleRegistry, AllCommunityModule } from 'ag-grid-community'
ModuleRegistry.registerModules([AllCommunityModule])
No CSS imports needed when using the theme prop.
Canonical Sources
TMNL Implementations
- tldraw shape:
src/components/tldraw/shapes/data-grid-shape.tsx
- Theme tokens:
src/components/tldraw/shapes/data-grid-theme.ts
- Base component:
src/components/data-grid/TmnlDataGrid.tsx
- Testbed:
src/components/testbed/DataGridTestbed.tsx
- Architecture doc:
assets/documents/AG_GRID_THEMING_ARCHITECTURE.md
AG-Grid Documentation
TMNL_TOKENS Design System
All grid styling derives from TMNL_TOKENS in data-grid-theme.ts:
export const TMNL_TOKENS = {
colors: {
background: '#0a0a0a',
surface: '#111111',
surfaceAlt: '#1a1a1a',
border: '#333333',
borderSubtle: '#222222',
text: {
primary: '#c8e4d8',
secondary: '#8ba89e',
muted: '#5a7a6e',
accent: '#00ffcc',
},
status: {
success: '#00cc88',
warning: '#ffaa00',
error: '#ff4444',
info: '#00aaff',
},
},
typography: {
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
sizes: { xs: 11, sm: 12, base: 13, lg: 14 },
},
spacing: { base: 4, scales: [0.5, 1, 2, 3, 4, 6, 8] },
: { : , : },
: { : { : , : } },
}
Core Patterns
Pattern 1: Theme Creation via themeQuartz
import { themeQuartz } from 'ag-grid-community'
import { TMNL_TOKENS } from './data-grid-theme'
export const tmnlDataGridTheme = themeQuartz.withParams({
backgroundColor: TMNL_TOKENS.colors.background,
foregroundColor: TMNL_TOKENS.colors.text.primary,
borderColor: TMNL_TOKENS.colors.border,
headerBackgroundColor: TMNL_TOKENS.colors.surface,
headerTextColor: TMNL_TOKENS.colors.text.secondary,
oddRowBackgroundColor: TMNL_TOKENS.colors.surface,
rowHoverColor: TMNL_TOKENS.colors.surfaceAlt,
selectedRowBackgroundColor: `${TMNL_TOKENS.colors.text.accent}15`,
fontFamily: TMNL_TOKENS.typography.fontFamily,
fontSize: TMNL_TOKENS.typography..,
: ...,
: ..,
: ..,
: ..,
})
Canonical source: src/components/tldraw/shapes/data-grid-theme.ts
Pattern 2: Custom Cell Renderers
Cell renderers are React components receiving ICellRendererParams.
import type { ICellRendererParams } from 'ag-grid-community'
const IdCellRenderer = (params: ICellRendererParams) => (
<span
style={{
color: TMNL_TOKENS.colors.text.muted,
fontSize: TMNL_TOKENS.typography.sizes.xs,
fontFamily: TMNL_TOKENS.typography.fontFamily,
letterSpacing: '0.05em',
}}
>
{params.value}
</span>
)
const ValueCellRenderer = (params: ICellRendererParams) => {
const value = params.value as number
const percentage = Math.min(100, Math.max(0, value))
return (
<div style={{ position: 'relative', width: '100%', height: '%' }}>
{value.toFixed(1)}
)
}
= () => {
status = params.
color = [status] ?? ...
(
)
}
Canonical source: src/components/tldraw/shapes/data-grid-shape.tsx
Pattern 3: Column Definitions
Type-safe column definitions with cell renderers.
import type { ColDef } from 'ag-grid-community'
const columnDefs: ColDef[] = [
{
field: 'id',
headerName: 'ID',
width: 80,
cellRenderer: IdCellRenderer,
sortable: true,
},
{
field: 'name',
headerName: 'Name',
flex: 1,
minWidth: 120,
filter: true,
},
{
field: 'value',
headerName: 'Value',
width: 120,
cellRenderer: ValueCellRenderer,
sortable: true,
comparator: (a, b) => a - b,
},
{
field: 'status',
headerName: 'Status',
width: 100,
cellRenderer: StatusCellRenderer,
},
]
Pattern 4: tldraw Shape Integration
AG-Grid as a tldraw custom shape.
import { BaseBoxShapeUtil, TLBaseShape } from 'tldraw'
import { AgGridReact } from 'ag-grid-react'
type DataGridWidgetShape = TLBaseShape<
'data-grid-widget',
{
w: number
h: number
rowData: unknown[]
columnDefs: ColDef[]
}
>
export class DataGridWidgetShapeUtil extends BaseBoxShapeUtil<DataGridWidgetShape> {
static override type = 'data-grid-widget' as const
override getDefaultProps(): DataGridWidgetShape['props'] {
return {
w: 600,
h: 400,
rowData: [],
columnDefs: defaultColumnDefs,
}
}
override component(shape: DataGridWidgetShape) {
return (
<>
)
}
}
Canonical source: src/components/tldraw/shapes/data-grid-shape.tsx
Pattern 5: effect-atom Integration
Grid rowData from atoms (Atom-as-State pattern).
import { Atom } from '@effect-rx/rx-react'
const rowDataAtom = Atom.make<RowData[]>([])
const columnDefsAtom = Atom.make<ColDef[]>(defaultColumnDefs)
const dataService = {
loadData: () => Effect.gen(function* () {
const data = yield* fetchData()
Atom.set(rowDataAtom, data)
}),
addRow: (row: RowData) => Effect.gen(function* () {
const current = Atom.get(rowDataAtom)
Atom.set(rowDataAtom, [...current, row])
}),
}
function DataGrid() {
const rowData = useAtomValue(rowDataAtom)
const columnDefs = useAtomValue(columnDefsAtom)
return (
<AgGridReact
=
=
=
/>
)
}
Canonical source: src/components/testbed/DataManagerTestbed.tsx
Pattern 6: Dynamic Column Generation
Generate columns from data schema.
import { Schema } from 'effect'
const generateColumnDefs = <A extends Schema.Struct.Fields>(
schema: Schema.Struct<A>
): ColDef[] => {
return Object.entries(schema.fields).map(([field, fieldSchema]) => ({
field,
headerName: field.charAt(0).toUpperCase() + field.slice(1),
cellRenderer: inferRenderer(fieldSchema),
}))
}
const inferRenderer = (schema: Schema.Schema.Any) => {
if (Schema.is(Schema.Number)(schema)) return ValueCellRenderer
if (hasTag(schema, 'Status')) return StatusCellRenderer
return undefined
}
Pattern 7: Grid Performance Optimization
For large datasets in tldraw shapes.
<AgGridReact
theme={tmnlDataGridTheme}
rowData={rowData}
columnDefs={columnDefs}
animateRows={false}
suppressCellFocus={true}
suppressRowClickSelection
rowBuffer={10}
immutableData={true}
getRowId={(params) => params.data.id}
/>
Theme Variants
Compact Theme
const tmnlCompactTheme = tmnlDataGridTheme.withParams({
rowHeight: 24,
headerHeight: 28,
fontSize: 11,
})
High Contrast Theme
const tmnlHighContrastTheme = tmnlDataGridTheme.withParams({
foregroundColor: '#ffffff',
borderColor: '#666666',
selectedRowBackgroundColor: `${TMNL_TOKENS.colors.text.accent}30`,
})
Filing New Patterns
When you create a new AG-Grid pattern:
- Implement in TMNL first - Working code in
src/components/data-grid/
- Add to theme tokens - Extend
TMNL_TOKENS if needed
- Update architecture doc - Add to
assets/documents/AG_GRID_THEMING_ARCHITECTURE.md
- Update this skill - Add pattern with canonical source
- Create bead - Track with
bd create --type=task --title="Document X grid pattern"
Anti-Patterns (BANNED)
Importing CSS Files
import 'ag-grid-community/styles/ag-grid.css'
import 'ag-grid-community/styles/ag-theme-quartz.css'
Missing Module Registration
<AgGridReact rowData={data} columnDefs={cols} />
useState for Grid Data
const [rowData, setRowData] = useState([])
Related Ecosystem
- Agent:
.claude/agents/ag-grid-specialist.md (TODO)
- Command:
.claude/commands/grid.md (TODO)
- Hook:
.claude/hooks/ag-grid-patterns.json (TODO)