Build an Expandable / Collapsible Data Table with 2 shadcn/ui Components
Tags: nextjs, tailwindcss, tutorial, webdev
Published: 2023-08-16 12:54:04

## What you will find in this article?
In the modern digital age, the way we present data to users significantly impacts their experience. A responsive, well-designed table not only provides clarity but also enhances user engagement. This article will guide you on building a data table that expands and reveals more data rows and is an essential component for swift data viewing.

Of course, there is Tanstack table, but it's overkill for most of the simpler usecases. Therefore, we are using shadcn/ui (only two components).

We will use Next.js and Tailwindcss ensuring our table looks stylish and performs optimally.

![cool gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/472gd2uhi78ruldsejs9.gif)

## Papermark - Your open-source DocSend alternative.
Before we begin, allow me to introduce Papermark. It's an open-source project for securely sharing documents with built-in real-time, page-by-page analytics.

If you're loving the concept, I'd greatly appreciate a star! And, as always, your thoughts and feedback in the comments section are highly valued ❤️

https://github.com/mfts/papermark

![Papermark Links Table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ov54e9eslp7uoso4glqf.gif)



## Setup the project
It's time to set up our project environment. We'll start with a Next.js app and then layer in our responsive table components.

### Set up tea
It's a good idea to have a package manager handy, like [`tea`](https://github.com/teaxyz/cli). It'll handle your development environment and simplify your (programming) life!

```sh
sh <(curl https://tea.xyz)

# --- OR ---
# using brew
brew install teaxyz/pkgs/tea-cli
```

`tea` frees you to focus on your code, as it takes care of installing `node`, `npm`, `vercel` and any other packages you may need. The best part is, `tea` installs all packages in a dedicated directory (_default: `~/.tea`_), keeping your system files neat and tidy.

### Set up Next.js with TypeScript and Tailwindcss
We will use create-next-app to generate a new Next.js project. We will also be using TypeScript and Tailwind CSS, so make sure to select those options when prompted.

```bash
npx create-next-app

# ---
# you'll be asked the following prompts
What is your project named?  my-app
Would you like to add TypeScript with this project?  Y/N
# select `Y` for typescript
Would you like to use ESLint with this project?  Y/N
# select `Y` for ESLint
Would you like to use Tailwind CSS with this project? Y/N
# select `Y` for Tailwind CSS
Would you like to use the `src/ directory` with this project? Y/N
# select `N` for `src/` directory
What import alias would you like configured? `@/*`
# enter `@/*` for import alias
```

## Building the Expandable Table
Having set up the environment, we can now focus on crafting our responsive, expandable table.

In this section, we will utilize the components from `shadcn/ui` to create a responsive and expandable table. `shadcn/ui` provides us with modular and highly customizable components, making it easier to develop user-friendly interfaces.

### #1 Table Structure and Styling
Using the `Table` component from `shadcn/ui`, we'll design a table that's both responsive and visually appealing. 

Head over to [ui.shadcn.com](https://ui.shadcn.com/) and grab the `<Table />` component.

```sh
# run this command in your Next.js project
npx shadcn-ui@latest add table
```

```tsx
// components/links-table.tsx
import {
  Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
} from "@/components/ui/table";

export default function LinksTable() {
  const links = [...] // these are link objects, we'll get there later

  return (
    <div className="w-full sm:p-4">
      <h2 className="p-4">All links</h2>
      <div className="rounded-md sm:border">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead className="font-medium">Name</TableHead>
              <TableHead className="font-medium">Link</TableHead>
              <TableHead className="font-medium">Views</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {links ? (
              links.map((link) => (
                <TableRow key={link.id}>
                  <TableCell>{link.name}</TableCell>
                  <TableCell>{link.id}</TableCell>
                  <TableCell>{link.viewCount}</TableCell>
                </TableRow>
              ))
            ) : null}
          </TableBody>
        </Table>
      </div>
    </div>
  );
}
```

### #2 Table Interactivity
The logic behind the table's expandability is crucial. We need to ensure smooth transitions and ease of access to data. 

For this, we are using the second `shadcn/ui` component: `<Collapsible />`.

Head over to [ui.shadcn.com](https://ui.shadcn.com/) and grab the `Collapsible` component.

```sh
# run this command in your Next.js project
npx shadcn-ui@latest add collapsible
```

We are creating a new component that will contain our expanded data of visitors for each link

```tsx
// components/links-visitors.tsx
import { TableCell, TableRow } from "@/components/ui/table";
import { Gauge } from "@/components/ui/gauge";

export default function LinksVisitors({linkId}: {linkId: string) {
  const visitors = [...] // these are the visitor objects based on the linkId
  
  return (
    <>
      {visitors ? ( 
        visitors.map((visitor) => (
          <TableRow key={visitor.id}>
            <TableCell>{visitor.name}</TableCell>
            <TableCell>{visitor.totalDuration}</TableCell>
            <TableCell>
              <Gauge value={view.completionRate} />
            </TableCell>
          </TableRow>
        ))
      ) : null}
    </>
  );
}
```

---
### Small excursion ✨
Now, you might ask yourself, what is this `<Gauge />` component?

It's a beautiful Vercel UI component that indicates a status from 0 to 100. I recreated it with Tailwindcss and made it open-source 🎉

[![Gauge Component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2blxd3kcfc5iee3jqt0.gif)](https://twitter.com/mfts0/status/1691110169319559169)

More detail and source code in this tweet: https://twitter.com/mfts0/status/1691110169319559169


---

Ok back to our expanding table. Let's add the `<Collapsible />` component to our first component, the `links-table`

```tsx
// components/links-table.tsx
import {
  Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
} from "@/components/ui/table";

import {
  Collapsible, CollapsibleContent, CollapsibleTrigger,
} from "@/components/ui/collapsible";

import LinksVisitors from "@/components/links-visitors.tsx"

export default function LinksTable() {
  const links = [...] // these are link objects, we'll get there later

  return (
    <div className="w-full sm:p-4">
      <h2 className="p-4">All links</h2>
      <div className="rounded-md sm:border">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead className="font-medium">Name</TableHead>
              <TableHead className="font-medium">Link</TableHead>
              <TableHead className="font-medium">Views</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {links ? (
              links.map((link) => (
                <Collapsible key={link.id} asChild>
                  <>
                    <TableRow>
                      <TableCell>{link.name}</TableCell>
                      <TableCell>{link.id}</TableCell>
                      <TableCell>{link.viewCount}
                        <CollapsibleTrigger asChild>
                          <div>{link.viewCount}</div>
                        </CollapsibleTrigger>
                      </TableCell>
                    </TableRow>
                    <CollapsibleContent asChild>
                      <LinksVisitors linkId={link.id} />
                    </CollapsibleContent>
                  </>
                </Collapsible>
              ))
            ) : null}
          </TableBody>
        </Table>
      </div>
    </div>
  );
}
```

It's important to note that we have to wrap the `TableRow` in a fragment (`<></>`) and use the property `asChild` on the `<Collapsible />` component in order for everything to function properly. 


And there you have it, an impeccably designed, responsive expandable (or collapsible) table, all set to elevate your data presentation!

#### Collapsed table view
![Collapsed Table View](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wuxiyvpf7f7gj8zvp1rt.png)

#### Expanded table view
![Expanded Table View](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2kli0c67b1bshexs6qt.png)


## Conclusion
You've successfully built a responsive, expandable/collapsible table for quick data viewing using Next.js and Tailwindcss. While this article provides a foundational guide, you can further enhance the table, adding sorting functionalities, filters, and more!

Thank you for joining me on this journey. I'm Marc, an advocate for open-source software. I'm passionately working on [papermark.io](https://www.papermark.io) - your go-to open-source alternative to DocSend.

## Help me out!
If you found this guide enlightening and have a clearer vision for building simple data tables, please give us a star! Your feedback in the comments propels us forward ❤️

https://github.com/mfts/papermark

![Cat please](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afhox5uciwxs6k4639vf.gif)