# Column Rendering

> Customize column rendering for Infinite Table DataGrid to match your app and use custom components. Column styling and formatting, conditional rendering...

Canonical page: https://infinite-table.com/docs/learn/columns/column-rendering

Columns render the [field](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.field) value of the data they are bound to. This is the default behavior, which can be customized in a number of ways that we're exploring below.

If you want to explicitly use the TypeScript type definition for columns, import the `InfiniteTableColumn` type

```
import { InfiniteTableColumn } from '@infinite-table/infinite-react'
```

Note that it's a generic type, so when you use it, you have to bind it to your `DATA_TYPE` (the type of your data object).

When using custom rendering or custom components for columns, make sure all your rendering logic is [controlled](https://reactjs.org/docs/forms.html#controlled-components) and that it doesn't have local/transient state.

This is important because `InfiniteTable` uses virtualization heavily, in both _column cells and column headers_, so **custom components can and will be unmounted and re-mounted multiple times**, during the virtualization process (triggered by user scrolling, sorting, filtering and a few other interactions).

## Change the value using `valueGetter`

The simplest way to change what's being rendered in a column is to use the `valueGetter` prop and return a new value for the column.

```tsx
const nameColumn: InfiniteTableColumn<Employee> = {
  header: 'Employee Name',
  valueGetter: ({ data }) => `${data.firstName} ${data.lastName}`,
};
```

The [`columns.valueGetter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) prop is a function that takes a single argument - an object with `data` and `field` properties.

Note that the [`columns.valueGetter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) is only called for non-group rows, so the `data` property is of type `DATA_TYPE`.

**Example: Column with custom valueGetter**

```tsx
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80 },
  name: {
    header: 'Full Name',
    valueGetter: ({ data }) => `${data.firstName} ${data.lastName}`,
  },

  preferredLanguage: { field: 'preferredLanguage' },
  stack: { field: 'stack' },
};

export default function ColumnValueGetterExample() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="column-valueGetter-example"
          columns={columns}
          columnDefaultWidth={200}
        />
      </DataSource>
    </>
  );
}
```

The column value getter should not return JSX or other markup, because the value return by [`columns.valueGetter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) will be used when the column is sorted (when sorting is done client-side and not remotely). For more in-depth information on sorting see [the column sorting page](./column-sorting).

## Use [renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) and [render](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) to display custom content

The next step in customizing the rendering for a column is to use the [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) or the [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) props. In those functions, you have access to more information than in the [`columns.valueGetter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) function. For example, you have access to the current value of `groupBy` and `pivotBy` props.

[renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) and [render](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) can return any value that React can render.

The [renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) and [render](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) functions are called with an object that has the following properties:

- `data` - the data object (of type `DATA_TYPE | Partial<DATA_TYPE> | null`) for the row.
- `rowInfo` - very useful information about the current row:
  - `rowInfo.collapsed` - if the row is collased or not.
  - `rowInfo.groupBy` - the current group by for the row
  - `rowInfo.indexInAll` - the index of the row in the whole data set
  - `rowInfo.indexInGroup` - the index of the row in the current group
  - `rowInfo.value` - the value (only for group rows) that will be rendered by default in group column cells.
  - ... there are other useful properties that we'll document in the near future
- `column` - the current column being rendered
- `columnsMap` - the `Map` of columns available to the table. Note these might not be all visible. The keys in this map will be column ids.
- `fieldsToColumn` a `Map` that links `DataSource` fields to columns. Columns bound to fields (so with [`columns.field`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.field) specified) will be included in this `Map`.
- `api` - A reference to the [Infinite Table API](https://infinite-table.com/docs/reference/api/index.md) object.

[`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) is the last function called in the rendering pipeline for a column cell, while [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) is called before render, towards the beginning of the [rendering pipeline (read more about this below)](#rendering-pipeline).

Avoid over-writing [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) for special columns (like group columns) unless you know what you're doing. Special columns use the `render` function to render additional content inside the column (eg: collapse/expand tool for group rows). The [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) function allows you to override this additional content. So if you specify this function, it's up to you to render whatever content, including the collapse/expand tool.

However, there are easier ways to override the collapse/expand group icon, like using [`columns.renderGroupIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupIcon).

Inside the [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) and [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) functions (and other rendering functions), you can use the [`useInfiniteColumnCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteColumnCell) hook to retrieve the same params that are passed to the render functions.

This is especially useful when inside those functions you render a custom component that needs access to the same information.

```tsx
type Developer = { country: string; name: string; id: string };

const CountryInfo = () => {
  const { data, rowInfo, value } = useInfiniteColumnCell<Developer>();

  return <div>Country: {value}</div>;
};

const columns = {
  country: {
    field: 'country',
    renderValue: () => <CountryInfo />,
  },
};
```

**Example: Column with custom renderValue**

```tsx
import {
  InfiniteTable,
  DataSource,
  DataSourceGroupBy,
  InfiniteTablePropGroupColumn,
  InfiniteTableColumnRenderValueParam,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80 },
  stack: {
    field: 'stack',
    renderValue: ({ data, rowInfo }) => {
      if (rowInfo.isGroupRow) {
        return <>{rowInfo.value} stuff</>;
      }

      return <b>🎇 {data?.stack}</b>;
    },
  },
  firstName: {
    field: 'firstName',
  },

  preferredLanguage: { field: 'preferredLanguage' },
};

const defaultGroupBy: DataSourceGroupBy<Developer>[] = [{ field: 'stack' }];

const groupColumn: InfiniteTablePropGroupColumn<Developer> = {
  defaultWidth: 250,

  renderValue: ({
    rowInfo,
  }: InfiniteTableColumnRenderValueParam<Developer>) => {
    if (rowInfo.isGroupRow) {
      return (
        <>
          Grouped by <b>{rowInfo.value}</b>
        </>
      );
    }
    return null;
  },
};

export default function ColumnValueGetterExample() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultGroupBy={defaultGroupBy}
      >
        <InfiniteTable<Developer>
          debugId="column-renderValue-example"
          groupColumn={groupColumn}
          columns={columns}
          columnDefaultWidth={200}
        />
      </DataSource>
    </>
  );
}
```

Changing the group icon using `render`. The icon can also be changed using [`columns.renderGroupIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupIcon).

**Example: Column with render - custom expand/collapse icon**

This snippet shows overriding the group collapse/expand tool via the [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) function.

```tsx
import {
  InfiniteTable,
  DataSource,
  DataSourceGroupBy,
  InfiniteTablePropGroupColumn,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80 },
  stack: {
    field: 'stack',
  },
  firstName: {
    field: 'firstName',
  },
  preferredLanguage: { field: 'preferredLanguage' },
};

const defaultGroupBy: DataSourceGroupBy<Developer>[] = [{ field: 'stack' }];

const groupColumn: InfiniteTablePropGroupColumn<Developer> = {
  defaultWidth: 250,
  render: ({ rowInfo, toggleCurrentGroupRow }) => {
    if (rowInfo.isGroupRow) {
      const { collapsed } = rowInfo;
      const expandIcon = (
        <svg
          style={{
            display: 'inline-block',
            fill: collapsed ? '#b00000' : 'blue',
          }}
          width="20px"
          height="20px"
          viewBox="0 0 24 24"
          fill="#000000"
        >
          {collapsed ? (
            <>
              <path d="M0 0h24v24H0V0z" fill="none" />
              <path d="M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z" />
            </>
          ) : (
            <path d="M7.41 18.59L8.83 20 12 16.83 15.17 20l1.41-1.41L12 14l-4.59 4.59zm9.18-13.18L15.17 4 12 7.17 8.83 4 7.41 5.41 12 10l4.59-4.59z" />
          )}
        </svg>
      );
      return (
        <div
          style={{
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            color: collapsed ? '#b00000' : 'blue',
          }}
          onClick={() => toggleCurrentGroupRow()}
        >
          <i style={{ marginRight: 5 }}>Grouped by</i> <b>{rowInfo.value}</b>
          {expandIcon}
        </div>
      );
    }
    return null;
  },
};

export default function ColumnCustomRenderExample() {
  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        defaultGroupBy={defaultGroupBy}
      >
        <InfiniteTable<Developer>
          debugId="column-render-example"
          groupColumn={groupColumn}
          columns={columns}
          columnDefaultWidth={200}
        />
      </DataSource>
    </>
  );
}
```

**Example: Column with custom expand/collapse tool via renderGroupIcon**

This snippet shows how you can override the group collapse/expand tool via the [`columns.renderGroupIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupIcon) function.

```tsx
import {
  InfiniteTable,
  DataSource,
  DataSourcePropGroupBy,
  InfiniteTablePropColumns,
  InfiniteTableColumn,
} from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80 },
  stack: {
    field: 'stack',
  },
  firstName: {
    field: 'firstName',
  },

  preferredLanguage: { field: 'preferredLanguage' },
};
const groupBy: DataSourcePropGroupBy<Developer> = [
  {
    field: 'country',
    column: {
      header: 'Country group',
      renderGroupValue: ({ value }) => <>Country: {value}</>,
    },
  },
  { field: 'preferredLanguage' },
];

const groupColumn: InfiniteTableColumn<Developer> = {
  renderGroupIcon: ({ rowInfo, toggleCurrentGroupRow }) => {
    return (
      <div
        onClick={toggleCurrentGroupRow}
        style={{ cursor: 'pointer', marginRight: 10 }}
      >
        {rowInfo.isGroupRow ? (rowInfo.collapsed ? '👇' : '👉') : ''}
      </div>
    );
  },
};

export default function App() {
  return (
    <DataSource<Developer> data={dataSource} primaryKey="id" groupBy={groupBy}>
      <InfiniteTable<Developer>
        debugId="column-renderGroupIcon-example"
        columns={columns}
        groupColumn={groupColumn}
      />
    </DataSource>
  );
}
```

## Using hooks for custom rendering

Inside the [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) and [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) functions, you can use hooks - both provided by `InfiniteTable` and any other `React` hooks.

### Hook: [`useInfiniteColumnCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteColumnCell)

When you're inside a rendering function for a column cell, you can use [useInfiniteColumnCell hook](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteColumnCell) to get access to the current cell's rendering information - the argument passed to the [render](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) or [renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) functions.

```tsx
import {
  useInfiniteColumnCell,
  InfiniteTableColumn,
} from '@infinite-table/infintie-react';

function CustomName() {
  const { data, rowInfo } = useInfiniteColumnCell<Employee>();

  return (
    <>
      <b>{data.firstName}</b>, {data.lastName}
    </>
  );
}

const nameColumn: InfiniteTableColumn<Employee> = {
  header: 'Employee Name',
  renderValue: () => <CustomName />,
};
```

**Example: Column with render & useInfiniteColumnCell**

```tsx
import {
  InfiniteTable,
  DataSource,
  useInfiniteColumnCell,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';
import { HTMLProps } from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

function CustomCell(_props: HTMLProps<HTMLElement>) {
  const { value, data } = useInfiniteColumnCell<Developer>();

  let emoji = '🤷';
  switch (value) {
    case 'photography':
      emoji = '📸';
      break;
    case 'cooking':
      emoji = '👨🏻‍🍳';
      break;
    case 'dancing':
      emoji = '💃';
      break;
    case 'reading':
      emoji = '📚';
      break;
    case 'sports':
      emoji = '⛹️';
      break;
  }

  const label = data?.stack === 'frontend' ? '⚛️' : '';

  return (
    <b>
      {emoji} + {label}
    </b>
  );
}

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', maxWidth: 80 },
  firstName: { field: 'firstName' },
  hobby: {
    field: 'hobby',
    // we're not using the arg of the render function directly
    // but CustomCell uses `useInfiniteColumnCell` to retrieve it instead
    render: () => <CustomCell />,
  },
};

export default function ColumnRenderWithHooksExample() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="column-render-hooks-example"
          columns={columns}
          columnDefaultWidth={200}
        />
      </DataSource>
    </>
  );
}
```

### Hook: [`useInfiniteHeaderCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteHeaderCell)

For column headers, you can use [`useInfiniteHeaderCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteHeaderCell) hook to get access to the current header's rendering information - the argument passed to the [`columns.header`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.header) function.

```tsx
import {
  useInfiniteHeaderCell,
  InfiniteTableColumn,
} from '@infinite-table/infintie-react';

function CustomHeader() {
  const { column } = useInfiniteHeaderCell<Employee>();

  return <b>{column.field}</b>;
}

const nameColumn: InfiniteTableColumn<Employee> = {
  header: 'Employee Name',
  field: 'firstName',
  header: () => <CustomHeader />,
};
```

**Example: Column Header with render & useInfiniteHeaderCell**

```tsx
import {
  InfiniteTable,
  DataSource,
  useInfiniteHeaderCell,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const HobbyHeader: React.FC = function () {
  const { column } = useInfiniteHeaderCell<Developer>();

  return <b style={{ color: '#0000c2' }}>{column?.field} 🤷📸👨🏻‍🍳💃📚⛹️</b>;
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', maxWidth: 80 },
  stack: {
    field: 'stack',
  },
  hobby: {
    field: 'hobby',
    components: {
      HeaderCell: HobbyHeader,
    },
  },
};

export default function ColumnHeaderExampleWithHooks() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="column-header-hooks-example"
          columns={columns}
          columnDefaultWidth={200}
        />
      </DataSource>
    </>
  );
}
```

## Use [column.components](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.components) to customize the column

There are cases when custom rendering via the [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) and [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) props is not enough and you want to fully control the column cell and render your own custom component for that.

For such scenarios, you can specify `column.components.HeaderCell` and `column.components.ColumnCell`, which will use those components to render the DOM nodes of the column header and column cells respectively.

```tsx
import { InfiniteTableColumn } from '@infinite-table/infintie-react';

const ColumnCell = (props: React.HTMLProps<HTMLDivElement>) => {
  const { domRef, rowInfo } = useInfiniteColumnCell<Developer>();

  return (
    <div ref={domRef} {...props} style={{ ...props.style, color: 'red' }}>
      {props.children}
    </div>
  );
};

const HeaderCell = (props: React.HTMLProps<HTMLDivElement>) => {
  const { domRef, sortTool } = useInfiniteHeaderCell<Developer>();

  return (
    <div ref={domRef} {...props} style={{ ...props.style, color: 'red' }}>
      {sortTool}
      First name
    </div>
  );
};

const nameColumn: InfiniteTableColumn<Developer> = {
  header: 'Name',
  field: 'firstName',
  components: {
    ColumnCell,
    HeaderCell,
  },
};
```

When using custom components, make sure you get `domRef` from the corresponding hook ([`useInfiniteColumnCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteColumnCell) for column cells and [`useInfiniteHeaderCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteHeaderCell) for header cells) and pass it on to the final `JSX.Element` that is the DOM root of the component.

```tsx
// inside a component specified in column.components.ColumnCell
const { domRef } = useInfiniteColumnCell<DATA_TYPE>();

return <div ref={domRef}>...</div>;
```

Also you have to make sure you spread all other `props` you receive in the component, as they are `HTMLProps` that need to end-up in the DOM (eg: `className` for theming and default styles, etc).

Both [components.ColumnCell](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.components.ColumnCell) and [components.HeaderCell](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.components.HeaderCell) need to be declared with `props` being of type `HTMLProps<HTMLDivElement>`.

**Example: Custom components**

```tsx
import {
  InfiniteTable,
  DataSource,
  useInfiniteColumnCell,
  useInfiniteHeaderCell,
  InfiniteTablePropColumnTypes,
} from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers1k')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const DefaultHeaderComponent: React.FunctionComponent<
  React.HTMLProps<HTMLDivElement>
> = (props) => {
  const { column, domRef, columnSortInfo } = useInfiniteHeaderCell<Developer>();

  const style = {
    ...props.style,
    border: '1px solid #fefefe',
  };

  let sortTool = '';
  switch (columnSortInfo?.dir) {
    case undefined:
      sortTool = '👉';
      break;
    case 1:
      sortTool = '👇';
      break;
    case -1:
      sortTool = '☝🏽';
      break;
  }

  return (
    <div ref={domRef} {...props} style={style}>
      {/* here you would usually have: */}
      {/* {props.children} {sortTool} */}
      {/* but in this case we want to override the default sort tool as well (which is part of props.children) */}
      {column.field} {sortTool}
    </div>
  );
};

const StackComponent: React.FunctionComponent<
  React.HTMLProps<HTMLDivElement>
> = (props) => {
  const { value, domRef } = useInfiniteColumnCell<Developer>();

  const isFrontEnd = value === 'frontend';
  const emoji = isFrontEnd ? '⚛️' : '💽';
  const style = {
    padding: '5px 20px',
    border: `1px solid ${isFrontEnd ? 'red' : 'green'}`,
    ...props.style,
  };
  return (
    <div ref={domRef} {...props} style={style}>
      {props.children} <div style={{ flex: 1 }} /> {emoji}
    </div>
  );
};

const columnTypes: InfiniteTablePropColumnTypes<Developer> = {
  default: {
    // override all columns to use these components
    components: {
      HeaderCell: DefaultHeaderComponent,
    },
  },
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id', defaultWidth: 80 },
  stack: {
    field: 'stack',
    renderValue: ({ data }) => 'Stack: ' + data?.stack,
    components: {
      HeaderCell: DefaultHeaderComponent,
      ColumnCell: StackComponent,
    },
  },
  firstName: {
    field: 'firstName',
  },
  preferredLanguage: {
    field: 'preferredLanguage',
  },
};

export default function ColumnValueGetterExample() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          debugId="column-components-example"
          columns={columns}
          columnTypes={columnTypes}
        />
      </DataSource>
    </>
  );
}
```

If you're using the [`useInfiniteColumnCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteColumnCell) hook inside the [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) or [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) functions (and not as part of a custom component in [`columns.components.ColumnCell`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.components.ColumnCell)), you don't need to pass on the `domRef` to the root of the DOM you're rendering (same is true if you're using [`useInfiniteHeaderCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteHeaderCell) inside the [`columns.header`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.header) function).

If the above [`columns.components`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.components) is still not enough, read about the rendering pipeline below.

## Rendering pipeline

The rendering pipeline for columns is a series of functions defined on the column that are called while rendering.

All the functions that have the word `render` in their name will be called with an object that has a `renderBag` property, which contains values that will be rendered.

The default [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) function (the last one in the pipeline) ends up rendering a few things:

- a `value` - generally comes from the [field](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.field) the column is bound to
- a `groupIcon` - for group columns
- a `selectionCheckBox` - for columns that have [`columns.renderSelectionCheckBox`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox) defined (combined with row selection)

When the rendering process starts for a column cell, all the above end up in the `renderBag` object.

### Rendering pipeline - `renderBag.value`

As already mentioned, the `value` defaults to the value of the column [field](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.field) for the current row.

If the column is not bound to a field, you can define a [valueGetter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter). The [valueGetter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) only has access to `{data, field?}` in order to compute a value and return it.

After the [valueGetter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) is called, the [valueFormatter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueFormatter) is next in the rendering pipeline.

This is called with more details about the current cell

```tsx
const column: InfiniteTableColumn<T> = {
  // the valueGetter can be useful when rows are nested objects
  // or you want to compose multiple values from the row
  valueGetter: ({ data }) => {
    return data.person.salary * 10;
  },
  valueFormatter: ({
    value,
    isGroupRow,
    data,
    field,
    rowInfo,
    rowSelected,
    rowActive,
  }) => {
    // the value here is what the `valueFormatter` returned
    return `USD ${value}`;
  },
};
```

After [valueGetter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) and [valueFormatter](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueFormatter) are called, the resulting value is the actual value used for the cell. This value will also be assigned to `renderBag.value`

When [renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) and [render](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) are called by `InfiniteTable`, both `value` and `renderBag` will be available as properties to the arguments object.

```tsx {3,12}
const column: InfiniteTableColumn<T> = {
  valueGetter: () => 'world',
  renderValue: ({ value, renderBag, rowInfo }) => {
    // at this stage, `value` is 'world' and `renderBag.value` has the same value, 'world'
    return <b>{value}</b>;
  },

  render: ({ value, renderBag, rowInfo }) => {
    // at this stage `value` is 'world'
    // but `renderBag.value` is <b>world</b>, as this was the value returned by `renderValue`
    return <div>Hello {renderBag.value}!</div>;
  },
};
```

After the [renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) function is called, the following are also called (if available):

- [renderGroupValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupValue) - for group rows
- [renderLeafValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderLeafValue) - for leaf rows

You can think of them as an equivalent to [renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue), but narrowed down to group/non-group rows.

Inside those functions, the `renderBag.value` refers to the value returned by the [renderValue](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) function.

### Rendering pipeline - `renderBag.groupIcon`

In a similar way to `renderBag.value`, the `renderBag.groupIcon` is also piped through to the [render](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) function.

```tsx {2,9}
const column: InfiniteTableColumn<T> = {
  renderGroupIcon: ({ renderBag, toggleGroupRow }) => {
    return <> [ {renderBag.groupIcon} ] </>;
  },
  render: ({ renderBag }) => {
    return (
      <>
        {/* use the groupIcon from the renderBag */}
        {renderBag.groupIcon}
        {renderBag.value}
      </>
    );
  },
};
```

Inside [`columns.renderGroupIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupIcon), you have access to `renderBag.groupIcon`, which is basically the default group icon - so you can use that if you want, and build on that.

Also inside [`columns.renderGroupIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupIcon), you have access to `toggleGroupRow` so you can properly hook the collapse/expand behaviour to your custom group icon.

### Rendering pipeline - `renderBag.selectionCheckBox`

Like with the previous properties of `renderBag`, you can customize the `selectionCheckBox` (used when multiple selection is configured) to be piped-through - for columns that specify [`columns.renderSelectionCheckBox`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox).

```tsx {2,25}
const column: InfiniteTableColumn<T> = {
  renderSelectionCheckBox: ({
    renderBag,
    rowSelected,
    isGroupRow,
    toggleCurrentRowSelection,
    toggleCurrentGroupRowSelection,
  }) => {
    const toggle = isGroupRow
      ? toggleCurrentGroupRowSelection
      : toggleCurrentRowSelection;

    // you could return renderBag.groupIcon to have the default icon

    const selection =
      rowSelected === null
        ? '-' // we're in a group row with indeterminate state if rowSelected === null
        : rowSelected
        ? 'x'
        : 'o';

    return <div onClick={toggle}> [ {selection} ] </div>;
  },
  render: ({ renderBag }) => {
    return (
      <>
        {/* use the selectionCheckBox from the renderBag */}
        {renderBag.selectionCheckBox}
        {renderBag.groupIcon}
        {renderBag.value}
      </>
    );
  },
};
```

To recap, here is the full list of the functions in the rendering pipeline, in order of invocation:

1. [`columns.valueGetter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueGetter) - doesn't have access to `renderBag`
2. [`columns.valueFormatter`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.valueFormatter) - doesn't have access to `renderBag`
3. [`columns.renderGroupIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupIcon) - can use all properties in `renderBag`
4. [`columns.renderSelectionCheckBox`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox) - can use all properties in `renderBag`
5. [`columns.renderValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderValue) - can use all properties in `renderBag`
6. [`columns.renderGroupValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderGroupValue) - can use all properties in `renderBag`
7. [`columns.renderLeafValue`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderLeafValue) - can use all properties in `renderBag`
8. [`columns.render`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.render) - can use all properties in `renderBag`

Additionally, the [`columns.components.ColumnCell`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.components.ColumnCell) custom component does have access to the `renderBag` via [`useInfiniteColumnCell`](https://infinite-table.com/docs/reference/hooks/index.md#useInfiniteColumnCell)
