Composition
Compose Liquidify with native elements, refs, and compound component APIs.
Liquidify components prefer a semantic native root. Pass the documented native props, use the forwarded ref for focus or measurement, and keep application layout outside the component.
asChild is opt-in
asChild is not a universal prop. A component exposes it only when its implementation merges behavior and accessibility onto a single child safely. Today, Button is the primary slottable control:
import { Button } from "@liquidify/react"
export function DocumentationLink() {
return (
<Button asChild variant="glass">
<a href="/docs">Read the documentation</a>
</Button>
)
}In this mode the child owns its element semantics. Supply exactly one element child and verify keyboard behavior after composition. Unsupported components reject asChild at the type level instead of ignoring it.
Refs
Refs point to the rendered native root, or to the merged child when a supported component uses asChild.
import { useRef } from "react"
import { Button } from "@liquidify/react"
export function FocusExample() {
const buttonRef = useRef<HTMLButtonElement>(null)
return <Button ref={buttonRef}>Continue</Button>
}Label typography with labelProps
A component that renders its own text routes that text through the internal Text
primitive. When you need to retype a label — a different variant, weight, or colour —
pass labelProps rather than wrapping the label in your own styled node. It is a
closed, token-backed set of typography axes (variant, weight, color, tint,
tracking, transform, italic, lineHeight, wrap) spread last into that Text,
so your value wins over the component's computed default without restructuring its
anatomy.
import { Toggle } from "@liquidify/react"
export function EmphasisedLabel() {
return (
<Toggle labelProps={{ variant: "headline", color: "secondary" }} defaultChecked>
Wi-Fi
</Toggle>
)
}When you instead pass an already-styled Text, Icon, or element as the label, the
component passes it through untouched — it never double-wraps a non-primitive child.
The onPress convention
Action controls report activation through onPress, a no-argument callback that
fires once per intended activation regardless of whether the user clicked, tapped, or
pressed a key. It is the SwiftUI action: analogue and is distinct from stateful
controls, which report a value through onChange (Toggle a boolean, Slider a
number, TextField and Picker a string).
import { Button } from "@liquidify/react"
export function SaveAction({ onSave }: { onSave: () => void }) {
return (
<Button variant="borderedProminent" onPress={onSave}>
Save
</Button>
)
}Because Button renders a native <button type="button">, use onPress to run the
action directly rather than relying on native form submission.
Compound components
Some APIs expose a root plus named parts or structured item data. Follow each component page's anatomy instead of flattening roles into decorative elements. Stable item identifiers are part of behavioral correctness for menus, actions, tabs, and other collections.