Button
Six intents × three sizes (plus the icon square) × disabled / focus / hover states. Always semantic: the intent communicates what the button does (default = the primary action, destructive = irreversible, ghost = chrome).
On the web every intent is a pill: the call to action is the green action pill, secondary a violet outline, outline a hairline, ghost and link bare. A primary button rises 1 px under the pointer over 150 ms and settles back when it leaves, the outline looks take a hover wash at once, and a link dims; a disabled or loading button stays put, and Reduce Motion makes the lift instant. A disabled web button goes transparent with a hairline and a muted label rather than fading. On iOS and Android the buttons keep their platform shapes in the theme's colours.
Pass ref to access the interactive Pressable, including link buttons. Use useRef<ComponentRef<typeof Button>>(null) from React, or useRef<View>(null) with React Native's View type. Object and callback refs are supported and detach on unmount. Calling ref.current?.focus() or .blur() delegates to the host without activating the control. Browser focus is supported; native focus depends on the platform and React Native version, and is separate from accessibility focus.
iOS
Android
Web
tsx
Default
Outline
Secondary
Ghost
Destructive
Link
Small
Large
Icon only
Disabled
Loading
Raised
Block
Measure
With icon
The variant only changes how a button looks; every one of them fires onPress the same way.
A button's whole job is to fire onPress. Wire it to your own handler and every press runs it.
Props
Do & Don’t
Default (primary)
Don’t
Multiple primaries compete; nothing stands out.
Do
One clear primary action; everything else is supporting.
Outline
Don’t
All-outline leaves no signal which action is primary.
Do
Promote the main action to default; keep the rest outline.
Secondary
Don’t
A secondary button as the main call to action under-sells it.
Do
Default for the primary action; secondary for the next one down.
Ghost
Don’t
A ghost button is too quiet to carry the primary action.
Do
Use ghost for tertiary and toolbar actions; keep the CTA filled.
Destructive
Don’t
Red on a safe action cries wolf; users learn to ignore it.
Do
Reserve the destructive variant for irreversible actions like delete.
Link
Don’t
A link-styled submit doesn't look pressable and gets lost.
Do
Link variant for inline navigation; a filled button for the submit.
Real links (href)
A button that NAVIGATES should be a real link, not a press handler that sets location. Pass href and the web render becomes a genuine browser link: middle-click, cmd-click, and open-in-new-tab all work, crawlers see the destination, and assistive tech hears a link. Native platforms have no anchors, so pair href with an onPress that runs the same navigation through your router; while disabled or loading the anchor is suppressed exactly like onPress. hrefAttrs carries the anchor attributes react-native-web forwards (target, rel, download).
Touch area
On iOS and Android a Button smaller than the platform's minimum touch target (44pt, 48dp) keeps its size and extends only its touch area to the minimum, from its first frame, so nothing moves. React Native bounds that area by two rules of its own. It never reaches past a native container that does not contain it: a container of yours that clips (overflow hidden, a scroll view's edge) stops it at that edge. A container that paints, carries a testID or handles pointer events admits it, because such a container records the touch area at its layout and the Button has it from its first one. Every kit control does the same for itself; a control the kit lacks belongs in the kit, never in a Pressable of your own (see DESIGN.md, Shapes). And where two touch areas overlap, the later sibling takes the tap: on Android two small icon Buttons 8 apart in a Row both reach across the gap between them, so a tap anywhere in it presses the second. Leave at least twice the extra touch area between small Buttons you place side by side (16 between two small icon Buttons on Android, 8 on iOS).