Skip to main content

List Products Table Block

ListProductsTable is a comprehensive tabular listing block built on MUI Table primitives. It supports pinned columns, tabs, dismissible search chip filters, row actions, and deep layout composition through compound child blocks or function-based block overrides.

Installation

npm install @nodeblocks/frontend-list-products-table-block

What You Need

ItemWhy it matters
listProductsTableTitleTitle or React Node for the table header
createHrefThe destination link/hash for the "Create Product" action button
dataArray of row data matching ListProductsTableRowData
labelsFull labels object mapping table headers, placeholders, action buttons, and empty/fallback states
Controlled state

ListProductsTable is presentational and does not manage internal state for filters, search keywords, tab selection, or pagination. Tab changes only update the active tab state; if you want the rows to change, filter the data you pass into the table yourself or by query new list and update that data in response to callbacks such as onTabChange and onPageChange.

Code Examples

Live Editor
function Example() {
  const [currentTab, setCurrentTab] = React.useState('published');
  const [page, setPage] = React.useState(1);
  const [lastAction, setLastAction] = React.useState('Navigation and row actions will appear here.');

  const products = [
    {
      id: '1',
      tab: 'draft',
      category: 'Furniture',
      title: 'Premium Office Chair',
      publication: { status: 'PUBLISHED', since: '2026-01-12', until: '2026-02-12' },
      createdBy: 'Marty',
      createdAt: '2026-01-10T09:21:00Z',
      updatedBy: 'Alice',
      updatedAt: '2026-01-11T10:30:00Z',
      favoritesCount: 14,
      ordersCount: 22,
    },
    {
      id: '2',
      tab: 'published',
      category: 'Electronics',
      title: 'Wireless Headphones',
      publication: { status: 'PUBLISHED', since: '2026-01-03', until: '2026-03-03' },
      createdBy: 'Ken',
      createdAt: '2026-01-02T09:21:00Z',
      updatedBy: 'Marty',
      updatedAt: '2026-01-05T10:30:00Z',
      favoritesCount: 34,
      ordersCount: 13,
    }
  ];

  const visibleProducts = products.filter((product) => product.tab === currentTab);

  const tabs = [
    { key: 'draft', label: 'Draft' },
    { key: 'published', label: 'Published' },
    { key: 'archived', label: 'Archived' },
  ];

  const labels = {
    emptyStateMessage: 'No products found',
    searchFieldPlaceholder: 'Search products',
    actions: {
      createProduct: 'Create Product',
      createProductShort: 'Create',
      filter: 'Filter',
    },
    headerRow: {
      productTitle: 'Product Title',
      publicationPeriod: 'Publication Period',
      created: 'Created',
      updated: 'Updated',
      favorites: 'Favorites',
      orders: 'Orders',
    },
    rowActions: {
      edit: 'Edit Product',
      archive: 'Archive Product',
      restore: 'Restore Product',
    },
    cellData: {
      sinceUnset: 'Not set',
      untilUnset: 'Not set',
      dateUnset: 'No publication period',
    },
  };

  return (
    <div style={{ minHeight: 450 }}>
      <ListProductsTable
        listProductsTableTitle="Product Management"
        createHref="#create"
        labels={labels}
        data={visibleProducts}
        tabs={tabs}
        currentTab={currentTab}
        onTabChange={setCurrentTab}
        onNavigate={(to) => setLastAction(`Navigating to: ${to}`)}
        pagination={{
          currentPage: page,
          totalPages: 1,
          onPageChange: setPage,
        }}
      />
      <div style={{marginTop: 12, color: '#475569', fontSize: 13}}>{lastAction}</div>
    </div>
  );
}
Result
Loading...

Important Props

Core Props

PropTypeRequiredDefaultDescription
dataListProductsTableRowData[]Yes-Array of product data objects to display
isLoadingbooleanNoundefinedSwitches table layout to loader mode when true
onNavigate(to: string) => voidNoundefinedNavigation callback triggered on click actions (such as row clicks or create buttons)
onProductEdit(productId: string, title: string) => voidNoundefinedCallback triggered when the "Edit" row menu action is clicked
onProductArchive(productId: string, title: string) => voidNoundefinedCallback triggered when the "Archive" row menu action is clicked
onProductUnarchive(productId: string) => voidNoundefinedCallback triggered when the "Restore" row menu action is clicked
resolveRowAction(row: ListProductsTableRowData) => ('edit' | 'archive' | 'restore')[] | undefinedNoundefinedEvaluation callback mapping a row to active menu actions
shouldShowDropdownMenu(row: ListProductsTableRowData) => booleanNoundefinedConditional check to display or hide the dropdown row menu button entirely
paginationPaginationPropsNoundefinedStateful pagination controls: { currentPage, totalPages, onPageChange, className? }

Content Props

PropTypeRequiredDefaultDescription
listProductsTableTitleReactNodeYes-Title text or node rendered inside the Title sub-block
createHrefstringYes-Href passed to onNavigate callback on "Create Product" action click
labels{ emptyStateMessage: string; searchFieldPlaceholder: string; actions: { createProduct: string; createProductShort?: string; filter: string }; headerRow: { productTitle: string; publicationPeriod: string; created: string; updated: string; favorites: string; orders: string }; rowActions?: { edit: string; archive: string; restore: string }; cellData: { sinceUnset: string; untilUnset: string; dateUnset: string } }Yes-Direct map containing UI text configurations
rowHref(row: ListProductsTableRowData) => stringNoundefinedCustom row link generator callback
ordersHref(row: ListProductsTableRowData) => stringNoundefinedDirect link generator callback applied to the orders count
tabs{ key: string; label: string; isDisabled?: boolean; subtitle?: string }[]NoundefinedProduct filtering tab definitions
currentTabstringNoundefinedKey of the currently active/selected tab
onTabChange(tab: string) => voidNoundefinedSwitch callback triggered when another tab is clicked
searchValuestringNoundefinedActive search input value
onSearchFieldChange(value: string) => voidNoundefinedCallback triggered when typing inside the search input field
onSearchSubmit() => voidNoundefinedTriggered when pressing Enter inside search input or clicking search button
onFilterButtonClick() => voidNoundefinedCallback triggered when clicking the filter button next to search
searchChipsTitleReactNodeNoundefinedLabel displayed next to search chip container
searchChipsBaseTableSearchChip[]NoundefinedActive filters rendered as chips: { key, label }
onSearchChipDelete(chip: BaseTableSearchChip, index: number, event: SyntheticEvent) => voidNoundefinedCallback triggered when dismissing an active filter chip

labels Structure

Provide exact translations and overrides inside the labels property matching this structure:

{
emptyStateMessage: string;
searchFieldPlaceholder: string;
actions: {
createProduct: string;
createProductShort?: string;
filter: string;
};
headerRow: {
productTitle: string;
publicationPeriod: string;
created: string;
updated: string;
favorites: string;
orders: string;
};
rowActions?: {
edit: string;
archive: string;
restore: string;
};
cellData: {
sinceUnset: string;
untilUnset: string;
dateUnset: string;
};
}

Layout and Composition Props

PropTypeRequiredDefaultDescription
childrenBlocksOverride | ReactNodeNoundefinedCompound child elements or override function child
classNamestringNoundefinedCustom styling class applied to the root element
sxSxPropsNoundefinedMUI SX styling overrides passed to the outer Stack

ListProductsTable inherits all StackProps (except children). Default block order is ['header', 'searchChips', 'tabs', 'content', 'pagination'] (defaultBlockOrder).

Default UI Blocks

BlockBuilt onNotes
ListProductsTable (root)BaseTableMain tabular wrapper layout wrapping full table context
ListProductsTable.HeaderBaseTable.HeaderFlex container holding Title and Actions
ListProductsTable.TitleBaseTable.Header.TitleRenders the section header
ListProductsTable.ActionBaseTable.Header.ActionsFlex container holding search input, filters, and create actions
ListProductsTable.SearchChipsBaseTable.SearchChipsRenders active search filters as dismissible chips
ListProductsTable.TabsBaseTable.TabsResponsive tabs used for table filtering
ListProductsTable.ContentBaseTable.ContentOuter block wrapper for tables and loading spinners
ListProductsTable.LoaderBaseTable.Content.LoaderSpinner displayed during state transitions
ListProductsTable.TableBaseTable.Content.GridPrimary table grid presenting product metadata and row actions
ListProductsTable.PaginationBaseTable.PaginationControls footer pagination controls

TypeScript

import { ListProductsTable } from '@nodeblocks/frontend-list-products-table-block';
import type { ListProductsTableRowData } from '@nodeblocks/frontend-list-products-table-block';

const products: ListProductsTableRowData[] = [
{
id: 'prod-001',
category: 'Appliances',
title: 'Smart Smart Oven Pro',
publication: { status: 'PUBLISHED', since: '2026-05-15T00:00:00Z' },
createdBy: 'System admin',
createdAt: '2026-05-10T09:21:00Z',
updatedBy: 'System admin',
updatedAt: '2026-05-11T10:30:00Z',
favoritesCount: 15,
ordersCount: 3,
}
];

const tabs = [
{ key: 'published', label: 'Published Items' }
];

const labels = {
emptyStateMessage: 'No items',
searchFieldPlaceholder: 'Search catalog',
actions: { createProduct: 'New', filter: 'Filter' },
headerRow: {
productTitle: 'Product',
publicationPeriod: 'Period',
created: 'Created',
updated: 'Updated',
favorites: 'Favorites',
orders: 'Orders',
},
cellData: { sinceUnset: '-', untilUnset: '-', dateUnset: 'No schedule' },
};

<ListProductsTable
listProductsTableTitle="App Dashboard"
createHref="#new"
labels={labels}
data={products}
tabs={tabs}
currentTab="published"
/>;