Building a Reusable Loadable Table in ReactJS
Welcome to our guide on building a reusable loadable table component in ReactJS. By the end of this article, you'll have a versatile table component that you can easily integrate into your React or Next.js projects. This component will have features like a loading state, dynamic header, empty state, and various customizable props and states. The best part? You can easily use this component anywhere in your app. We'll also keep things simple by using pure HTML for the table structure.
Introduction
Tables are a fundamental part of many web applications, and creating reusable components can greatly simplify development and maintenance. Our goal here is to create a LoadableTable
component that you can drop into any part of your app. We'll be using pure HTML for the table structure, making it lightweight and easy to style to match your application's design.
Prerequisites
Before we dive into creating our reusable table component, make sure you have the following prerequisites in place:
Basic knowledge of ReactJS or Next.js
Familiarity with TypeScript and TailwindCSS
An existing React or Next.js project
If you meet these requirements, you're ready to get started!
Getting Started
Let's jump right in and start creating our LoadableTable
component. First, navigate to your project's components folder and create a new file named LoadableTable.tsx
. Inside this file, add the following code:
// Import necessary dependencies
import SpinnerIcon from '@/elements/SpinnerIcon';
import {
Children,
cloneElement,
ReactNode,
useRef,
useEffect,
useState,
ReactElement,
FC,
} from 'react';
// Define the props for the LoadableTable component
interface LoadableTableProps {
isLoading: boolean;
noData: boolean;
header: ReactElement | boolean;
noDataText: string | ReactElement;
children: ReactNode;
}
// Create the LoadableTable component
const LoadableTable: FC<LoadableTableProps> = ({
isLoading,
children,
header,
noData,
noDataText = 'No Data found',
}) => {
// Initialize necessary references and state
const childrenRef = useRef<HTMLTableSectionElement[]>([]);
const [columnCount, setColumnCount] = useState(0);
useEffect(() => {
// Count the number of columns in the table header
const count = childrenRef.current[0]?.querySelectorAll('th').length;
setColumnCount(count);
}, []);
// Render the table component
return (
<table className="items-center w-full bg-transparent border-collapse">
<thead>
{header &&
Children.map(header, (child, index) =>
cloneElement(child as ReactElement, {
ref: (ref: HTMLTableSectionElement) =>
(childrenRef.current[index] = ref),
})
)}
</thead>
{isLoading && (
<tbody>
<tr className="single-item">
<td
className="table-data"
align="center"
colSpan={columnCount}
style={{
padding: '70px 0',
border: 0,
}}
>
<SpinnerIcon />
</td>
</tr>
</tbody>
)}
{!isLoading && !noData && children}
{!isLoading &&
noData &&
(typeof noDataText === 'string' ? (
<tbody>
<tr className="single-item">
<td
align="center"
colSpan={columnCount}
style={{
padding: '60px 0',
border: 0,
}}
>
<div style={{ textAlign: 'center', fontSize: '1rem' }}>
{noDataText}
</div>
</td>
</tr>
</tbody>
) : (
<tbody>
<tr className="single-item">
<td
align="center"
colSpan={columnCount}
style={{
padding: '40px 0',
border: 0,
}}
>
{noDataText}
</td>
</tr>
</tbody>
))}
</table>
);
};
export default LoadableTable;
In the code above, we've created the LoadableTable
component with props for loading state, header, no data state, and more. We've also included logic to dynamically calculate the number of columns in the table header. We've imported a SpinnerIcon from our elements folder, which will display a spinning SVG to indicate the table's loading state. You can use the provided SpinnerIcon code in your project.
const SpinnerIcon = () => {
return (
<svg
width={40}
height={40}
viewBox="0 0 38 38"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient x1="8.042%" y1="0%" x2="65.682%" y2="23.865%" id="a">
<stop
stopColor={'#38B776'}
stopOpacity="0"
offset="0%"
></stop>
<stop
stopColor={'#38B776'}
stopOpacity=".631"
offset="63.146%"
></stop>
<stop stopColor={'#38B776'} offset="100%"></stop>
</linearGradient>
</defs>
<g fill="none" fillRule="evenodd">
<g transform="translate(1 1)">
<path
d="M36 18c0-9.94-8.06-18-18-18"
id="Oval-2"
stroke={'#38B776'}
strokeWidth="2"
>
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="0.9s"
repeatCount="indefinite"
></animateTransform>
</path>
<circle fill={'#38B776'} cx="36" cy="18" r="1">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="0.9s"
repeatCount="indefinite"
></animateTransform>
</circle>
</g>
</g>
</svg>
);
}
export default SpinnerIcon;
Using Our Reusable Loadable Table Component
Now that we have our LoadableTable
component, let's put it to use. Create another file called UsersTable.tsx
and use the LoadableTable
component to display user data. Here's the code for UsersTable
:
import LoadableTable from '@/components/LoadableTable';
const UsersTable = ({ isLoading, users }: { isLoading: boolean; users: [] }) => {
return (
<LoadableTable
header={
<tr>
<th className="table-head">User ID</th>
<th className="table-head">User Name</th>
<th className="table-head">Email</th>
<th className="table-head">Date Registered</th>
</tr>
}
isLoading={isLoading}
noData={users?.length === 0}
>
<tbody>
{users &&
users.map((user) => (
<tr key={user._id}>
<td className="table-data">{user.userId}</td>
<td className="table-data">{user.name}</td>
<td className="table-data">{user.email}</td>
<td className="table-data">{user.created_at}</td>
</tr>
))}
</tbody>
</LoadableTable>
);
};
export default UsersTable;
In this code, we've created a UsersTable
component that uses the LoadableTable
component to display user data. We've provided a dynamic header and checked for loading and empty data states. Feel free to use this template with your own data to populate the table. You can also include other props like footer
to pass a pagination component, etc.
That's it! You can now use this LoadableTable
component anywhere in your app.
Conclusion
Reusable components like our LoadableTable
can save you time and make your project more maintainable. We hope you found this guide insightful and that it helps you create efficient and flexible tables for your React or Next.js projects. Thank you for reading!
Subscribe to my newsletter
Read articles from Emerenini Cynthia Ngozi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Emerenini Cynthia Ngozi
Emerenini Cynthia Ngozi
Frontend Software Engineer with 3+ years of experience in solving software problems