Button
Pressable with variant, size and width, an icon slot, and a press that scales instead of fading.


npx @eoria/cli add buttonUsage
import { Button } from '@/components/ui/button'import { Plus } from 'lucide-react-native'
<Button width="full" onPress={save}>Save changes</Button><Button variant="secondary" icon={<Plus />}>Add item</Button><Button variant="outline" size="sm">Edit</Button><Button variant="link" size="sm">Forgot?</Button><Button size="icon" icon={<Plus />} accessibilityLabel="Add" />Variants
| Prop | Values | Default |
|---|---|---|
variant |
default, secondary, outline, ghost, destructive, link |
default |
size |
sm 40pt, md 52pt, lg 56pt, icon 48pt circle |
md |
width |
auto, full |
auto |
Props
| Prop | Type |
|---|---|
icon |
element accepting size and color |
iconPosition |
left, right |
loading |
boolean. A spinner replaces the icon, presses are ignored |
styles |
overrides for label, icon, rootPressed, rootDisabled |
disabled |
boolean, also sets accessibility state |
style is not accepted. Use styles={{ root: … }} so overrides go through the recipe.
loading is for a press that starts work, such as a form submit. The button keeps its colours, which disabled does not, and a screen reader hears it as busy. It uses the platform spinner, so Button needs no extra package.
<Button loading={saving} onPress={save}> Save changes</Button>Press feedback
On press the button scales to 0.97 over 90 ms and a tint layer appears over the fill. The tint is the foreground colour at 8% opacity, or the label colour at 16% on filled variants, so it reads on every theme and preset.
Deriving a button
createButton(recipe) builds a new component from any recipe that keeps the label, icon, rootPressed and rootDisabled slots. Run npx @eoria/cli extend button checkout-button to generate the local extension, then fill in its recipe.
import { extendSlotRecipe } from '@eoria/core'import { buttonRecipe, createButton } from '@/components/ui/button'
export const checkoutButtonRecipe = extendSlotRecipe(buttonRecipe, (theme) => ({ slots: { root: { borderRadius: theme.radius.full } }, variants: { variant: { brand: { root: { backgroundColor: '#7c3aed' } } } }, defaultVariants: { variant: 'brand', size: 'lg' },}))export const CheckoutButton = createButton(checkoutButtonRecipe)The icon adapter reads width and color from the icon slot and passes them as size and color, so it works with lucide and anything with the same props.