Columns
Column Autosizing Copy Page Open additional page export options
LyteNyte Grid can automatically size columns to fit their content. Columns do not need to be visible to be autosized. The grid uses heuristic functions to determine optimal widths based on content.
Autosizing can consider header width, cell content width, or both. Each column can
define autosizeHeaderFn and autosizeCellFn to control header and cell
width calculations. If you omit these functions, LyteNyte Grid
uses a default text measurement function.
LyteNyte Grid encourages custom header and cell renderers. When using
custom renderers, provide matching autosize functions to ensure accurate sizing.
Use the api.autosizeColumns method to autosize all columns or
a selected subset. The demo below shows this behavior.
Clicking Autosize Cells triggers the autosizing logic.
Cell Autosize Autosize Cells Reset Columns
import " @1771technologies/lytenyte-pro/components.css " ;
import " @1771technologies/lytenyte-pro/light-dark.css " ;
PercentCellPositiveNegative ,
} from " ./components.jsx " ;
import type { DEXPerformanceData } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { data } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { Grid , measureText , useClientDataSource } from " @1771technologies/lytenyte-pro " ;
import { useRef , useState } from " react " ;
export interface GridSpec {
readonly data : DEXPerformanceData ;
const initialColumns : Grid . Column < GridSpec > [] = [
cellRenderer : SymbolCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
measureText ( `${ data . symbol . split ( " / " )[ 0 ] . trim () }${ data . symbolTicker }` , p . api . viewport ()) ?. width ??
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : NetworkCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . network , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : ExchangeCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . exchange , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Change " , " 24h " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1w " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 3m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 6m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " YTD " ) ,
{ id : " volatility " , cellRenderer : PercentCell , name : " Volatility " , type : " number " },
cellRenderer : PercentCell ,
headerRenderer : makePerfHeaderCell ( " Volatility " , " 1m " ) ,
const base : Grid . ColumnBase < GridSpec > = { width : 80 };
export default function ColumnDemo () {
const ds = useClientDataSource ( { data : data } ) ;
const ref = useRef < Grid . API < GridSpec >> ( null ) ;
const [ columns , setColumns ] = useState ( initialColumns ) ;
< div className = " border-ln-border flex gap-4 border-b px-2 py-2 " >
ref . current ?. columnAutosize ();
setColumns ( initialColumns );
className = " ln-grid ln-cell:text-xs ln-header:text-xs ln-header:text-ln-text-xlight "
< Grid columns = { columns } onColumnsChange = { setColumns } columnBase = { base } rowSource = { ds } ref = { ref } />
import type { ClassValue } from " clsx " ;
import { twMerge } from " tailwind-merge " ;
import { exchanges , networks , symbols } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
export function tw ( ... c : ClassValue [] ) {
return twMerge ( clsx ( ... c )) ;
import type { Grid } from " @1771technologies/lytenyte-pro " ;
import type { GridSpec } from " ./demo " ;
export function SymbolCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const ticker = row . data . symbolTicker ;
const symbol = row . data . symbol ;
const image = symbols [ row . data . symbolTicker ] ;
< div className = " grid grid-cols-[20px_auto_auto] items-center gap-1.5 " >
alt = { ` Logo for symbol ${ symbol }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " bg-ln-gray-20 text-ln-text-dark flex h-fit items-center justify-center rounded-lg px-2 py-px text-[10px] " >
< div className = " w-full overflow-hidden text-ellipsis " >{ symbol . split ( " / " )[ 0 ]}</ div >
export function NetworkCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . network ;
const image = networks [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for network ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function ExchangeCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . exchange ;
const image = exchanges [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for exchange ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function PercentCellPositiveNegative ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
" h-ful flex w-full items-center justify-end tabular-nums " ,
field < 0 ? " text-red-600 dark:text-red-300 " : " text-green-600 dark:text-green-300 " ,
export function PercentCell ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
return < div className = " h-ful flex w-full items-center justify-end tabular-nums " >{ value }</ div >;
export const makePerfHeaderCell = ( name : string , subname : string ) => {
return ( _ : Grid . T . HeaderParams < GridSpec >) => {
< div className = " flex h-full w-full flex-col items-end justify-center tabular-nums " >
< div className = " text-ln-text-light font-mono uppercase " >{ subname }</ div >
The Autosize Cells button calls api.autosizeColumns to compute and apply
new widths. When you call this method without arguments,
the grid autosizes every column. Autosizing is a one off operation, if
cell data changes later, the grid does not autosize again.
Call api.autosizeColumns whenever updated content requires resizing.
The api.autosizeColumns method accepts an option that includes a column’s header
content in the calculation. You can customize how header width
is measured using autosizeHeaderFn. This is useful when header text
is longer than any cell in the column.
In the demo below, the Crypto Currency Symbol, Ticker, and Name column
has a long header, so autosizing expands the column to fit the header.
Autosize Header Text Autosize Including Headers Reset Columns
import " @1771technologies/lytenyte-pro/components.css " ;
import " @1771technologies/lytenyte-pro/light-dark.css " ;
PercentCellPositiveNegative ,
} from " ./components.jsx " ;
import type { DEXPerformanceData } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { data } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { Grid , measureText , useClientDataSource } from " @1771technologies/lytenyte-pro " ;
import { useRef , useState } from " react " ;
export interface GridSpec {
readonly data : DEXPerformanceData ;
const initialColumns : Grid . Column < GridSpec > [] = [
cellRenderer : SymbolCell ,
name : " Crypto Currency Symbol, Ticker, and Name " ,
autosizeHeaderFn : ( p ) => {
const textWidth = measureText ( `${ p . column . name ?? p . column . id }` , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + padding ;
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
measureText ( `${ data . symbol . split ( " / " )[ 0 ] . trim () }${ data . symbolTicker }` , p . api . viewport ()) ?. width ??
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : NetworkCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . network , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : ExchangeCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . exchange , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Change " , " 24h " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1w " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 3m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 6m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " YTD " ) ,
{ id : " volatility " , cellRenderer : PercentCell , name : " Volatility " , type : " number " },
cellRenderer : PercentCell ,
headerRenderer : makePerfHeaderCell ( " Volatility " , " 1m " ) ,
const base : Grid . ColumnBase < GridSpec > = { width : 80 };
export default function ColumnDemo () {
const ds = useClientDataSource ( { data : data } ) ;
const ref = useRef < Grid . API < GridSpec >> ( null ) ;
const [ columns , setColumns ] = useState ( initialColumns ) ;
< div className = " border-ln-border flex gap-4 border-b px-2 py-2 " >
ref . current ?. columnAutosize ({ includeHeader: true });
Autosize Including Headers
setColumns ( initialColumns );
className = " ln-grid ln-cell:text-xs ln-header:text-xs ln-header:text-ln-text-xlight "
< Grid columns = { columns } onColumnsChange = { setColumns } columnBase = { base } rowSource = { ds } ref = { ref } />
import type { ClassValue } from " clsx " ;
import { twMerge } from " tailwind-merge " ;
import { exchanges , networks , symbols } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
export function tw ( ... c : ClassValue [] ) {
return twMerge ( clsx ( ... c )) ;
import type { Grid } from " @1771technologies/lytenyte-pro " ;
import type { GridSpec } from " ./demo " ;
export function SymbolCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const ticker = row . data . symbolTicker ;
const symbol = row . data . symbol ;
const image = symbols [ row . data . symbolTicker ] ;
< div className = " grid grid-cols-[20px_auto_auto] items-center gap-1.5 " >
alt = { ` Logo for symbol ${ symbol }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " bg-ln-gray-20 text-ln-text-dark flex h-fit items-center justify-center rounded-lg px-2 py-px text-[10px] " >
< div className = " w-full overflow-hidden text-ellipsis " >{ symbol . split ( " / " )[ 0 ]}</ div >
export function NetworkCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . network ;
const image = networks [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for network ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function ExchangeCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . exchange ;
const image = exchanges [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for exchange ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function PercentCellPositiveNegative ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
" h-ful flex w-full items-center justify-end tabular-nums " ,
field < 0 ? " text-red-600 dark:text-red-300 " : " text-green-600 dark:text-green-300 " ,
export function PercentCell ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
return < div className = " h-ful flex w-full items-center justify-end tabular-nums " >{ value }</ div >;
export const makePerfHeaderCell = ( name : string , subname : string ) => {
return ( _ : Grid . T . HeaderParams < GridSpec >) => {
< div className = " flex h-full w-full flex-col items-end justify-center tabular-nums " >
< div className = " text-ln-text-light font-mono uppercase " >{ subname }</ div >
The Autosize Cells Including Header button
passes the includeHeader flag:
api . columnAutosize ({ includeHeader: true });
Columns can define their own autosize calculation functions. When you
call api.autosizeColumns, the grid uses these functions to compute each width.
The example below shows the autosize functions used in this guide’s demos:
cellRenderer : SymbolCell ,
name : " Crypto Currency Symbol, Ticker, and Name " ,
autosizeHeaderFn : ( p ) => {
const textWidth = measureText ( `${ p . column . name ?? p . column . id }` , p . api . viewport ()) . width ;
return textWidth + padding ;
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText (
`${ data . symbol . split ( " / " )[ 0 ] . trim () }${ data . symbolTicker }` ,
return textWidth + iconWidth + gapWidth + padding ;
These autosize functions mirror the structure of the cell renderer. They account for the logo icon,
ticker text, symbol name, and the spacing added by CSS.
The autosizeCellFn combines these measurements into a final width.
The autosizeHeaderFn and autosizeCellFn functions above use measureText, a utility exported
from the LyteNyte Grid package. The measureText function uses an offscreen
canvas to approximate text width based on the provided element, in this case the viewport.
The autosize method returns an AutosizeResult object mapping
column ids to their computed widths. It also supports a
dry-run mode that calculates widths without applying them:
const widths = api . columnAutosize ( { dryRun : true } ) ;
You can autosize only specific columns by passing their
identifiers to api.autosizeColumns. The demo below shows
Autosize Symbol and Autosize Network , which resize only those columns.
Autosize Individual Columns Autosize Symbol Autosize Network Autosize Cells Reset Columns
import " @1771technologies/lytenyte-pro/components.css " ;
import " @1771technologies/lytenyte-pro/light-dark.css " ;
PercentCellPositiveNegative ,
} from " ./components.jsx " ;
import type { DEXPerformanceData } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { data } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { Grid , measureText , useClientDataSource } from " @1771technologies/lytenyte-pro " ;
import { useRef , useState } from " react " ;
export interface GridSpec {
readonly data : DEXPerformanceData ;
const initialColumns : Grid . Column < GridSpec > [] = [
cellRenderer : SymbolCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
measureText ( `${ data . symbol . split ( " / " )[ 0 ] . trim () }${ data . symbolTicker }` , p . api . viewport ()) ?. width ??
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : NetworkCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . network , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : ExchangeCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . exchange , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Change " , " 24h " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1w " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 3m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 6m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " YTD " ) ,
{ id : " volatility " , cellRenderer : PercentCell , name : " Volatility " , type : " number " },
cellRenderer : PercentCell ,
headerRenderer : makePerfHeaderCell ( " Volatility " , " 1m " ) ,
const base : Grid . ColumnBase < GridSpec > = { width : 80 };
export default function ColumnDemo () {
const ds = useClientDataSource ( { data : data } ) ;
const ref = useRef < Grid . API < GridSpec >> ( null ) ;
const [ columns , setColumns ] = useState ( initialColumns ) ;
< div className = " border-ln-border flex gap-2 border-b px-2 py-2 " >
ref . current ?. columnAutosize ({ columns: [ " symbol " ] });
ref . current ?. columnAutosize ({ columns: [ " network " ] });
ref . current ?. columnAutosize ();
setColumns ( initialColumns );
className = " ln-grid ln-cell:text-xs ln-header:text-xs ln-header:text-ln-text-xlight "
< Grid columns = { columns } onColumnsChange = { setColumns } columnBase = { base } rowSource = { ds } ref = { ref } />
import type { ClassValue } from " clsx " ;
import { twMerge } from " tailwind-merge " ;
import { exchanges , networks , symbols } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
export function tw ( ... c : ClassValue [] ) {
return twMerge ( clsx ( ... c )) ;
import type { Grid } from " @1771technologies/lytenyte-pro " ;
import type { GridSpec } from " ./demo " ;
export function SymbolCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const ticker = row . data . symbolTicker ;
const symbol = row . data . symbol ;
const image = symbols [ row . data . symbolTicker ] ;
< div className = " grid grid-cols-[20px_auto_auto] items-center gap-1.5 " >
alt = { ` Logo for symbol ${ symbol }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " bg-ln-gray-20 text-ln-text-dark flex h-fit items-center justify-center rounded-lg px-2 py-px text-[10px] " >
< div className = " w-full overflow-hidden text-ellipsis " >{ symbol . split ( " / " )[ 0 ]}</ div >
export function NetworkCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . network ;
const image = networks [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for network ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function ExchangeCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . exchange ;
const image = exchanges [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for exchange ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function PercentCellPositiveNegative ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
" h-ful flex w-full items-center justify-end tabular-nums " ,
field < 0 ? " text-red-600 dark:text-red-300 " : " text-green-600 dark:text-green-300 " ,
export function PercentCell ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
return < div className = " h-ful flex w-full items-center justify-end tabular-nums " >{ value }</ div >;
export const makePerfHeaderCell = ( name : string , subname : string ) => {
return ( _ : Grid . T . HeaderParams < GridSpec >) => {
< div className = " flex h-full w-full flex-col items-end justify-center tabular-nums " >
< div className = " text-ln-text-light font-mono uppercase " >{ subname }</ div >
Specify the columns to autosize using any supported reference:
api . columnAutosize ( { columns : [ " symbol " ] } ) ;
// Other ways to reference columns:
api . columnAutosize ( { columns : [ 1 ] } ) ; // By visible column index
api . columnAutosize ( { columns : [ { id : " symbol " } ] } ) ; // By column object
The columnDoubleClickToAutosize grid property controls whether LyteNyte
Grid autosizes a column when you double-click the column’s right edge.
Try double-clicking the header edges in the demo below.
Double Click To Autosize import " @1771technologies/lytenyte-pro/components.css " ;
import " @1771technologies/lytenyte-pro/light-dark.css " ;
PercentCellPositiveNegative ,
} from " ./components.jsx " ;
import type { DEXPerformanceData } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { data } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
import { Grid , measureText , useClientDataSource } from " @1771technologies/lytenyte-pro " ;
import { useRef , useState } from " react " ;
export interface GridSpec {
readonly data : DEXPerformanceData ;
const initialColumns : Grid . Column < GridSpec > [] = [
cellRenderer : SymbolCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
measureText ( `${ data . symbol . split ( " / " )[ 0 ] . trim () }${ data . symbolTicker }` , p . api . viewport ()) ?. width ??
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : NetworkCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . network , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : ExchangeCell ,
if ( p . row . kind !== " leaf " || ! p . row . data ) return null ;
const textWidth = measureText ( data . exchange , p . api . viewport ()) ?. width ?? 100 ;
return textWidth + iconWidth + gapWidth + padding ;
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Change " , " 24h " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1w " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 1m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 3m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " 6m " ) ,
cellRenderer : PercentCellPositiveNegative ,
headerRenderer : makePerfHeaderCell ( " Perf % " , " YTD " ) ,
{ id : " volatility " , cellRenderer : PercentCell , name : " Volatility " , type : " number " },
cellRenderer : PercentCell ,
headerRenderer : makePerfHeaderCell ( " Volatility " , " 1m " ) ,
const base : Grid . ColumnBase < GridSpec > = { width : 80 , resizable : true };
export default function ColumnDemo () {
const ds = useClientDataSource ( { data : data } ) ;
const ref = useRef < Grid . API < GridSpec >> ( null ) ;
const [ columns , setColumns ] = useState ( initialColumns ) ;
className = " ln-grid ln-cell:text-xs ln-header:text-xs ln-header:text-ln-text-xlight "
onColumnsChange = { setColumns }
columnDoubleClickToAutosize
import type { ClassValue } from " clsx " ;
import { twMerge } from " tailwind-merge " ;
import { exchanges , networks , symbols } from " @1771technologies/grid-sample-data/dex-pairs-performance " ;
export function tw ( ... c : ClassValue [] ) {
return twMerge ( clsx ( ... c )) ;
import type { Grid } from " @1771technologies/lytenyte-pro " ;
import type { GridSpec } from " ./demo " ;
export function SymbolCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const ticker = row . data . symbolTicker ;
const symbol = row . data . symbol ;
const image = symbols [ row . data . symbolTicker ] ;
< div className = " grid grid-cols-[20px_auto_auto] items-center gap-1.5 " >
alt = { ` Logo for symbol ${ symbol }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " bg-ln-gray-20 text-ln-text-dark flex h-fit items-center justify-center rounded-lg px-2 py-px text-[10px] " >
< div className = " w-full overflow-hidden text-ellipsis " >{ symbol . split ( " / " )[ 0 ]}</ div >
export function NetworkCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . network ;
const image = networks [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for network ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function ExchangeCell ({ api , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const name = row . data . exchange ;
const image = exchanges [ name ] ;
< div className = " grid grid-cols-[20px_1fr] items-center gap-1.5 " >
alt = { ` Logo for exchange ${ name }` }
className = " h-full w-full overflow-hidden rounded-full "
< div className = " w-full overflow-hidden text-ellipsis " >{ name }</ div >
export function PercentCellPositiveNegative ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
" h-ful flex w-full items-center justify-end tabular-nums " ,
field < 0 ? " text-red-600 dark:text-red-300 " : " text-green-600 dark:text-green-300 " ,
export function PercentCell ({ api , column , row } : Grid . T . CellRendererParams < GridSpec >) {
if ( ! api . rowIsLeaf ( row ) || ! row . data ) return null ;
const field = api . columnField ( column , row ) ;
if ( typeof field !== " number " ) return " - " ;
const value = ( field > 0 ? " + " : "" ) + ( field * 100 ) . toFixed ( 2 ) + " % " ;
return < div className = " h-ful flex w-full items-center justify-end tabular-nums " >{ value }</ div >;
export const makePerfHeaderCell = ( name : string , subname : string ) => {
return ( _ : Grid . T . HeaderParams < GridSpec >) => {
< div className = " flex h-full w-full flex-col items-end justify-center tabular-nums " >
< div className = " text-ln-text-light font-mono uppercase " >{ subname }</ div >
For double-click autosizing to work, the column must be resizable.
Set the resizable property on the column’s specification.
The example below enables double-click resizing for all columns:
const base : Grid . ColumnBase < GridSpec > = { width : 80 , resizable : true };
onColumnsChange = { setColumns }
columnDoubleClickToAutosize
LyteNyte Grid uses row virtualization for performance.
Autosizing considers only visible rows. If the viewport has not initialized,
the grid samples about 50 rows for calculations.
If a column has a known set of possible values, consider writing an autosize function
that returns the maximum width required for those values,
independent of viewport visibility.
To learn more about LyteNyte Grid’s virtualization features and configuration options,
see the Row & Column Virtualization guide .
LyteNyte Grid autosizes every column by default, even when virtualization keeps a
column out of the DOM. If your grid defines many columns, consider
autosizing only the visible ones to avoid unnecessary work.