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

LabeledCard

Card variant that pairs a tiny label above the body - used for compact stat or metadata blocks. Supports an optional navigation link on the label and a set of layout modifiers.

A generic card container with a header label, an optional navigation link on the label, and a set of layout modifiers. Useful for compact stat or metadata blocks.

Import

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

Props

All props are optional.

PropStatusTypeDefaultDescription
labelOptionalReactNodenoneHeader content shown above the card body
childrenOptionalReactNodenoneCard body content
getNavigateOptionsOptional(label: string) => { href?: string; target?: string }noneReturns a link href/target for the label; only activates when label is a plain string
isDarkOptionalbooleanfalseApplies a dark background modifier
isFullWidthOptionalbooleanfalseStretches the card to full container width
isFullHeightOptionalbooleanfalseStretches the card to full container height
isRelativeOptionalbooleanfalseSets position: relative on the container
isScrollableOptionalbooleanfalseEnables vertical scroll on the card body
hasNoWrapOptionalbooleanfalsePrevents content from wrapping
hasNoMarginOptionalbooleanfalseRemoves default margin
hasNoBorderOptionalbooleanfalseRemoves the card border
classNameOptionalstringnoneAdditional class for the root element

Basic usage

<LabeledCard label="Device Overview" isFullWidth>
  <p>Content goes here.</p>
</LabeledCard>
<LabeledCard
  label="Miners with error"
  getNavigateOptions={(label) => ({ href: '/miners?filter=error' })}
>
  <MinerList />
</LabeledCard>

When label is the string 'Miners with error', an informational tooltip is added automatically, explaining that minor errors not affecting hashrate are excluded.

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
onChangeOptionalfunctionnonePage/size change callback

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

Typography

Text styling components for headings and paragraphs.

Import

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

Basic usage

<Typography variant="h1">Dashboard</Typography>
<Typography variant="h2">Miner status</Typography>
<Typography variant="body">
  Primary site operations are running within expected parameters.
</Typography>
<Typography variant="caption">Updated 2 minutes ago</Typography>

Indicator

Status dot for online/offline/warning states.

Import

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

Basic usage

<Indicator status="online" />
<Indicator status="offline" />
<Indicator status="warning" />

<span className="flex items-center gap-2">
  <Indicator status="online" />
  Miner online
</span>

CurrencyToggler

Toggle between different currency display formats.

Import

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

Basic usage

<CurrencyToggler
  value={currency}
  onChange={setCurrency}
  options={['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'

Basic usage

<ListViewFilter
  search={search}
  onSearchChange={setSearch}
  filters={[
    { key: 'status', label: 'Status', options: ['online', 'offline'] },
    { key: 'site', label: 'Site', options: ['A', 'B', 'C'] },
  ]}
  activeFilters={activeFilters}
  onFilterChange={setActiveFilters}
/>

Mosaic

Grid layout component for dashboard widgets and responsive content arrangements.

Import

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

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