A simple, fast, and un-opinionated table component for React.
It is small (1kB gzipped, from 3kB) and has no dependencies.
npm install --save knoll
import React from 'react'
import { Table, Column } from 'knoll'
const data = [
{ id: 1, name: 'Birch', kind: 'coniferous', image: 'http...' },
{ id: 2, name: 'Oak', kind: 'deciduous', image: 'http...' },
{ id: 3, name: 'Maple', kind: 'deciduous', image: 'http...' },
{ id: 4, name: 'Fir', kind: 'coniferous', image: 'http...' }
]
function CustomTable () {
return (
<Table data={data}>
{/* Get a column's content via a function */}
<Column header="ID" cell={row => row.id} />
{/* Customize what renders with React elements */}
<Column header="Image" cell={row => <img src={row.image} alt={row.name} />} />
{/* You can also use the `cellKey` prop as a shorthand to get a property */}
<Column header="Name" cellKey="name" />
<Column header="Kind" cellKey="kind" />
</Table>
)
}
The Table
component is for configure the table and passing data. It accepts a few props:
data
- Array of data to render (required)components
- Object of elements to render (optional, see Styling)
You can render a Table
by passing it one or many Column
children:
import { Table, Column } from 'knoll'
const fruit = [
{ name: 'Apple', quantity: 2 },
{ name: 'Banana', quantity: 5 },
{ name: 'Honeydew', quantity: 3 },
]
<Table data={fruit}>
<Column header="Name" cellKey="name" />
<Column header="Quantity" cellKey="quantity" />
</Table>
Column
allows you to specify the order and rendering of your data. Columns accept these props:
header
- A variable to render as the header contentcell
- A variable to render as the cell contentcellKey
- An shorthand getter for objects (see below)
The header
and cell
props accept strings, functions, or other React components, letting you customize how your content looks.
import { Table, Column } from 'knoll'
const data = [
{ id: 5, name: "Mies van der Rohe", phoneNumber: '6505551234' },
{ id: 6, name: "Florence Knoll", phoneNumber: '6505559123' },
{ id: 7, name: "Harry Bertoia", phoneNumber: '6505555678' }
]
<Table data={data}>
<Column header="ID" cell={row => `#${row.id}`} />
<Column header="Phone Number" cell={row => formatPhoneNumber(row.phoneNumber)} />
<Column header="Name" cell={row => row.name} />
</Table>
Would output a table like this:
ID | Phone Number | Name |
---|---|---|
#5 | +650 555-1234 | Mies van der Rohe |
#6 | +650 555-9123 | Florence Knoll |
#7 | +650 555-5678 | Harry Bertoia |
Since tables often show properties directly from an object, Column
components accept a cellKey
prop to make this easier.
Passing cellKey="propName"
is shorthand for cell={row => row.propName}
. Like so:
const data = [
{ id: 5, name: "Mies van der Rohe", phoneNumber: '650 555 1234' },
{ id: 6, name: "Florence Knoll", phoneNumber: '650 555 9123' },
{ id: 7, name: "Harry Bertoia", phoneNumber: '650 555 5678' }
]
<Table data={data}>
<Column header="ID" cellKey="id" />
<Column header="Phone Number" cellKey="phoneNumber" />
<Column header="Name" cellKey="name" />
</Table>
Note: cellKey
does not currently support nested properties (eg. obj.a.b.c
). If you need to access nested properties, we recommend you use cell
instead.
Knoll ships with no styling. It only renders HTML. There’s few things worse than finding a good component, only to realize you need to override a bunch of default styles it provides.
Knoll allows you to customize the HTML of every it renders via the components
prop:
// Define components you'd like to render. (defaults below)
const components = {
table: 'table',
header: 'thead',
headerRow: 'tr',
headerCell: 'th',
body: 'tbody',
row: 'tr',
cell: 'td',
}
const DefaultTable = props => (
<Table components={components} {...props} />
)
This approach means Knoll supports both className
-based styling (eg. plain CSS, CSS Modules), and CSS-in-JS approaches.
Pass components in that add a className
, be it a string of a CSS module class.
const components = {
table: props => <table className="CustomTable" {...props} />,
header: props => <thead className="CustomTable-header" {...props} />,
headerRow: props => <tr className="CustomTable-headerRow" {...props} />,
headerCell: props => <th className="CustomTable-headerCell" {...props} />,
body: props => <tbody className="CustomTable-body" {...props} />,
// `row` is special and receives the row's data as a prop too
row: ({ row, ...props }) => <tr className="CustomTable-row" {...props} />,
cell: props => <td className="CustomTable-cell" {...props} />,
}
const CustomTable = props => (
<Table components={components} {...props} />
)
Look overly verbose? Use the flexility of JSX to help!
const wrap = (Component, className) => ({ row, ...props }) => <Component className={className} {...props} />
const components = {
table: wrap('table', 'CustomTable'),
header: wrap('thead', 'CustomTable-header'),
headerRow: wrap('tr', 'CustomTable-headerRow'),
headerCell: wrap('th', 'CustomTable-headerCell'),
body: wrap('tbody', 'CustomTable-body'),
row: wrap('tr', 'CustomTable-row'),
cell: wrap('td', 'CustomTable-cell'),
}
const CustomTable = props => (
<Table components={components} {...props} />
)
Being able to customize each element means it’s easy to work with external CSS frameworks like Bootstrap. When you only need to customize the className
of <Table />
, you can simply pass it as a prop to the table:
const DefaultTable = props => <Table className="table" {...props} />
const StripedTable = props => <Table className="table table-striped" {...props} />
const data = [
{ foo: 'bar' },
{ foo: 'baz' },
{ foo: 'foobar' },
]
<DefaultTable data={data}>
<Column header="This table is plain" cellKey="foo" />
</DefaultTable>
<StripedTable data={data}>
<Column header="This table is striped" cellKey="foo" />
</StripedTable>
It’s also easy to work with CSS-in-JS solutions. The example below uses styled-components, but is easy to apply to other solutions.
const components = {
table: styled.table`
color: #242424;
font-size: 12px;
`,
tableHeader: styled.thead`
font-weight: bold;
text-align: center;
`,
}
const StyledTable = props => (
<Table components={components} {...props} />
)
Showing tabular data on the web has been a solved problem since 1997. But recent React components for tables make it quite heavy-handed.
React Table comes preloaded with styles, loading states, “pivoting” tables, and a lot of other stuff I don’t want. Other options have strange APIs and look abandoned.
Knoll tries to provide a simple, composable, and elegant interface to building reactive tables.
A few future plans:
- Examples of using composition for searching, filtering, sorting
- Examples of styling
MIT