rt

@saltyaom/react-table

Declarative React Table under 1kb

Showing:

Popularity

Downloads/wk

5

GitHub Stars

7

Maintenance

Last Commit

2mos ago

Contributors

1

Package

Dependencies

0

License

MIT

Type Definitions

Built-In

Tree-Shakeable

Yes?

Categories

Readme

@saltyaom/react-table

Declarative React Table under 1kb.

Polka Drake

Humanity Restored

Feature

  • No dependencies.
  • Light, 700 bytes on production.
  • Easy to understand, declarative.
  • Automatic key management.
  • Full control over table.
  • Full TypeScript support.

Size

Should be around 700 bytes, checkout Bundlephobia for accurate result.

Getting start

yarn add @saltyaom/react-table

// Or npm
npm install @saltyaom/react-table --save

Example

import Table from '@saltyaom/react-table'

const Example = () => {
    return (
        <Table
            header={['name', 'detail']}
            dataKey='name'
            data={[
                ['Fubuki', 'Waifriend'],
                ['Korone', 'Yubi yubi']   
            ]}
        />
    )
}

Why

Compose React table in a simple, elegant way.

Creating table in React is complicate.

Let create a simple table from the following data.

nametypevalue
Okayucat1
Koronedog-1

Where the requirement is:

  • First field on table head is bold.
  • Value field must be color by the following:
    • if value >= 0, return green
    • otherwise, return red

Implement on normal React would be like:

const VTuberTable = () => {
    const header = ['name', 'description', 'value']
    const data = [
        ['Okayu' , 'cat', 10],
        ['Korone', 'dog', -1]
    ]

    return (
        <table>
            <thead className="head">
                <tr>
                    {header.map((title, index) => {
                        if(index === 0) return <th className="title bold">{title}</th>

                        return (
                            <th className="title">{title}</th>
                        )
                    })}
                </tr>
            </thead>
            <tbody className="body">
                {data.map(row => 
                    <tr key={row[0]}>
                        {row.map((data, index) => {
                            if(data === 2)
                                if(data >= 0)
                                    return (
                                <td 
                                    key={`${row[0]}-${index}-${data}`} 
                                    className="green"
                                >
                                    {data}
                                </td>
                            )
                                else
                                    return (
                                <td 
                                    key={`${row[0]}-${index}-${data}`} 
                                    className="red"
                                >
                                    {data}
                                </td>
                            )

                            return (
                                <td 
                                    key={`${row[0]}-${index}-${data}`} 
                                    className="black"
                                >
                                    {data}
                                </td>
                            )
                        })}
                    </tr>
                )}
            </tbody>
        </table>
    )
}

The problem is:

  • The code is very long.
  • Semantic table require a lot of boilerplate, thead, tr, table.
  • Hard to understand, imperative.
  • key managment is complex.

Entering @saltyaom/react-table

@saltyaom/react-table is a simple, declarative way to compose table in React.

All you need to do is specified your data and key, you can bring your className anywhere, even a custom condition for class.

In the other word, you have full control over the table even in a declarative way.

Let's re-implement previous table in @saltyaom/react-table.

import Table from '@saltyaom/react-table'

const VTuberTable = () => {
    const header = ['name', 'description', 'value']
    const data = [
        ['Okayu' , 'cat', 10],
        ['Korone', 'dog', -1]
    ]

    return (
        <Table 
            dataKey="name"
            header={header}
            data={data}

            theadClassName="head"
            tbodyClassName="body"

            // Apply 'title' to all <th> element
            allThClassName="title"
            // Apply 'bold' to index 0 <th>
            thClassName={['bold']}

            tdClassName={[
                '',
                '',
                // On index 2, apply custom condition
                (value: number) => value >= 0 ? 'green' : 'red'
            ]}
        />
    )
}

That's it, we have a simple happy ending for composing table in React.

Documentation

Table is the only export and is default export from @saltyaom/react-table.

The acceptable props is:

export interface ITable<
    T =
        | (string | number | JSX.Element)[]
        | readonly (string | number | JSX.Element)[]
> {
    /**
     * Table header to be appear in `<thead>` in order.
     *
     * @example
     * ['name', 'description']
     */
    header: T

    /**
     * Data to be appear for each row `<td>` in order.
     *
     * @example
     * [
     *  ['Korone', 'Dog'],
     *  ['Okayu', 'Cat']
     * ]
     */
    data: T[] | readonly T[]

    /**
     * Key of data, can be either `string` which match in `header` or number as index.
     *
     * @example
     * 0
     *
     * @example
     * 'name'
     */
    dataKey?: string | number

    /**
     * className of wrapper of `<table>`
     * (element: `<section>`)
     */
    wrapperClassName?: string
    /**
     * className of `<table>`
     */
    className?: string
    /**
     * Width of each cell in order from left to right.
     *
     * @example
     * [80, 160]
     */
    cellsWidth?: number[]

    /**
     * className of `<thead>`
     */
    theadClassName?: string
    /**
     * className of `<th>`
     */
    thClassName?: string[]
    /**
     * className to apply to all `<th>`
     */
    allThClassName?: string

    /**
     * className of `<tbody>`
     */
    tbodyClassName?: string
    /**
     * className of `<td>`
     *
     * Can be either `string` or `function()` which accepts `([valueof data:, index: number])`
     *
     * @example
     * w-8
     *
     * @example
    // If nothing is returned, fallback to ''
     * (rowData, index) => {
     *  if(rowData.value === 0) return 'bg-blue-50'
     *  if(rowData.index === 0) return 'bg-red-50'
     * }
     */
    trClassName?: string | ((data: readonly T[], index: number) => string)
    /**
     * className of `<td>`
     *
     * Can be either `string[]` or `function()` which accepts `([valueof data:, index: number])`
     *
     * @example
     * ['w-8', 'w-16']
     *
     * @example
    // If nothing is returned, fallback to ''
     * (value, index) => {
     *  if(value === 0) return 'text-red-500'
     *  if(index === 0) return 'text-blue-500'
     * }
     */
    tdClassName?:
        | (string | ((data: any, index: number) => string))[]
        | ((data: string, index: number) => string)
    /**
     * className to apply to all `<tbody>`
     */
    allTdClassName?: string

    /**
     * Prepend element before table
     *
     * @example
     * <nav>
     *   <input
     *     name="search"
     *     type="text"
     *     placeholder="Search"
     *     onChange={handleSearch}
     *   />
     * </nav>
     */
    beforeTable?: JSX.Element
    /**
     * Append element after table
     *
     * @example
     * <section className="pagination">
     *  <button className="prev" onClick={previous}>Previous</button>
     *  <button className="next" onClick={next}>Next</button>
     * </section>
     */
    afterTable?: JSX.Element

    /**
     * Add custom props to `<table>` element
     *
     * @example
     * {
     *   style={
     *     borderCollapse: 'collapse'
     *   }
     * }
     */
    tableProps?: Omit<
        DetailedHTMLProps<
            TableHTMLAttributes<HTMLTableElement>,
            HTMLTableElement
        >,
        'className'
    >

    /**
     * Add custom props to `<thead>` element
     *
     * @example
     * {
     *   onClick: () => console.log("Clicked")
     * }
     */
    theadProps?: Omit<
        DetailedHTMLProps<
            HTMLAttributes<HTMLTableSectionElement>,
            HTMLTableSectionElement
        >,
        'className'
    >
    /**
     * Add custom props to `<th>` element
     *
     * @example
     * (data, index) => {
     *   if(isOdd(index)) return ({ className: '--odd' })
     * }
     */
    thProps?: (
        data: T[keyof T],
        index: number
    ) => Omit<
        DetailedHTMLProps<
            ThHTMLAttributes<HTMLTableHeaderCellElement>,
            HTMLTableHeaderCellElement
        >,
        'className'
    > | void
    /**
     * Add custom props to `<tbody>` element
     *
     * @example
     * {
     *   onClick: (data, index) => console.log("Clicked")
     * }
     */
    tbodyProps?: Omit<
        DetailedHTMLProps<
            HTMLAttributes<HTMLTableSectionElement>,
            HTMLTableSectionElement
        >,
        'className'
    >
    /**
     * Add custom props to `<tr>` element
     *
     * @example
     * (data, index) => {
     *   if(isOdd(index)) return ({ className: '--odd' })
     * }
     */
    trProps?: (
        row: T,
        index: number
    ) => Omit<
        DetailedHTMLProps<
            HTMLAttributes<HTMLTableRowElement>,
            HTMLTableRowElement
        >,
        'className'
    > | void
    /**
     * Add custom props to `<td>` element
     *
     * @example
     * (data, { column, row }) => ({
     *   onClick: () => console.log(column, row)
     * })
     */
    tdProps?: (
        data: T[keyof T],
        indexes: {
            column: number
            row: number
        }
    ) => Omit<
        DetailedHTMLProps<
            TdHTMLAttributes<HTMLTableDataCellElement>,
            HTMLTableDataCellElement
        >,
        'className'
    > | void
}

For more information, you can looks directly in the source code as it's very easy to read.

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100