cx@crudx/mui · Components
@crudx/mui

Material UI component reference

Every component exported by @crudx/mui, rendered live with a copy-pasteable snippet. These are the building blocks that the higher-level CrudPanelView composes.

BreadcrumbView
import { BreadcrumbView } from '@crudx/mui';

Navigation trail. Items can be plain links or chips, with an optional icon per crumb. Pass `current` to mark the active path.

tsx
<BreadcrumbView
  items={[
    { label: 'Home', url: '/', icon: <HomeIcon fontSize="small" /> },
    { label: 'Team', url: '/team' },
    { label: 'Ada Lovelace' },
  ]}
  current="/team/ada"
/>
ButtonDropdown
import { ButtonDropdown } from '@crudx/mui';

Menu-style dropdown with either button or icon trigger. Each item dispatches its `key` through `onItemClick`.

Last:
tsx
<Stack direction="row" spacing={2} alignItems="center">
  <ButtonDropdown
    type="button"
    items={[
      { key: 'edit', title: 'Edit' },
      { key: 'duplicate', title: 'Duplicate' },
      { key: 'archive', title: 'Archive' },
    ]}
    onItemClick={setLast}
  >
    Actions
  </ButtonDropdown>
  <Typography variant="caption" color="text.secondary">
    Last: <code>{last ?? '—'}</code>
  </Typography>
</Stack>
Dialog
import { Dialog, DialogRefProps } from '@crudx/mui';

Imperative dialog driven by ref (`open()`, `close()`, `toggle()`). Built-in `confirmation`, `info`, `success`, `error`, `warning`, and `custom` variants.

Last action:
tsx
<Stack direction="row" spacing={2} alignItems="center">
  <Button variant="outlined" onClick={() => ref.current?.open()}>
    Open dialog
  </Button>
  <Typography variant="caption" color="text.secondary">
    Last action: <code>{result}</code>
  </Typography>
  <Dialog
    ref={ref}
    type="confirmation"
    title="Delete this record?"
    message="This action can't be undone. Are you sure you want to continue?"
    primaryText="Delete"
    secondaryText="Cancel"
    onClickPrimaryAction={() => setResult('confirmed')}
    onClickSecondaryAction={() => setResult('cancelled')}
  />
</Stack>
NumberFormatView
import { NumberFormatView } from '@crudx/mui';

Number formatter wrapper. Uses numeral.js format strings and supports prefix / postfix slots.

$1,234,567.89 USD74.2%2.5m
tsx
<Stack direction="row" spacing={3} alignItems="center">
  <NumberFormatView
    amount={1234567.89}
    format="0,0.00"
    prefix="$"
    postfix=" USD"
  />
  <NumberFormatView amount={0.7421} format="0.0%" />
  <NumberFormatView amount={2_500_000} format="0.0a" prefix="≈ " />
</Stack>
RenderFlexView
import { RenderFlexView } from '@crudx/mui';

Declarative MUI Grid layout. Each row is an array of `xs/sm/md`-sized cells. Useful when you want layout to live in data rather than JSX.

Cell A

Cell B

Full-width row

tsx
<RenderFlexView
  containerProps={{ spacing: 2 }}
  items={[
    [
      {
        xs: 12,
        sm: 6,
        children: (
          <Card variant="outlined">
            <CardContent>
              <Typography variant="body2">Cell A</Typography>
            </CardContent>
          </Card>
        ),
      },
      {
        xs: 12,
        sm: 6,
        children: (
          <Card variant="outlined">
            <CardContent>
              <Typography variant="body2">Cell B</Typography>
            </CardContent>
          </Card>
        ),
      },
    ],
    [
      {
        xs: 12,
        children: (
          <Card variant="outlined">
            <CardContent>
              <Typography variant="body2">Full-width row</Typography>
            </CardContent>
          </Card>
        ),
      },
    ],
  ]}
/>
RenderNodeView
import { RenderNodeView } from '@crudx/mui';

Inline horizontal stack with a keyed list of nodes — handy for icon + label combos that need stable keys.

23 favourites
tsx
<RenderNodeView
  spacing={1}
  items={[
    {
      key: 'icon',
      content: <FavoriteIcon color="error" fontSize="small" />,
    },
    { key: 'label', content: <span>23 favourites</span> },
  ]}
/>
Table
import { Table } from '@crudx/mui';

The full table primitive — head, rows and pagination wired up. Use this when you want a turnkey table without going through CrudTableView.

IDNAMEROLESALARY
1Ada LovelaceEngineer$95,000
2Alan TuringResearcher$110,000
3Grace HopperArchitect$120,000

Rows per page:

1–3 of 3

tsx
type Row = { id: number; name: string; role: string; salary: number };

const SAMPLE_ROWS: Row[] = [
  { id: 1, name: 'Ada Lovelace', role: 'Engineer', salary: 95000 },
  { id: 2, name: 'Alan Turing', role: 'Researcher', salary: 110000 },
  { id: 3, name: 'Grace Hopper', role: 'Architect', salary: 120000 },
];

const SAMPLE_COLUMNS = [
  { key: 'id', title: 'ID', width: 60, dataIndex: 'id' as const },
  { key: 'name', title: 'Name', dataIndex: 'name' as const, sortable: true },
  { key: 'role', title: 'Role', dataIndex: 'role' as const },
  {
    key: 'salary',
    title: 'Salary',
    align: 'right' as const,
    render: (_: unknown, record: Row) => (
      <NumberFormatView amount={record.salary} format="0,0" prefix="$" />
    ),
  },
];

<Table<Row>
  data={SAMPLE_ROWS}
  columns={SAMPLE_COLUMNS}
  striped
  bordered
  page={page}
  pageSize={pageSize}
  total={SAMPLE_ROWS.length}
  onPageChange={setPage}
  onPageSizeChange={setPageSize}
/>
TableHead
import { TableHead } from '@crudx/mui';

Standalone table header — use it when composing your own `<table>` instead of using `Table`. Drives sort indicators and the bulk-checkbox state.

IDNAMEsorted ascendingROLESALARY
tsx
type Row = { id: number; name: string; role: string; salary: number };

const SAMPLE_COLUMNS = [
  { key: 'id', title: 'ID', width: 60, dataIndex: 'id' as const },
  { key: 'name', title: 'Name', dataIndex: 'name' as const, sortable: true },
  { key: 'role', title: 'Role', dataIndex: 'role' as const },
  {
    key: 'salary',
    title: 'Salary',
    align: 'right' as const,
    render: (_: unknown, record: Row) => (
      <NumberFormatView amount={record.salary} format="0,0" prefix="$" />
    ),
  },
];

<Box
  component="table"
  sx={{ width: '100%', borderCollapse: 'collapse' }}
>
  <TableHead<Row>
    columns={SAMPLE_COLUMNS}
    checkbox={{ enabled: true }}
    checked="partial"
    sorting={{ defaultOrder: 'name', defaultDirection: 'asc' }}
  />
</Box>
TableRow
import { TableRow } from '@crudx/mui';

Standalone table row — pair with TableHead when you need a custom shell. Renders cells from the same column config as Table.

1Ada LovelaceEngineer$95,000
2Alan TuringResearcher$110,000
3Grace HopperArchitect$120,000
tsx
type Row = { id: number; name: string; role: string; salary: number };

const SAMPLE_ROWS: Row[] = [
  { id: 1, name: 'Ada Lovelace', role: 'Engineer', salary: 95000 },
  { id: 2, name: 'Alan Turing', role: 'Researcher', salary: 110000 },
  { id: 3, name: 'Grace Hopper', role: 'Architect', salary: 120000 },
];

const SAMPLE_COLUMNS = [
  { key: 'id', title: 'ID', width: 60, dataIndex: 'id' as const },
  { key: 'name', title: 'Name', dataIndex: 'name' as const, sortable: true },
  { key: 'role', title: 'Role', dataIndex: 'role' as const },
  {
    key: 'salary',
    title: 'Salary',
    align: 'right' as const,
    render: (_: unknown, record: Row) => (
      <NumberFormatView amount={record.salary} format="0,0" prefix="$" />
    ),
  },
];

<Box
  component="table"
  sx={{ width: '100%', borderCollapse: 'collapse' }}
>
  <Box component="tbody">
    {SAMPLE_ROWS.map((row, idx) => (
      <TableRow<Row>
        key={row.id}
        position={idx}
        data={row}
        columns={SAMPLE_COLUMNS}
      />
    ))}
  </Box>
</Box>
TablePagination
import { TablePagination } from '@crudx/mui';

Page controls. Note that MUI's variant is 0-indexed — `page=0` is the first page.

Rows per page:

1–10 of 142

tsx
<TablePagination
  page={page}
  pageSize={pageSize}
  total={142}
  pageSizeOptions={[10, 25, 50]}
  onPageChange={setPage}
  onPageSizeChange={setPageSize}
/>
TableSelectedBulkOptions
import { TableSelectedBulkOptions } from '@crudx/mui';

Bulk-action menu shown above a table when rows are selected. Defaults to a `{count} Item(s) Selected` label.

Last:
tsx
<Stack direction="row" spacing={2} alignItems="center">
  <TableSelectedBulkOptions
    total={3}
    items={[
      { key: 'delete', title: 'Delete selected' },
      { key: 'export', title: 'Export selected' },
    ]}
    onChange={(key: string) => setLast(key)}
  />
  <Typography variant="caption" color="text.secondary">
    Last: <code>{last ?? '—'}</code>
  </Typography>
</Stack>
TableSettingsDensityOptions
import { TableSettingsDensityOptions } from '@crudx/mui';

Density picker. Three preset rows — default / small / medium — with a localisable label per option.

Density: default
tsx
<Stack direction="row" spacing={2} alignItems="center">
  <TableSettingsDensityOptions
    onChange={(key: string) => setDensity(key)}
  />
  <Typography variant="caption" color="text.secondary">
    Density: <code>{density}</code>
  </Typography>
</Stack>
TableSettingsOptions
import { TableSettingsOptions } from '@crudx/mui';

Generic settings dropdown for table-level actions (column toggles, exports, …). Same item shape as ButtonDropdown.

Last:
tsx
<Stack direction="row" spacing={2} alignItems="center">
  <TableSettingsOptions
    items={[
      { key: 'columns', title: 'Manage columns' },
      { key: 'export', title: 'Export CSV' },
      { key: 'reset', title: 'Reset view' },
    ]}
    onChange={(key: string) => setLast(key)}
  />
  <Typography variant="caption" color="text.secondary">
    Last: <code>{last ?? '—'}</code>
  </Typography>
</Stack>
TableSettingsSortingOptions
import { TableSettingsSortingOptions, SortingOptionType } from '@crudx/mui';

Three-state global sort toggle (DEFAULT / ASC / DESC). Pair with a sortable list view that doesn't pin sort to a single column.

Sort: DEFAULT
tsx
<Stack direction="row" spacing={2} alignItems="center">
  <TableSettingsSortingOptions
    selected={selected}
    onChange={(key: string) => setSelected(key as SortingOptionType)}
  />
  <Typography variant="caption" color="text.secondary">
    Sort: <code>{selected}</code>
  </Typography>
</Stack>
TabView
import { TabView } from '@crudx/mui';

Tab list with content slots. Pass `content` per item, or use `renderContent` for fully controlled rendering.

tsx
<TabView
  items={[
    {
      key: 'overview',
      label: 'Overview',
      content: (
        <Box sx={{ p: 2 }}>
          <Typography variant="body2">
            Overview content rendered for the active tab.
          </Typography>
        </Box>
      ),
    },
    {
      key: 'activity',
      label: 'Activity',
      content: (
        <Box sx={{ p: 2 }}>
          <Typography variant="body2">No recent activity.</Typography>
        </Box>
      ),
    },
    {
      key: 'settings',
      label: 'Settings',
      content: (
        <Box sx={{ p: 2 }}>
          <Typography variant="body2">Settings tab content.</Typography>
        </Box>
      ),
    },
  ]}
/>
TooltipView
import { TooltipView } from '@crudx/mui';

Tooltip wrapper around an arbitrary trigger. Disabled automatically when `title` is empty unless you force `enabled`.

tsx
<Stack direction="row" spacing={2} alignItems="center">
  <TooltipView title="Edit this record" arrow>
    <Button startIcon={<EditIcon />}>Edit</Button>
  </TooltipView>
  <TooltipView title="Disabled — no permission" arrow>
    <span>
      <Button disabled>Save</Button>
    </span>
  </TooltipView>
</Stack>