createRecipe
createRecipe is a typed variant + state style resolver. It's the same system Reactnatively uses internally for all its components. Use it to build your own components that support variants, sizes, and interactive states — fully type-safe.
Concept
A recipe defines a component's style in terms of:
baseStyles shared by all instances, always applied.variantsNamed style groups (e.g. solid, outline, ghost). Only one applies at a time.sizesScale modifiers (e.g. sm, md, lg). Only one applies at a time.statesModifier styles for interactive states: pressed, hovered, focused, disabled, loading, invalid.defaultsFallback variant, size, and material when none is provided.Basic usage
tagRecipe.ts
1import { createRecipe } from 'reactnatively/theme';23export type TagVariant = 'filled' | 'outline' | 'ghost';4export type TagSize = 'sm' | 'md' | 'lg';56export const tagRecipe = createRecipe<TagVariant, TagSize>({7 base: {8 borderRadius: 6,9 alignItems: 'center',10 justifyContent: 'center',11 flexDirection: 'row',12 gap: 4,13 },14 variants: {15 filled: { backgroundColor: '#6366f1' },16 outline: { borderWidth: 1, borderColor: '#6366f1' },17 ghost: { backgroundColor: 'rgba(99,102,241,0.12)' },18 },19 sizes: {20 sm: { paddingHorizontal: 8, paddingVertical: 3, fontSize: 11 },21 md: { paddingHorizontal: 12, paddingVertical: 5, fontSize: 13 },22 lg: { paddingHorizontal: 16, paddingVertical: 7, fontSize: 15 },23 },24 states: {25 disabled: { opacity: 0.4 },26 pressed: { opacity: 0.75 },27 },28 defaults: {29 variant: 'filled',30 size: 'md',31 },32});Resolving the recipe
Tag.tsx
1import { tagRecipe, type TagVariant, type TagSize } from './tagRecipe';23interface TagProps {4 variant?: TagVariant;5 size?: TagSize;6 disabled?: boolean;7 children: React.ReactNode;8}910export function Tag({ variant, size, disabled, children }: TagProps) {11 const resolved = tagRecipe({12 variant,13 size,14 states: disabled ? ['disabled'] : [],15 });1617 return (18 <Pressable style={resolved.style}>19 {children}20 </Pressable>21 );22}extendRecipe
Extend an existing recipe without rewriting it — useful for brand-specific overrides on top of Reactnatively's built-in recipes:
typescript
import { extendRecipe } from 'reactnatively/theme';
import { tagRecipe } from './tagRecipe';
// Override just the filled variant colors
export const brandTagRecipe = extendRecipe(tagRecipe, {
variants: {
filled: { backgroundColor: '#e11d48' }, // brand red
},
});RecipeState values
| State | Typical trigger |
|---|---|
| base | Always applied (same as base style block) |
| pressed | onPressIn active |
| hovered | Pointer/hover active (web / pointer device) |
| focused | Keyboard focus active |
| selected | Controlled selected state |
| disabled | disabled prop is true |
| loading | loading prop is true |
| invalid | Form validation error |