Documentation
@prop
Define component properties for reusable UI.
Basic Usage
// UserCard.wh
@prop val name: String
@prop val email: String
<Card class="p-16">
<Text fontSize={20}>{name}</Text>
<Text color="#999">{email}</Text>
</Card> Use the component:
<UserCard name="Alice" email="alice@example.com" /> Default Values
@prop val name: String
@prop val age: Int = 18
@prop val verified: Boolean = false
<Text>{name} ({age}) {verified ? "✓" : ""}</Text> Optional Props
@prop val title: String
@prop val subtitle: String? = null
<Column>
<Text>{title}</Text>
@if (subtitle != null) {
<Text color="#999">{subtitle}</Text>
}
</Column> Complex Types
data class User(val id: String, val name: String)
@prop val user: User
@prop val users: List<User>
@prop val onUserClick: (User) -> Unit
<LazyColumn>
@for (user in users) {
<Card onClick={() => onUserClick(user)}>
<Text>{user.name}</Text>
</Card>
}
</LazyColumn> Callback Props
@prop val onClick: () -> Unit
@prop val onSubmit: (String) -> Unit
var text = ""
<Column>
<TextField bind:value={text} />
<Button onClick={() => onSubmit(text)} text="Submit" />
</Column> Usage:
<MyForm
onSubmit={value => println("Submitted: $value")}
/> Behind the Scenes
Whitehall transpiles @prop to Composable function parameters:
@prop val name: String
@prop val age: Int = 18
// Becomes:
@Composable
fun MyComponent(name: String, age: Int = 18) {
// ...
} Nested Components
// ProfileCard.wh
@prop val user: User
@prop val showActions: Boolean = true
<Card class="p-16">
<Row class="gap-12">
<Image src={user.avatar} w={48} h={48} />
<Column>
<Text fontWeight="bold">{user.name}</Text>
<Text color="#999">{user.email}</Text>
</Column>
</Row>
@if (showActions) {
<Button text="View Profile" />
}
</Card>
// Usage
@for (user in users) {
<ProfileCard user={user} showActions={true} />
} Component Composition
// Button.wh
@prop val text: String
@prop val icon: String? = null
@prop val onClick: () -> Unit
<Button onClick={onClick}>
<Row class="gap-8">
@if (icon != null) {
<Icon name={icon} />
}
<Text>{text}</Text>
</Row>
</Button>
// IconButton.wh
@prop val icon: String
@prop val label: String
@prop val onClick: () -> Unit
<MyButton icon={icon} text={label} onClick={onClick} /> State in Components
Components can have internal state:
@prop val initialCount: Int = 0
@prop val onChange: (Int) -> Unit = {}
var count = initialCount
<Row class="gap-8">
<Button onClick={() => {
count--
onChange(count)
}} text="-" />
<Text>{count}</Text>
<Button onClick={() => {
count++
onChange(count)
}} text="+" />
</Row> Case Insensitive
All annotation styles work:
@prop val name: String
@Prop val name: String
@PROP val name: String Use @prop to create reusable components. Props are compile-time safe and generate clean Kotlin code.
Complete Example
// ShowCard.wh
data class Show(val id: String, val name: String, val poster: String)
@prop val show: Show
@prop val onCardClick: (String) -> Unit
<Card
onClick={() => onCardClick(show.id)}
p={16}
class="rounded-lg"
>
<Image
src={show.poster}
w={120}
h={180}
fit="cover"
class="rounded"
/>
<Text
fontSize={16}
fontWeight="bold"
class="mt-2"
>
{show.name}
</Text>
</Card>
// Usage
val shows = [...]
<FlowRow gap={16}>
@for (show in shows) {
<ShowCard
show={show}
onCardClick={id => $navigate("/show/$id")}
/>
}
</FlowRow>