ReactHustle

How to Create DaisyUI Table with Sorting and Pagination (Typescript)

Jasser Mark Arioste

Jasser Mark Arioste

How to Create DaisyUI Table with Sorting and Pagination (Typescript)

In our previous tutorial, we learned how to create a basic table using DaisyUI and react-table. It had a very basic functionality which is just to display a table. Today, we're going to add client-side sorting and pagination to that table using react-table@v8.x.x

To follow along with this tutorial, you don't necessarily need to use the DaisyUI library, you can also use bootstrap or whatever UI library you want. The main focus in this tutorial is how to use react-table to implement client-side sorting and pagination. However if you wish to follow along from the start, take a look at our previous tutorial. Note that we'll also be using typescript in this tutorial.

With that out of the way, let's start! 

Adding Client-Side Sorting functionality #

To add sorting functionality to our table we only really need to do two things: 

  1. Modify useTable hook to add the options necessary to calculate the sorted rows and calculate the sort state of the table.
  2. Modify the header to add sort functionality and display the current sorted column

Modifying the useTable hook to add sort functionality.

Check steps in the code  below.

//components/Table.tsx
...
const Table = () => {
  //1. add a sorting state using useState hook, this will allow us to keep track of the state
  const [sorting, setSorting] = React.useState<SortingState>([]);
  const table = useReactTable({
    columns: userColumnDefs,
    data: data as Person[],
    getCoreRowModel: getCoreRowModel(),
    //2. add getSortedRowModel into the pipeline. this will calculate the sorted rows when the sort state changes
    getSortedRowModel: getSortedRowModel(),
    //3. add state to our table we use the  sorting state from step 1
    state: {
      sorting,
    },
    //4. add a handler for onSortingChange using the setSorting from step 1
    onSortingChange: setSorting,
  });
  ...
}
1234567891011121314151617181920

Modifying the th elements to add sorting indicators and handlers

Checkout the steps in the code below:

//components/Table.tsx

const Table = ()=> {
 ...
 return (
  ...
      <thead>
          <tr>
            {headers.map((header) => {
              //5. check if the column is sorted
              const direction = header.column.getIsSorted();

              //6. create a map to get the sorting indicator
              const arrow: any = {
                asc: "🔼",
                desc: "🔽",
              };

              //6. get the sorting indicator if header is sorted
              const sort_indicator = direction && arrow[direction];
              return (
                <th key={header.id}>
                  {header.isPlaceholder ? null : (
                    //7. add an onClick handler using header.column.getToggleSortingHandler
                    <div
                      onClick={header.column.getToggleSortingHandler()}
                      // 8. add a class to render the sorting indicator properly
                      className="cursor-pointer flex gap-4"
                    >
                      {flexRender(
                        header.column.columnDef.header,
                        header.getContext()
                      )}
                      {/* 9. render the sorting indicator */}
                      {direction && <span>{sort_indicator}</span>}
                    </div>
                  )}
                </th>
              );
            })}
          </tr>
        </thead>
  ...
 )
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445

With everything done you should have a table with sorting functionality:

Table with sorting functionality

Adding Pagination #

To add a pagination component to our table we need to do 3 things:

  1. Add pagination to the row calculation pipeline by using getPaginationRowModel provided by react-table
  2. Create a Pagination component and make use of react-tables pagination functions to modify the pagination state.
  3. Use the Pagination component in our table component.

Using getPaginationRowModel to add a pagination pipeline

//components/Table.tsx
import {
  flexRender,
  getCoreRowModel,
  getSortedRowModel,
  // 1. add necessary import
  getPaginationRowModel,  
  SortingState,
  useReactTable,
} from "@tanstack/react-table";
...
const Table = () => {
   ...
   const table = useReactTable({
    columns: userColumnDefs,
    data: data as Person[],
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    //2.  add getPaginationRowModel
    getPaginationRowModel: getPaginationRowModel(),
    state: {
      sorting,
    },
    onSortingChange: setSorting,
  });
  ...
}
123456789101112131415161718192021222324252627

By default this will calculate the rows and return the first 10 rows of our table. We can change how many rows we want to display later when creating the Pagination component.

Creating the Pagination component:

Let's create the pagination component. Create a file components/Pagination.tsx and copy-past the code below. 

//components/Pagination.tsx

import { Table } from "@tanstack/react-table";
import React from "react";

type Props = {
  // table returned from useTable hook.
  table: Table<any>;
};

const Pagination = ({ table }: Props) => {
  // pagination state
  const state = table.getState().pagination;
  //last page helper function
  const goLastPage = () => table.setPageIndex(table.getPageCount() - 1);
  return (
    <div className="my-2">
      <div className="flex items-center gap-2">
        <div className="btn-group btn-sm">
          {/* button to go to first page */}
          <button
            className="btn btn-sm"
            onClick={() => table.setPageIndex(0)}
            disabled={!table.getCanPreviousPage()}
          >
            {"<<"}
          </button>
          {/* button to go previous page */}
          <button
            className="btn btn-sm"
            onClick={() => table.previousPage()}
            disabled={!table.getCanPreviousPage()}
          >
            {"<"}
          </button>
          {/* button to go next page */}
          <button
            className="btn btn-sm"
            onClick={() => table.nextPage()}
            disabled={!table.getCanNextPage()}
          >
            {">"}
          </button>
          {/* button to go last page */}
          <button
            className="btn btn-sm"
            onClick={goLastPage}
            disabled={!table.getCanNextPage()}
          >
            {">>"}
          </button>
        </div>
        {/* page info */}
        <span className="flex items-center gap-1">
          <div>Page</div>
          <strong>
            {state.pageIndex + 1} of {table.getPageCount()}
          </strong>
        </span>
        {/* input to skip to a specific page */}
        <span className="flex items-center gap-1">
          | Go to page:
          <input
            defaultValue={state.pageIndex + 1}
            type="number"
            onChange={(e) => {
              const page = e.target.value ? Number(e.target.value) - 1 : 0;
              table.setPageIndex(page);
            }}
            className="input input-bordered w-20 input-sm mx-2"
          />
        </span>
        {/* select to input page size */}
        <select
          value={state.pageSize}
          onChange={(e) => {
            table.setPageSize(Number(e.target.value));
          }}
          className="select select-sm select-bordered"
        >
          {[10, 20, 30, 40, 50].map((pageSize) => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>
      </div>
    </div>
  );
};

export default Pagination;
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192

In our pagination component, we are using functions provided by the useTableHook to modify the pagination state. These functions include table.setPageIndextable.getCanPreviousPagetable.setPageSize which are pretty self-explanatory based on their function name.

Using the Pagination component  #

In order to use the Pagination component, we have to call it from inside our Table component.

//components/Table.tsx
...
import Pagination from "./Pagination";
...

const Table = () => {
  const [sorting, setSorting] = React.useState<SortingState>([]);
  const table = useReactTable({
    columns: userColumnDefs,
    data: data as Person[],
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    //2.  add getPaginationRowModel
    getPaginationRowModel: getPaginationRowModel(),
    state: {
      sorting,
    },
    onSortingChange: setSorting,
  });
  const headers = table.getFlatHeaders();
  const rows = table.getRowModel().rows;
  return (
    <div>
      ...
      <Pagination table={table} />
    </div>
  );
};
12345678910111213141516171819202122232425262728

And we're done! Adding sorting and pagination functionality to our table was not as hard as we expected. Here are the results of our pagination component.

Table component after adding pagination component using react-table

Below are some questions you might ask regarding react-table

How to add server-side pagination and sorting? #

We'll cover this in our next tutorial.

Is it wise to create a Table with multiple functionalities that can be used with all sorts of data? #

In my opinion, nope. that's a bad idea. We should create a different table depending on the functionality needed and use react-table to get the necessary functionality we need. react-table provides a good way of plug-and-play functionality that keeps our codebase clean and free of clutter. 

Resources #

If you like articles like these, please subscribe to our newsletter. Also if you have a tutorial you want me to demo, please contact me through our contact page.

Credits: Image by Simon from Pixabay

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle