MDK Logo

Data display

Cards, tables, tags, and data presentation components

Components for displaying data in cards, tables, badges, tags, and other visual formats.

Prerequisites

DataTable

Full-featured data table built on TanStack Table with sorting, pagination, and row selection.

Import

import { DataTable } from '@tetherto/mdk-react-devkit/core'

// Types for column definitions
import type {
  DataTableColumnDef,
  DataTableSortingState,
  DataTablePaginationState,
  DataTableRowSelectionState,
} from '@tetherto/mdk-react-devkit/core'

Basic usage

import { DataTable } from '@tetherto/mdk-react-devkit/core'
import type { DataTableColumnDef } from '@tetherto/mdk-react-devkit/core'

type Miner = {
  id: string
  name: string
  hashrate: number
  status: string
}

const columns: DataTableColumnDef<Miner>[] = [
  {
    accessorKey: 'name',
    header: 'Name',
  },
  {
    accessorKey: 'hashrate',
    header: 'Hashrate',
    cell: ({ row }) => `${row.original.hashrate} TH/s`,
  },
  {
    accessorKey: 'status',
    header: 'Status',
  },
]

function MinersTable() {
  return <DataTable columns={columns} data={miners} />
}

With sorting and pagination

const [sorting, setSorting] = useState<DataTableSortingState>([])
const [pagination, setPagination] = useState<DataTablePaginationState>({
  pageIndex: 0,
  pageSize: 10,
})

<DataTable
  columns={columns}
  data={miners}
  sorting={sorting}
  onSortingChange={setSorting}
  pagination={pagination}
  onPaginationChange={setPagination}
/>

With row selection

const [rowSelection, setRowSelection] = useState<DataTableRowSelectionState>({})

<DataTable
  columns={columns}
  data={miners}
  rowSelection={rowSelection}
  onRowSelectionChange={setRowSelection}
  enableRowSelection
/>

Styling

  • .mdk-data-table: Root container
  • .mdk-data-table__header: Header row
  • .mdk-data-table__body: Table body
  • .mdk-data-table__row: Data row
  • .mdk-data-table__cell: Table cell

Card

Flexible container component with optional header, body, and footer slots.

Import

import { Card, CardHeader, CardBody, CardFooter } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
classNameOptionalstringnoneAdditional CSS class
onClickOptionalfunctionnoneClick handler (adds clickable styling)
childrenOptionalReactNodenoneCard content

Basic usage

<Card>
  <Card.Header>Title</Card.Header>
  <Card.Body>Content goes here</Card.Body>
  <Card.Footer>Actions</Card.Footer>
</Card>

Default children are automatically wrapped in the body slot:

<Card>
  <Card.Header>Title</Card.Header>
  This content goes to the body automatically
</Card>

Clickable card

<Card onClick={() => navigate('/details')}>
  <Card.Header>Click me</Card.Header>
  <Card.Body>Navigates on click</Card.Body>
</Card>

Styling

  • .mdk-card: Root container
  • .mdk-card--clickable: Applied when onClick is provided
  • .mdk-card__header: Header slot
  • .mdk-card__body: Body slot
  • .mdk-card__footer: Footer slot

Accordion

Collapsible content sections built on Radix UI primitives.

Import

import {
  Accordion,
  AccordionItem,
  AccordionTrigger,
  AccordionContent,
} from '@tetherto/mdk-react-devkit/core'

Accordion props

PropStatusTypeDefaultDescription
titleOptionalstring''Accordion header title
isOpenedOptionalbooleanfalseInitially expanded
isRowOptionalbooleanfalseRow layout for content
unpaddedOptionalbooleanfalseRemove content padding
noBorderOptionalbooleanfalseRemove trigger border
solidBackgroundOptionalbooleanfalseSolid background color
showToggleIconOptionalbooleantrueShow expand/collapse icon
toggleIconPositionOptional'left' | 'right''left'Icon position
customLabelOptionalReactNodenoneCustom label next to title
onValueChangeOptionalfunctionnoneCallback when state changes

Basic usage

<Accordion title="FAQ Section">
  <p>This content can be expanded or collapsed.</p>
</Accordion>

With custom label

<Accordion
  title="System Status"
  customLabel={<Badge variant="success">Active</Badge>}
  showToggleIcon={false}
>
  <p>All systems operational.</p>
</Accordion>

Multiple items

<AccordionRoot type="multiple">
  <AccordionItem value="item-1">
    <AccordionTrigger>Section 1</AccordionTrigger>
    <AccordionContent>Content 1</AccordionContent>
  </AccordionItem>
  <AccordionItem value="item-2">
    <AccordionTrigger>Section 2</AccordionTrigger>
    <AccordionContent>Content 2</AccordionContent>
  </AccordionItem>
</AccordionRoot>

Styling

  • .mdk-accordion: Root container
  • .mdk-accordion--solid-background: Solid background variant
  • .mdk-accordion__item: Individual item
  • .mdk-accordion__trigger: Clickable header
  • .mdk-accordion__content: Collapsible content area

Badge

Numeric or status badge that can wrap content or stand alone.

Import

import { Badge } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
countOptionalnumber0Number to display
overflowCountOptionalnumber99Max count before showing "99+"
showZeroOptionalbooleanfalseShow badge when count is 0
dotOptionalbooleanfalseShow as dot instead of number
textOptionalstringnoneCustom text content
colorOptionalColorVariant'primary'Badge color
sizeOptional'sm' | 'md' | 'lg''md'Badge size
statusOptional'success' | 'processing' | 'error' | 'warning' | 'default'noneStatus variant
squareOptionalbooleanfalseSquare badge (no border-radius)
offsetOptional[number, number][0, 0]Position offset [x, y]

Basic usage

// Number badge on content
<Badge count={5}>
  <Button>Messages</Button>
</Badge>

// Overflow
<Badge count={100} overflowCount={99}>
  <BellIcon />
</Badge>
// Shows "99+"

Dot badge

<Badge dot>
  <NotificationIcon />
</Badge>

Status badge

<Badge status="success" text="Online" />
<Badge status="error" text="Offline" />
<Badge status="processing" text="Syncing" />
<Badge status="warning" text="Warning" />

Standalone badge

<Badge count={25} />
<Badge text="NEW" color="primary" square />

Styling

  • .mdk-badge: Badge element
  • .mdk-badge--{color}: Color variant
  • .mdk-badge--{size}: Size variant
  • .mdk-badge--dot: Dot variant
  • .mdk-badge--status: Status variant
  • .mdk-badge-wrapper: Wrapper when wrapping content

Pagination

Page navigation with size selector.

Import

import { Pagination } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
currentOptionalnumber1Current page number
totalOptionalnumber0Total number of items
pageSizeOptionalnumber20Items per page
pageSizeOptionsOptionalnumber[][10, 20, 50, 100]Page size options
showSizeChangerOptionalbooleantrueShow page size dropdown
showTotalOptionalbooleanfalseShow total count text
disabledOptionalbooleanfalseDisable pagination
sizeOptional'sm' | 'md' | 'lg''sm'Size variant
onChangeOptional(page: number, pageSize: number) => voidnoneCallback when the page number or page size changes
onSizeChangeOptional(current: number, size: number) => voidnoneCallback when the page size changes

Basic usage

const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(20)

<Pagination
  current={page}
  total={500}
  pageSize={pageSize}
  onChange={(newPage, newSize) => {
    setPage(newPage)
    setPageSize(newSize)
  }}
/>

With total display

<Pagination
  current={1}
  total={100}
  pageSize={20}
  showTotal
/>
// Shows "1-20 of 100"

Styling

  • .mdk-pagination: Root container
  • .mdk-pagination__pages: Page buttons container
  • .mdk-pagination__button: Navigation button
  • .mdk-pagination__button--active: Active page
  • .mdk-pagination__total: Total count text
  • .mdk-pagination__size-changer: Page size select

Tag

Removable tag/chip component.

Import

import { Tag } from '@tetherto/mdk-react-devkit/core'

Basic usage

<Tag>Default Tag</Tag>
<Tag color="primary">Primary</Tag>
<Tag color="success">Success</Tag>
<Tag onClose={() => handleRemove()}>Removable</Tag>

Avatar

User avatar display component with fallback initials.

Import

import { Avatar } from '@tetherto/mdk-react-devkit/core'

Basic usage

<Avatar src="/user.jpg" alt="User" />
<Avatar>JD</Avatar>
<Avatar src="/user.jpg" size="lg" />

SkeletonBlock

Loading placeholder animation.

Import

import { SkeletonBlock } from '@tetherto/mdk-react-devkit/core'

Basic usage

// Text skeleton
<SkeletonBlock className="h-4 w-48" />

// Circle skeleton
<SkeletonBlock className="h-12 w-12 rounded-full" />

// Card skeleton
<Card>
  <Card.Body>
    <SkeletonBlock className="h-4 w-full mb-2" />
    <SkeletonBlock className="h-4 w-3/4" />
  </Card.Body>
</Card>

EmptyState

Placeholder component for empty data states.

Import

import { EmptyState } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
descriptionOptionalReactNodenoneRequired. Description text
imageOptional'default' | 'simple' | ReactNode'default'Image/icon to display
sizeOptional'sm' | 'md' | 'lg''md'Size variant

Basic usage

<EmptyState description="No data available" />

<EmptyState
  description="No miners found matching your search"
  image="simple"
  size="sm"
/>

Custom image

<EmptyState
  description="No alerts at this time"
  image={<BellOffIcon size={48} />}
/>

Styling

  • .mdk-empty-state: Root container
  • .mdk-empty-state--{size}: Size variant
  • .mdk-empty-state__image: Image container
  • .mdk-empty-state__description: Description text

BtcAveragePrice

Displays a formatted BTC/USD price with a configurable label. Shows a dash when the value is absent, non-finite, or negative.

Import

import { BtcAveragePrice } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
priceOptionalnumber | nullnoneBTC price in USD; formatted with grouping and no decimal places. Shows - when null, undefined, non-finite, or negative
labelOptionalstring'BTC Average Price'Label shown before the price

Basic usage

<BtcAveragePrice price={97_500} />
<BtcAveragePrice price={null} />

Typography

Text styling components for headings and paragraphs.

Import

import { Typography } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
variantOptional'heading1' | 'heading2' | 'heading3' | 'body' | 'secondary' | 'caption'noneTypography variant
sizeOptional'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'noneText size
weightOptional'light' | 'normal' | 'medium' | 'semibold' | 'bold'noneFont weight
alignOptionalTextAlignnoneText alignment
colorOptional'default' | 'muted' | 'primary' | 'success' | 'warning' | 'error'noneText color variant
truncateOptionalbooleanfalseTruncate text with ellipsis
classNameOptionalstringnoneCustom class name

Basic usage

<Typography variant="heading1">Operations dashboard</Typography>
<Typography variant="heading2">Sites</Typography>
<Typography variant="body">
  Primary site operations are running within expected parameters.
</Typography>
<Typography variant="secondary">Last refreshed 12 seconds ago.</Typography>
<Typography variant="caption" color="muted">Updated 2 minutes ago</Typography>
<Typography weight="semibold" align="center" truncate>Site #1</Typography>

Indicator

Status dot for online/offline/warning states.

Import

import { Indicator } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
colorOptional'gray' | 'slate' | 'red' | 'amber' | 'yellow' | 'green' | 'blue' | 'purple'noneColor variant of the indicator dot
sizeOptional'sm' | 'md' | 'lg'noneSize variant of the indicator
verticalOptionalbooleanfalseWhen true, adds extra spacing between child elements and stacks them vertically
onClickOptionalfunctionnoneClick handler
childrenOptionalReactNodenoneLabel content (text, icons, or multiple elements)
classNameOptionalstringnoneCustom class name

Basic usage

<Indicator color="green">Online</Indicator>
<Indicator color="red">Offline</Indicator>
<Indicator color="amber">Maintenance</Indicator>

<Indicator color="green" size="sm">Running</Indicator>

<Indicator color="green" vertical>
  <span>Running</span>
  <span>142</span>
</Indicator>

CurrencyToggler

Toggle between different currency display formats.

Import

import { CurrencyToggler } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
currenciesRequired(string | CurrencyItem)[]noneCurrencies to toggle between, as codes or CurrencyItem objects
valueRequiredstringnoneCurrently selected currency code
onChangeRequired(currency: string) => voidnoneCalled with the newly selected currency code
classNameOptionalstringnoneCustom class name for the root element

Basic usage

<CurrencyToggler
  value={currency}
  onChange={setCurrency}
  currencies={['USD', 'BTC', 'SAT']}
/>

ListViewFilter

Filter controls for list and table views with search and category filtering.

Import

import { ListViewFilter } from '@tetherto/mdk-react-devkit/core'
import type { CascaderValue, LocalFilters } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
optionsRequiredCascaderOption[]noneCascader options for filtering
onChangeRequired(selections: CascaderValue[]) => voidnoneCallback when filters change
localFiltersOptionalLocalFiltersnoneCurrent filter values as key-value pairs (e.g. { status: ['active'] })
filterKeyOptionalstringnoneKey to force re-mounting the Cascader when filters change (resets its internal state)
classNameOptionalstringnoneCustom class name for the filter button

Basic usage

const FILTER_OPTIONS = [
  {
    value: 'type',
    label: 'Type',
    children: [
      { value: 'Antminer S19XP', label: 'Antminer S19XP' },
      { value: 'Avalon A1346', label: 'Avalon A1346' },
    ],
  },
  {
    value: 'status',
    label: 'Status',
    children: [
      { value: 'active', label: 'Active' },
      { value: 'offline', label: 'Offline' },
    ],
  },
]

const [filters, setFilters] = useState<LocalFilters>({})

<ListViewFilter
  options={FILTER_OPTIONS}
  localFilters={filters}
  onChange={(selections) => setFilters(selectionToFilters(selections))}
/>

Mosaic

Grid layout component for dashboard widgets and responsive content arrangements.

Import

import { Mosaic, MosaicItem } from '@tetherto/mdk-react-devkit/core'

Props

PropStatusTypeDefaultDescription
templateRequiredstring[] | string[][]noneGrid layout template — area names per row that map child MosaicItems to grid regions
childrenRequiredReactNodenoneMosaicItem elements placed into the named grid areas
columnsOptionalstring | string[]noneColumn track sizes (e.g. '1fr 2fr 1fr'); defaults to equal columns derived from the template
gapOptionalstring'12px'Gap between grid cells (any CSS length)
rowHeightOptionalstring'auto'Height applied to each grid row (any CSS length)
classNameOptionalstringnoneCustom class name for the root element

Basic usage

<Mosaic columns={3} gap={16}>
  <MosaicItem>
    <Card>Hashrate</Card>
  </MosaicItem>
  <MosaicItem span={2}>
    <Card>Active miners</Card>
  </MosaicItem>
  <MosaicItem>
    <Card>Power usage</Card>
  </MosaicItem>
</Mosaic>

On this page