Documentation

Button Component

Interactive buttons with Material Design styling and variants.

Basic Usage

<Button onClick={handleClick} text="Click Me" />

Button Text

Use the text prop for simple text buttons:

<Button text="Save" onClick={save} />

Or use children for more control:

<Button onClick={save}>
  <Text>Save</Text>
</Button>

Button with Icon

<Button onClick={add}>
  <Icon name="add" />
  <Text>Add Item</Text>
</Button>

<Button onClick={save}>
  <Text>Save</Text>
  <Icon name="check" />
</Button>

Icon-Only Button

<Button onClick={handleClick}>
  <Icon name="favorite" />
</Button>

Enabled State

var email = ""
var password = ""
val isValid = email.isNotEmpty() && password.length >= 8

<Button enabled={isValid} onClick={submit} text="Sign In" />

Loading State

var isLoading = false

suspend fun submit() {
  isLoading = true
  $fetch.post(url = "...", body = mapOf(...))
  isLoading = false
}

<Button onClick={submit} enabled={!isLoading}>
  @if (isLoading) {
    <CircularProgressIndicator size={20} />
  } else {
    <Text>Submit</Text>
  }
</Button>

Full Width

<Button text="Continue" fillMaxWidth onClick={next} />

Styling

Colors

<Button text="Primary" />
<Button text="Secondary" background="secondary" />
<Button text="Error" background="error" />

Padding

<Button text="Small" px={12} py={6} />
<Button text="Large" px={24} py={16} />

Corner Radius

<Button text="Rounded" cornerRadius={8} />
<Button text="Pill" cornerRadius={100} />

Common Patterns

Primary/Secondary Actions

<Row gap={12}>
  <Button onClick={cancel} text="Cancel" background="secondary" />
  <Button onClick={save} text="Save" />
</Row>

Bottom Sheet Actions

<Column fillMaxSize>
  <Column weight={1} scrollable>
    <Text>Content</Text>
  </Column>

  <Row safeBottom gap={12} p={16}>
    <Button onClick={cancel} text="Cancel" weight={1} />
    <Button onClick={confirm} text="Confirm" weight={1} />
  </Row>
</Column>

Floating Action Button

<Box fillMaxSize>
  <LazyColumn>
    <!-- Content -->
  </LazyColumn>

  <Button
    onClick={add}
    background="primary"
    cornerRadius={28}
    w={56} h={56}
    position="absolute"
    bottom={16}
    right={16}
  >
    <Icon name="add" color="#FFF" />
  </Button>
</Box>

Action Bar

<Row gap={8} p={8} background="#F5F5F5">
  <Button onClick={bold}>
    <Icon name="format-bold" />
  </Button>
  <Button onClick={italic}>
    <Icon name="format-italic" />
  </Button>
  <Button onClick={underline}>
    <Icon name="format-underlined" />
  </Button>
</Row>

Segmented Button

var view = "grid"

<Row gap={0} background="#F5F5F5" cornerRadius={8}>
  <Button
    onClick={() => view = "grid"}
    background={if (view == "grid") "primary" else "transparent"}
  >
    <Icon name="grid-view" />
  </Button>
  <Button
    onClick={() => view = "list"}
    background={if (view == "list") "primary" else "transparent"}
  >
    <Icon name="list" />
  </Button>
</Row>

Chip Button

<Row gap={8} wrap>
  @for (tag in tags) {
    <Button
      onClick={() => selectTag(tag)}
      background={if (selectedTags.contains(tag)) "primary" else "surface"}
      cornerRadius={16}
      px={16} py={8}
    >
      <Text fontSize={14}>{tag}</Text>
    </Button>
  }
</Row>

Universal onClick

In Whitehall, onClick works on ANY component, not just Button. This lets you make images, text, cards, and containers clickable:

<Image src={poster} onClick={() => $navigate("/detail")} />

<Text color="primary" onClick={handleClick}>
  Click me
</Text>

<Card onClick={() => $navigate("/product/{id}")}>
  <Image src={image} />
  <Text>{name}</Text>
</Card>

Async Actions

Button click handlers can be async (suspend functions):

suspend fun loadData() {
  val data = $fetch("https://api.example.com/data")
  items = data
}

<Button onClick={loadData} text="Load Data" />

Navigation

<Button onClick={() => $navigate("/profile")} text="View Profile" />

<Button onClick={() => $navigate("/product/{productId}")} text="Details" />

Prop Reference

PropTypeDescription
textStringButton label
onClickFunctionClick handler
enabledBooleanEnable/disable button
backgroundStringBackground color
fillMaxWidthBooleanFull width button
px/pyNumberPadding
cornerRadiusNumberBorder radius in dp

See Also