--- sidebar_label: "Table" --- import { TypeList } from "docs-ui" export const metadata = { title: `Table - Admin Components`, } # {metadata.title} If you're using [Medusa v2.4.0+](https://github.com/medusajs/medusa/releases/tag/v2.4.0), it's recommended to use the [Data Table](../data-table/page.mdx) component instead as it provides features for sorting, filtering, pagination, and more with a simpler API. You can use the `Table` component from Medusa UI to display data in a table. It's mostly recommended for simpler tables. To create a component that shows a table with pagination, create the file `src/admin/components/table.tsx` with the following content: ```tsx title="src/admin/components/table.tsx" import { useMemo } from "react" import { Table as UiTable } from "@medusajs/ui" export type TableProps = { columns: { key: string label?: string render?: (value: unknown) => React.ReactNode }[] data: Record[] pageSize: number count: number currentPage: number setCurrentPage: (value: number) => void } export const Table = ({ columns, data, pageSize, count, currentPage, setCurrentPage, }: TableProps) => { const pageCount = useMemo(() => { return Math.ceil(count / pageSize) }, [data, pageSize]) const canNextPage = useMemo(() => { return currentPage < pageCount - 1 }, [currentPage, pageCount]) const canPreviousPage = useMemo(() => { return currentPage - 1 >= 0 }, [currentPage]) const nextPage = () => { if (canNextPage) { setCurrentPage(currentPage + 1) } } const previousPage = () => { if (canPreviousPage) { setCurrentPage(currentPage - 1) } } return (
{columns.map((column, index) => ( {column.label || column.key} ))} {data.map((item, index) => { const rowIndex = "id" in item ? item.id as string : index return ( {columns.map((column, index) => ( <> {column.render && column.render(item[column.key])} {!column.render && ( <>{item[column.key] as string} )} ))} ) })}
) } ``` The `Table` component uses the component from the [UI package](!ui!/components/table), with additional styling and rendering of data. It accepts the following props: React.ReactNode`", optional: true, description: "By default, the data is shown as-is in the table. You can use this function to change how the value is rendered. The function receives the value is a parameter and returns a React node." } ] }, { name: "data", type: "`Record[]`", optional: false, description: "The data to show in the table for the current page. The keys of each object should be in the `columns` array." }, { name: "pageSize", type: "`number`", optional: false, description: "The number of items to show per page." }, { name: "count", type: "`number`", optional: false, description: "The total number of items." }, { name: "currentPage", type: "`number`", optional: false, description: "A zero-based index indicating the current page's number." }, { name: "setCurrentPage", type: "`(value: number) => void`", optional: false, description: "A function used to change the current page." } ]} /> --- ## Example 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: ```tsx title="src/admin/widgets/product-widget.tsx" import { defineWidgetConfig } from "@medusajs/admin-sdk" import { StatusBadge } from "@medusajs/ui" import { Table } from "../components/table" import { useState } from "react" import { Container } from "../components/container" const ProductWidget = () => { const [currentPage, setCurrentPage] = useState(0) return ( { const isEnabled = value as boolean return ( {isEnabled ? "Enabled" : "Disabled"} ) }, }, ]} data={[ { name: "John", is_enabled: true, }, { name: "Jane", is_enabled: false, }, ]} pageSize={2} count={2} currentPage={currentPage} setCurrentPage={setCurrentPage} /> ) } export const config = defineWidgetConfig({ zone: "product.details.before", }) export default ProductWidget ``` This widget also uses the [Container](../container.mdx) 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](../../../js-sdk/page.mdx), create the UI route `src/admin/routes/custom/page.tsx` with the following content: export const tableExampleHighlights = [ ["12", "currentPage", "The table's current page."], ["13", "limit", "The maximum number of items per page."], ["14", "offset", "The number of items to skip before retrieving the current page's items."], ["18", "data", "The response fields."], ["18", "useQuery", "Fetch products from the Medusa application."] ] ```tsx title="src/admin/routes/custom/page.tsx" collapsibleLines="1-10" expandButtonLabel="Show Imports" highlights={tableExampleHighlights} import { defineRouteConfig } from "@medusajs/admin-sdk" import { ChatBubbleLeftRight } from "@medusajs/icons" import { useQuery } from "@tanstack/react-query" import { SingleColumnLayout } from "../../layouts/single-column" import { Table } from "../../components/table" import { sdk } from "../../lib/config" import { useMemo, useState } from "react" import { Container } from "../../components/container" import { Header } from "../../components/header" const CustomPage = () => { const [currentPage, setCurrentPage] = useState(0) const limit = 15 const offset = useMemo(() => { return currentPage * limit }, [currentPage]) const { data } = useQuery({ queryFn: () => sdk.admin.product.list({ limit, offset, }), queryKey: [["products", limit, offset]], }) // TODO display table } export const config = defineRouteConfig({ label: "Custom", icon: ChatBubbleLeftRight, }) export 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 of `currentPage` and `limit`. Then, you use `useQuery` from [Tanstack Query](https://tanstack.com/query/latest) 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. You can change the query to send a request to a custom API route as explained in [this guide](../../../js-sdk/page.mdx#send-requests-to-custom-routes). Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency. `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: ```tsx return (
{data && (
)} ) ``` Aside from the `Table` component, this UI route also uses the [SingleColumnLayout](../../layouts/single-column/page.mdx), [Container](../container/page.mdx), and [Header](../header/page.mdx) 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 the `products` property of `data`. - `pageSize`: The maximum number of items per page. You pass it the `count` property of `data`. - `currentPage` and `setCurrentPage`: 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.