- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
Table - Admin Components
The listing pages in the Admin show a table with pagination.
To create a component that shows a table with pagination, create the file src/admin/components/table.tsx
with the following content:
1import { useMemo } from "react"2import { Table as UiTable } from "@medusajs/ui"3 4export type TableProps = {5 columns: {6 key: string7 label?: string8 render?: (value: unknown) => React.ReactNode9 }[]10 data: Record<string, unknown>[]11 pageSize: number12 count: number13 currentPage: number14 setCurrentPage: (value: number) => void15}16 17export const Table = ({18 columns,19 data,20 pageSize,21 count,22 currentPage,23 setCurrentPage,24}: TableProps) => {25 const pageCount = useMemo(() => {26 return Math.ceil(count / pageSize)27 }, [data, pageSize])28 29 const canNextPage = useMemo(() => {30 return currentPage < pageCount - 131 }, [currentPage, pageCount])32 const canPreviousPage = useMemo(() => {33 return currentPage - 1 >= 034 }, [currentPage])35 36 const nextPage = () => {37 if (canNextPage) {38 setCurrentPage(currentPage + 1)39 }40 }41 42 const previousPage = () => {43 if (canPreviousPage) {44 setCurrentPage(currentPage - 1)45 }46 }47 48 return (49 <div className="flex h-full flex-col overflow-hidden !border-t-0">50 <UiTable>51 <UiTable.Header>52 <UiTable.Row>53 {columns.map((column, index) => (54 <UiTable.HeaderCell key={index}>55 {column.label || column.key}56 </UiTable.HeaderCell>57 ))}58 </UiTable.Row>59 </UiTable.Header>60 <UiTable.Body>61 {data.map((item, index) => {62 const rowIndex = "id" in item ? item.id as string : index63 return (64 <UiTable.Row key={rowIndex}>65 {columns.map((column, index) => (66 <UiTable.Cell key={`${rowIndex}-${index}`}>67 <>68 {column.render && column.render(item[column.key])}69 {!column.render && (70 <>{item[column.key] as string}</>71 )}72 </>73 </UiTable.Cell>74 ))}75 </UiTable.Row>76 )77 })}78 </UiTable.Body>79 </UiTable>80 <UiTable.Pagination81 count={count}82 pageSize={pageSize}83 pageIndex={currentPage}84 pageCount={pageCount}85 canPreviousPage={canPreviousPage}86 canNextPage={canNextPage}87 previousPage={previousPage}88 nextPage={nextPage}89 />90 </div>91 )92}
The Table
component uses the component from the UI package, with additional styling and rendering of data.
It accepts the following props:
columns
object[]The table's columns.
columns
object[]data
Record<string, unknown>[]columns
array.pageSize
numbercount
numbercurrentPage
numbersetCurrentPage
(value: number) => voidExample#
Use the Table
component in any widget or UI route.
For example, create the widget src/admin/widgets/product-widget.tsx
with the following content:
1import { defineWidgetConfig } from "@medusajs/admin-sdk"2import { StatusBadge } from "@medusajs/ui"3import { Table } from "../components/table"4import { useState } from "react"5import { Container } from "../components/container"6 7const ProductWidget = () => {8 const [currentPage, setCurrentPage] = useState(0)9 10 return (11 <Container>12 <Table13 columns={[14 {15 key: "name",16 label: "Name",17 },18 {19 key: "is_enabled",20 label: "Status",21 render: (value: unknown) => {22 const isEnabled = value as boolean23 24 return (25 <StatusBadge color={isEnabled ? "green" : "grey"}>26 {isEnabled ? "Enabled" : "Disabled"}27 </StatusBadge>28 )29 },30 },31 ]}32 data={[33 {34 name: "John",35 is_enabled: true,36 },37 {38 name: "Jane",39 is_enabled: false,40 },41 ]}42 pageSize={2}43 count={2}44 currentPage={currentPage}45 setCurrentPage={setCurrentPage}46 />47 </Container>48 )49}50 51export const config = defineWidgetConfig({52 zone: "product.details.before",53})54 55export default ProductWidget
This widget also uses the Container custom component.
Example With Data Fetching#
This section shows you how to use the Table
component when fetching data from the Medusa application's API routes.
Assuming you've set up the JS SDK as explained in this guide, create the UI route src/admin/routes/custom/page.tsx
with the following content:
9import { Header } from "../../components/header"10 11const CustomPage = () => {12 const [currentPage, setCurrentPage] = useState(0)13 const limit = 1514 const offset = useMemo(() => {15 return currentPage * limit16 }, [currentPage])17 18 const { data } = useQuery({19 queryFn: () => sdk.admin.product.list({20 limit,21 offset22 }),23 queryKey: [["products", limit, offset]],24 })25 26 // TODO display table27}28 29export const config = defineRouteConfig({30 label: "Custom",31 icon: ChatBubbleLeftRight,32})33 34export default CustomPage
In the CustomPage
component, you define:
- A state variable
currentPage
that stores the current page of the table. - A
limit
variable, indicating how many items to retrieve per page - An
offset
memoized variable indicating how many items to skip before the retrieved items. It's calculated as a multiplication ofcurrentPage
andlimit
.
Then, you use useQuery
from Tanstack Query to retrieve products using the JS SDK. You pass limit
and offset
as query parameters, and you set the queryKey
, which is used for caching and revalidation, to be based on the key products
, along with the current limit and offset. So, whenever the offset
variable changes, the request is sent again to retrieve the products of the current page.
useQuery
returns an object containing data
, which holds the response fields including the products and pagination fields.
Then, to display the table, replace the TODO
with the following:
1return (2 <SingleColumnLayout>3 <Container>4 <Header title="Products" />5 {data && (6 <Table7 columns={[8 {9 key: "id",10 label: "ID"11 },12 {13 key: "title",14 label: "Title"15 }16 ]}17 data={data.products as any}18 pageSize={data.limit}19 count={data.count}20 currentPage={currentPage}21 setCurrentPage={setCurrentPage}22 />23 )}24 </Container>25 </SingleColumnLayout>26)
Aside from the Table
component, this UI route also uses the SingleColumnLayout, Container, and Header custom component.
If data
isn't undefined
, you display the Table
component passing it the following props:
columns
: The columns to show. You only show the product's ID and title.data
: The rows of the table. You pass it theproducts
property ofdata
.pageSize
: The maximum number of items per page. You pass it thecount
property ofdata
.currentPage
andsetCurrentPage
: The current page and the function to change it.
To test it out, log into the Medusa Admin and open http://localhost:9000/app/custom
. You'll find a table of products with pagination.