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.
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
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
. It'll handle your development environment and simplify your (programming) life!
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.
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 and grab the <Table />
component.
# run this command in your Next.js project
npx shadcn-ui@latest add table
// 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 and grab the Collapsible
component.
# 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
// 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 🎉
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
// 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
Expanded table view
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 - 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 ❤️
Top comments (9)
Is this live on Papermark already?
It's live on Papermark 🎉
Nice post! What did you use to record / edit the videos and gifs?
Screen.studio
Thanks for sharing this, I get the following error when using fragments: Error: React.Children.only expected to receive a single React element child.
Solved this by using div, but that screws up the table for me obviously.
It was Next.js server component issue, changed it to a client component and it now works as intended.
You just saved me so much time @espennilsen
Love it!
can i know where is this live & its repo?