Skip to content
kerfjsGitHub

Spacer

Takes up the free space in a Row or Stack, so what follows sits at the far end.

SourcePackage @kerf-js/veneer

Preview

tsx
import { Inset, Row, Spacer } from '@kerf-js/veneer'export function InRow() {  return (  <Inset>    <Row gap="tight">      <Box>Left</Box>      <Box>Left</Box>      <Spacer />      <Box>Pushed to the end</Box>    </Row>  </Inset>  )}type Status =  | 'Live'  | 'Building'  | 'Failed'  | 'Paused'const PROJECTS: {  id: string  name: string  owner: string  region: string  status: Status  deploys: number  uptime: number  latency: number  cost: number  updated: string}[] = [  {    id: 'storefront',    name: 'storefront',    owner: 'Priya Raman',    region: 'eu-west',    status: 'Live',    deploys: 412,    uptime: 99.98,    latency: 84,    cost: 1240,    updated: '2026-10-31',  },  {    id: 'checkout',    name: 'checkout',    owner: 'Tomás Oliveira',    region: 'eu-west',    status: 'Live',    deploys: 288,    uptime: 99.95,    latency: 112,    cost: 980,    updated: '2026-10-31',  },  {    id: 'search-api',    name: 'search-api',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Building',    deploys: 196,    uptime: 99.91,    latency: 146,    cost: 1610,    updated: '2026-10-30',  },  {    id: 'billing',    name: 'billing',    owner: 'Mara Lindqvist',    region: 'us-east',    status: 'Live',    deploys: 154,    uptime: 99.99,    latency: 73,    cost: 720,    updated: '2026-10-29',  },  {    id: 'mobile-bff',    name: 'mobile-bff',    owner: 'Sam Okafor',    region: 'ap-south',    status: 'Failed',    deploys: 233,    uptime: 99.42,    latency: 208,    cost: 860,    updated: '2026-10-29',  },  {    id: 'notifier',    name: 'notifier',    owner: 'Jun Tanaka',    region: 'eu-north',    status: 'Live',    deploys: 97,    uptime: 99.97,    latency: 58,    cost: 310,    updated: '2026-10-27',  },  {    id: 'reports',    name: 'reports',    owner: 'Noor Haddad',    region: 'us-west',    status: 'Paused',    deploys: 41,    uptime: 99.8,    latency: 321,    cost: 190,    updated: '2026-10-22',  },  {    id: 'edge-cache',    name: 'edge-cache',    owner: 'Jun Tanaka',    region: 'global',    status: 'Live',    deploys: 318,    uptime: 100,    latency: 21,    cost: 1430,    updated: '2026-10-31',  },  {    id: 'auth',    name: 'auth',    owner: 'Mara Lindqvist',    region: 'eu-west',    status: 'Live',    deploys: 176,    uptime: 99.99,    latency: 66,    cost: 540,    updated: '2026-10-28',  },  {    id: 'ingest',    name: 'ingest',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Live',    deploys: 264,    uptime: 99.93,    latency: 131,    cost: 2120,    updated: '2026-10-30',  },]const min = 60_000const BOX =  'display:grid;align-content:center;' +  'gap:2px;' +  'padding:var(--veneer-space-related);' +  'border:var(--veneer-border-hairline) ' +  'solid var(--veneer-color-border);' +  'border-radius:' +  'var(--veneer-radius-surface);' +  'background:var(--veneer-color-surface);' +  'color:var(--veneer-color-text);' +  'font-size:var(--veneer-text-caption-size);' +  'min-inline-size:0'const projectOf = (c: unknown) => {  const n = Number(typeof c === 'function' ? c() : c)  return Number.isInteger(n) && n > 0    ? PROJECTS[(n - 1) % PROJECTS.length]    : undefined}function Box({  tall,  children,  ...rest}: {  tall?: number  children?: unknown  [attr: string]: unknown}) {  return (    <div      style={        tall ? `${BOX};min-block-size:${tall}px` : BOX      }      {...rest}    >      {projectOf(children) ? (        <>          <strong>{projectOf(children)!.name}</strong>          <span            style={              'color:var(--veneer-color-text-subtle)'            }          >            {projectOf(children)!.region}          </span>        </>      ) : (        children      )}    </div>  )}

Installation

npm install @kerf-js/veneer

Usage

tsx
import { Spacer } from '@kerf-js/veneer'<Inset>  <Row gap="tight">    <Box>Left</Box>    <Box>Left</Box>    <Spacer />    <Box>Pushed to the end</Box>  </Row></Inset>

In a Stack

A Stack needs a height for there to be free space.

tsx
import { Inset, Spacer, Stack } from '@kerf-js/veneer'export function InStack() {  return (  <Inset>    <Stack      gap="tight"      style="block-size:200px"    >      <Box>Top</Box>      <Spacer />      <Box>Pushed to the bottom</Box>    </Stack>  </Inset>  )}type Status =  | 'Live'  | 'Building'  | 'Failed'  | 'Paused'const PROJECTS: {  id: string  name: string  owner: string  region: string  status: Status  deploys: number  uptime: number  latency: number  cost: number  updated: string}[] = [  {    id: 'storefront',    name: 'storefront',    owner: 'Priya Raman',    region: 'eu-west',    status: 'Live',    deploys: 412,    uptime: 99.98,    latency: 84,    cost: 1240,    updated: '2026-10-31',  },  {    id: 'checkout',    name: 'checkout',    owner: 'Tomás Oliveira',    region: 'eu-west',    status: 'Live',    deploys: 288,    uptime: 99.95,    latency: 112,    cost: 980,    updated: '2026-10-31',  },  {    id: 'search-api',    name: 'search-api',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Building',    deploys: 196,    uptime: 99.91,    latency: 146,    cost: 1610,    updated: '2026-10-30',  },  {    id: 'billing',    name: 'billing',    owner: 'Mara Lindqvist',    region: 'us-east',    status: 'Live',    deploys: 154,    uptime: 99.99,    latency: 73,    cost: 720,    updated: '2026-10-29',  },  {    id: 'mobile-bff',    name: 'mobile-bff',    owner: 'Sam Okafor',    region: 'ap-south',    status: 'Failed',    deploys: 233,    uptime: 99.42,    latency: 208,    cost: 860,    updated: '2026-10-29',  },  {    id: 'notifier',    name: 'notifier',    owner: 'Jun Tanaka',    region: 'eu-north',    status: 'Live',    deploys: 97,    uptime: 99.97,    latency: 58,    cost: 310,    updated: '2026-10-27',  },  {    id: 'reports',    name: 'reports',    owner: 'Noor Haddad',    region: 'us-west',    status: 'Paused',    deploys: 41,    uptime: 99.8,    latency: 321,    cost: 190,    updated: '2026-10-22',  },  {    id: 'edge-cache',    name: 'edge-cache',    owner: 'Jun Tanaka',    region: 'global',    status: 'Live',    deploys: 318,    uptime: 100,    latency: 21,    cost: 1430,    updated: '2026-10-31',  },  {    id: 'auth',    name: 'auth',    owner: 'Mara Lindqvist',    region: 'eu-west',    status: 'Live',    deploys: 176,    uptime: 99.99,    latency: 66,    cost: 540,    updated: '2026-10-28',  },  {    id: 'ingest',    name: 'ingest',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Live',    deploys: 264,    uptime: 99.93,    latency: 131,    cost: 2120,    updated: '2026-10-30',  },]const min = 60_000const BOX =  'display:grid;align-content:center;' +  'gap:2px;' +  'padding:var(--veneer-space-related);' +  'border:var(--veneer-border-hairline) ' +  'solid var(--veneer-color-border);' +  'border-radius:' +  'var(--veneer-radius-surface);' +  'background:var(--veneer-color-surface);' +  'color:var(--veneer-color-text);' +  'font-size:var(--veneer-text-caption-size);' +  'min-inline-size:0'const projectOf = (c: unknown) => {  const n = Number(typeof c === 'function' ? c() : c)  return Number.isInteger(n) && n > 0    ? PROJECTS[(n - 1) % PROJECTS.length]    : undefined}function Box({  tall,  children,  ...rest}: {  tall?: number  children?: unknown  [attr: string]: unknown}) {  return (    <div      style={        tall ? `${BOX};min-block-size:${tall}px` : BOX      }      {...rest}    >      {projectOf(children) ? (        <>          <strong>{projectOf(children)!.name}</strong>          <span            style={              'color:var(--veneer-color-text-subtle)'            }          >            {projectOf(children)!.region}          </span>        </>      ) : (        children      )}    </div>  )}

Two spacers

They share the free space evenly, which centres what's between them.

tsx
import { Inset, Row, Spacer } from '@kerf-js/veneer'export function Two() {  return (  <Inset>    <Row gap="tight">      <Box>Start</Box>      <Spacer />      <Box>Middle</Box>      <Spacer />      <Box>End</Box>    </Row>  </Inset>  )}type Status =  | 'Live'  | 'Building'  | 'Failed'  | 'Paused'const PROJECTS: {  id: string  name: string  owner: string  region: string  status: Status  deploys: number  uptime: number  latency: number  cost: number  updated: string}[] = [  {    id: 'storefront',    name: 'storefront',    owner: 'Priya Raman',    region: 'eu-west',    status: 'Live',    deploys: 412,    uptime: 99.98,    latency: 84,    cost: 1240,    updated: '2026-10-31',  },  {    id: 'checkout',    name: 'checkout',    owner: 'Tomás Oliveira',    region: 'eu-west',    status: 'Live',    deploys: 288,    uptime: 99.95,    latency: 112,    cost: 980,    updated: '2026-10-31',  },  {    id: 'search-api',    name: 'search-api',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Building',    deploys: 196,    uptime: 99.91,    latency: 146,    cost: 1610,    updated: '2026-10-30',  },  {    id: 'billing',    name: 'billing',    owner: 'Mara Lindqvist',    region: 'us-east',    status: 'Live',    deploys: 154,    uptime: 99.99,    latency: 73,    cost: 720,    updated: '2026-10-29',  },  {    id: 'mobile-bff',    name: 'mobile-bff',    owner: 'Sam Okafor',    region: 'ap-south',    status: 'Failed',    deploys: 233,    uptime: 99.42,    latency: 208,    cost: 860,    updated: '2026-10-29',  },  {    id: 'notifier',    name: 'notifier',    owner: 'Jun Tanaka',    region: 'eu-north',    status: 'Live',    deploys: 97,    uptime: 99.97,    latency: 58,    cost: 310,    updated: '2026-10-27',  },  {    id: 'reports',    name: 'reports',    owner: 'Noor Haddad',    region: 'us-west',    status: 'Paused',    deploys: 41,    uptime: 99.8,    latency: 321,    cost: 190,    updated: '2026-10-22',  },  {    id: 'edge-cache',    name: 'edge-cache',    owner: 'Jun Tanaka',    region: 'global',    status: 'Live',    deploys: 318,    uptime: 100,    latency: 21,    cost: 1430,    updated: '2026-10-31',  },  {    id: 'auth',    name: 'auth',    owner: 'Mara Lindqvist',    region: 'eu-west',    status: 'Live',    deploys: 176,    uptime: 99.99,    latency: 66,    cost: 540,    updated: '2026-10-28',  },  {    id: 'ingest',    name: 'ingest',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Live',    deploys: 264,    uptime: 99.93,    latency: 131,    cost: 2120,    updated: '2026-10-30',  },]const min = 60_000const BOX =  'display:grid;align-content:center;' +  'gap:2px;' +  'padding:var(--veneer-space-related);' +  'border:var(--veneer-border-hairline) ' +  'solid var(--veneer-color-border);' +  'border-radius:' +  'var(--veneer-radius-surface);' +  'background:var(--veneer-color-surface);' +  'color:var(--veneer-color-text);' +  'font-size:var(--veneer-text-caption-size);' +  'min-inline-size:0'const projectOf = (c: unknown) => {  const n = Number(typeof c === 'function' ? c() : c)  return Number.isInteger(n) && n > 0    ? PROJECTS[(n - 1) % PROJECTS.length]    : undefined}function Box({  tall,  children,  ...rest}: {  tall?: number  children?: unknown  [attr: string]: unknown}) {  return (    <div      style={        tall ? `${BOX};min-block-size:${tall}px` : BOX      }      {...rest}    >      {projectOf(children) ? (        <>          <strong>{projectOf(children)!.name}</strong>          <span            style={              'color:var(--veneer-color-text-subtle)'            }          >            {projectOf(children)!.region}          </span>        </>      ) : (        children      )}    </div>  )}

A toolbar

Put a Spacer between items in a row to push the ones after it to the far end.

tsx
import { state } from '@kerf-js/core'import {  Button,  Row,  Segmented,  Spacer} from '@kerf-js/veneer'export function Toolbar() {  const view = state('all')  return (    <Row gap="related">      <Segmented        size="sm"        label="Show"        value={view.value}        options={VIEWS}        onChange={(v) => (view.value = v)}      />      <Spacer />      <Button        emphasis="ghost"        tone="neutral"        icon="download"      >        Export      </Button>      <Button icon="plus">New project</Button>    </Row>  )}const VIEWS = [  { value: 'all', label: 'All' },  { value: 'mine', label: 'Mine' },  { value: 'failed', label: 'Failed' },]

Buttons along the bottom

Two cards of different lengths still line up their buttons.

tsx
import {  Button,  Card,  Grid,  Spacer,  Stack,  Text} from '@kerf-js/veneer'export function CardFooter() {  return (  <Grid    min="sm"    cols={2}    gap="related"  >    <Card>      <Stack        gap="related"        style="block-size:100%"      >        <Text role="heading">Pro</Text>        <Text subtle>Up to 20 projects.</Text>        <Spacer />        <Button          emphasis="outline"          tone="neutral"          full        >          Current plan        </Button>      </Stack>    </Card>    <Card>      <Stack        gap="related"        style="block-size:100%"      >        <Text role="heading">Scale</Text>        <Text subtle>          Unlimited projects, private regions, audit logs          and a named contact who answers within the hour.        </Text>        <Spacer />        <Button full>Upgrade</Button>      </Stack>    </Card>  </Grid>  )}

When to use

Use it for

  • A toolbar with items at both ends
  • A card whose button sits at the bottom

Reach for something else

  • Row, when two groups apart is all you need: use `justify="between"`

Accessibility

  • It is an empty box with no role, so screen readers skip it.

API Reference

PropTypeDefaultDescription
childrenunknown—What it contains.

RelatedRowStack