Use when building a list, table, grid, or virtualized scrolling UI in Positron, or touching `DataGridInstance` / `PositronDataGrid`. Avoids the common mistake of mediating props into the instance via useEffect.
77
96%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Positron has a virtualized grid system built around an abstract DataGridInstance and a renderer component <PositronDataGrid />. The pattern is non-obvious from outside, and the wrong instinct is to build a React wrapper component that takes props and pushes them into the instance via useEffect. Don't. This skill exists so you don't make that mistake.
A DataGridInstance subclass IS the data grid.
The instance owns:
onDidUpdate, custom events from the subclass)<PositronDataGrid instance={...} /> is a thin React renderer over any DataGridInstance subclass. It does not own state. It just observes the instance and paints.
The shared shape, regardless of data strategy:
// 1. Pick or build a DataGridInstance subclass.
// - Embed strategy: subclass PositronListInstance, or DataGridInstance directly.
// - fetchData strategy: subclass DataGridInstance and override fetchData().
// 2. The caller creates exactly one instance for the component's lifetime.
const [instance] = useState(() => new MySubclass({ /* options */ }));
// 3. The caller subscribes to instance events as needed.
useEffect(() => {
const d = instance.onDidSomething(payload => /* ... */);
return () => d.dispose();
}, [instance]);
// 4. Dispose on unmount.
useEffect(() => () => instance.dispose(), [instance]);
// 5. Render the grid.
return <PositronDataGrid instance={instance} />;How data gets into the instance depends on the strategy:
Embed strategy (e.g. PositronListInstance): the caller pushes items in via a setter the subclass exposes. Example:
useEffect(() => {
instance.setItems(items);
}, [instance, items]);This setter is specific to PositronListInstance; other embed subclasses can expose whatever shape they want.
fetchData strategy (e.g. TableDataDataGridInstance): the caller does not push data in. The subclass owns its data source - typically wired to a comm/backend in its constructor or via a method that connects it. The base class then calls fetchData(...) whenever the viewport needs cells, and the subclass populates its cache and returns.
Either way: no React wrapper component sits between the caller and the instance. The instance is the API surface.
Don't build <MyList items={...} renderItem={...} onActivate={...} /> as a React component that internally:
useMemo / useStateuseEffect(() => instance.setX(props.x), [props.x])Symptoms that you're heading the wrong way:
useEffects that all just call setters on the instance.useEffect that re-creates the instance when one of its construction options changes (and a paired disposal effect to handle the swap).If you see those, delete the wrapper. Let the caller drive the instance directly.
A DataGridInstance subclass can manage data in one of two ways. Pick based on dataset size.
For small-to-medium lists where the entire dataset fits comfortably in memory.
setItems).fetchData() is implemented as a no-op.Example: PositronListInstance.
fetchData for lazy/virtualized dataFor huge datasets where holding everything in memory is infeasible. The base class calls fetchData(rowStartIndex, rowCount, columnStartIndex, columnCount) (or similar) when it needs the cells for the visible viewport.
fetchData populates the cache for the requested window.cell(col, row) returns from the cache.Examples: TableDataDataGridInstance, TableSummaryDataGridInstance, InlineTableDataGridInstance, ColumnSelectorDataGridInstance.
You generally know which you need before writing a line: is the dataset bounded and small? Embed. Is it backed by a query/file/comm and possibly enormous? Implement fetchData.
The subclass passes options to super(...) in its constructor - column/row headers, scrollbars (and overscroll), pinning, selection, resizing, automatic layout, etc. The same <PositronDataGrid /> renderer adapts to all of these via the instance's configuration.
If you need a behavior the base class doesn't expose, the path is usually:
DataGridInstance's options type and constructor.This is invasive - many subclasses share the base - so weigh the cost before going there.
PositronListInstance - single-column virtualized list (embed strategy).TableDataDataGridInstance - main data explorer table (fetchData strategy).TableSummaryDataGridInstance - column summary panel (fetchData strategy).InlineTableDataGridInstance - inline data preview in notebook outputs (fetchData strategy).ColumnSelectorDataGridInstance - column picker in modals (fetchData strategy).Skim two before writing a new one.
DataGridInstance (or an existing subclass), implement what's required.useState(() => new MySubclass(...)).<PositronDataGrid instance={instance} />.b87c504
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.