Skip to content

Case 2: GitHub Contributors Dashboard

This demo shows the top 30 contributors of the facebook/react repository.

It uses the GitHub API to fetch contributor data and displays it in a grid.

Loading contributors data...
📄 View Source Code

Converting an API Response to a Matrix with oa2aa

Section titled “Converting an API Response to a Matrix with oa2aa”

oa2aa (Object Array to Array Array) is a utility that converts an array of objects into the 2D array format that GridSheet expects.

const fields = ['id', 'avatar_url', 'login', 'html_url', 'contributions'];
const response = await request('GET /repos/{owner}/{repo}/contributors', { ... });
setData(oa2aa(response.data as { [s: string]: any }[], fields));

Specify the keys you want to extract in fields in the desired order, and each object will be projected into a row in that column order. The order of fields directly determines the column order.

Loading Dynamic Data with buildInitialCellsFromOrigin

Section titled “Loading Dynamic Data with buildInitialCellsFromOrigin”

After fetching data from an API, pass the matrix to buildInitialCellsFromOrigin to populate the grid starting from A1. Unlike the matrices key in buildInitialCells, the origin is always fixed at A1.

<GridSheet
book={book}
sheetName="contributors"
initialCells={buildInitialCellsFromOrigin({
matrix: data, // the state value from useState
cells: { ... }, // column styles and header definitions go here
})}
/>

Although initialCells is recomputed whenever data changes, GridSheet only reads it on first initialization. Toggling rendering with the loading flag is the reliable way to handle this.

Post-processing Rendered Output with renderCallback

Section titled “Post-processing Rendered Output with renderCallback”

renderCallback on PolicyMixinType is a hook that runs after the type-specific render method (renderString, renderNumber, etc.) has executed. It is ideal for adding decorations that are independent of the cell’s value type — such as badges or address overlays.

const AddressOverlayMixin: PolicyMixinType = {
renderCallback(rendered: any, { point }: RenderProps) {
const address = p2a(point);
return (
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
<span style={{ position: 'absolute', top: 2, right: 4, fontSize: '9px', color: '#aaa' }}>
{address}
</span>
{rendered}
</div>
);
},
};

rendered is the JSX already returned by renderString or similar. p2a(point) converts the { y, x } coordinate to a human-readable address string like "A1".

A single Policy can combine multiple mixins. Later mixins override earlier ones, so place shared behaviors first and specific overrides last.

const book = useSpellbook({
policies: {
avatar: new Policy({
mixins: [
ImagePolicyMixin, // renders the image via renderString
{ renderColHeaderLabel: () => 'Avatar' }, // overrides the column header label
AddressOverlayMixin, // adds the address badge via renderCallback
],
}),
contributions: new Policy({
mixins: [
ThousandSeparatorPolicyMixin, // formats numbers with thousand separators
{ renderColHeaderLabel: () => 'Contributions' },
AddressOverlayMixin,
],
}),
},
});

Dropping in an inline object with renderColHeaderLabel is all you need to customize a column’s header label.

This built-in mixin from @gridsheet/react-core renders numbers with thousand separators (1,234,567).

import { ThousandSeparatorPolicyMixin } from '@gridsheet/react-core';
new Policy({ mixins: [ThousandSeparatorPolicyMixin] })

When the number of columns is fixed by the data source, use the limits option to set equal minCols and maxCols. This prevents users from accidentally adding or removing columns.

options={{
mode: 'dark',
sheetHeight: 500,
sheetWidth: 1000,
limits: { minCols: 5, maxCols: 5 },
}}

Setting mode: 'dark' switches the entire grid to a dark theme with no additional configuration.

Make the API call inside an immediately-invoked async function (IIFE) within useEffect, and toggle rendering with a loading state.

React.useEffect(() => {
(async () => {
try {
setLoading(true);
const response = await request('GET /repos/{owner}/{repo}/contributors', { ... });
setData(oa2aa(response.data as any[], fields));
} catch (error) {
console.error('Failed to fetch data:', error);
} finally {
setLoading(false);
}
})();
}, []);

Calling setLoading(false) in finally ensures the loading indicator disappears even when an error occurs.


This example demonstrates how to display the cell address (e.g. A1, B2) in the top-right corner of every cell using a custom Policy with renderCallback.

This pattern is the recommended replacement for the showAddress option that was available in v2 but removed in v3.

Each cell displays its address (e.g. A1) in the top-right corner using a renderCallback policy.

The key is the renderCallback hook on PolicyMixinType. It receives:

  • rendered — the output already produced by the policy’s type-specific render method (e.g. renderString, renderNumber)
  • props — a RenderProps object including point: { y, x }

Use p2a(point) (exported from @gridsheet/react-core) to convert the coordinates to a human-readable address string.

renderCallback is called after the primary render method resolves the value. This makes it ideal for decorating cells with metadata that is independent of the cell’s value type — such as an address badge, a status icon, or a tooltip trigger.

If you only want the overlay on certain cells (rather than all cells via default), assign the policy per cell or per column:

const initialCells = buildInitialCells({
cells: {
// Only column A shows addresses
A: { policy: 'address' },
A1: { value: 'Alice' },
A2: { value: 'Bob' },
},
});
  • Real-time Data Updates: Live data synchronization with external APIs
  • Caching Strategies: Efficient data caching and refresh mechanisms
  • Error Handling: Robust error handling for API failures
  • Loading States: Professional loading indicators and states
  • Data Validation: Input validation and data integrity checks
  • RESTful APIs: Standard REST API integration patterns
  • Authentication: Handle API authentication and authorization
  • Caching: Implement data caching for improved performance
  • Pagination: Handle large datasets with pagination
  • Webhooks: Real-time data updates using webhooks