ContainerRow
One container with its state, image, uptime, ports, usage and actions. Pick it for a list of containers or services; the demo changes state, shows a busy row and confirms with a polite message.
- ContainerRow
PreviewLink to the section Preview
Live in all three themes. Everything here is interactive.
Light theme, ContainerRow
Runningweb
shop-web-1Healthynode:26-alpineUp 3 hoursOpen in the browser :5173→51732.4% · 128 MiB (3.1%)Unhealthydb
shop-db-1Unhealthypostgres:18Up 12 minutesOpen in the browser :5432→5432Crashedworker
shop-worker-1Exit code 137registry.example.com/shop/worker-with-a-long-image-name:2026.11.08-build.4512Exited 2 minutes agoPausedcache
shop-cache-1valkey:9PausedOpen in the browser :6379→6379:5353→53/udpStoppedmail
shop-mail-1axllent/mailpitStoppedDark theme, ContainerRow
Runningweb
shop-web-1Healthynode:26-alpineUp 3 hoursOpen in the browser :5173→51732.4% · 128 MiB (3.1%)Unhealthydb
shop-db-1Unhealthypostgres:18Up 12 minutesOpen in the browser :5432→5432Crashedworker
shop-worker-1Exit code 137registry.example.com/shop/worker-with-a-long-image-name:2026.11.08-build.4512Exited 2 minutes agoPausedcache
shop-cache-1valkey:9PausedOpen in the browser :6379→6379:5353→53/udpStoppedmail
shop-mail-1axllent/mailpitStoppedHigh contrast theme, ContainerRow
Runningweb
shop-web-1Healthynode:26-alpineUp 3 hoursOpen in the browser :5173→51732.4% · 128 MiB (3.1%)Unhealthydb
shop-db-1Unhealthypostgres:18Up 12 minutesOpen in the browser :5432→5432Crashedworker
shop-worker-1Exit code 137registry.example.com/shop/worker-with-a-long-image-name:2026.11.08-build.4512Exited 2 minutes agoPausedcache
shop-cache-1valkey:9PausedOpen in the browser :6379→6379:5353→53/udpStoppedmail
shop-mail-1axllent/mailpitStoppedUsageLink to the section Usage
import { ContainerRow } from '@skribly/ui/ops'<List aria-label={t('docker.containers')} items={containers} emptyState={t('docker.none')}>
{(c) => (
<ContainerRow id={c.id} name={c.service} secondaryName={c.name} state={runState(c)}
stateLabel={t(`docker.state.${runState(c)}`)} image={c.image} status={c.status}
ports={c.ports.map((p) => ({ host: p.hostPort, container: p.containerPort, href: url(p), label: t('docker.openPort') }))}
isBusy={busy === c.id} busyLabel={t('docker.working', { name: c.service })}
actions={<IconButton label={t('docker.restart', { name: c.service })} size="sm" onPress={() => restart(c)}><RotateCw aria-hidden /></IconButton>} />
)}
</List>Every label is a prop, so the app passes its own words in its own language. See no built-in text.
AccessibilityLink to the section Accessibility
- The list is one tab stop; arrow keys move between containers and Right reaches the links and buttons in a row.
- The state is a shaped glyph and a word, never colour alone.
- Port links start their name with what is seen.
- A busy row shows a named spinner and its buttons wait.
- Actions wrap under the text on a narrow row, and every button clears 24px.
This demo runs through axe in all three themes on every test run. See what the tests check.