Patterns
48 high-level UX patterns — bottom sheets, onboarding, paywalls, chips, search bars, and more.
Patterns are finished UX flows or UI shapes. They're the largest pieces in @otfdashkit/ui-native —
where you'd otherwise pull in five hooks and a third-party library and tune for two days.
Sheets + dialogs
| Pattern | What it is |
|---|---|
BottomSheet | Bottom-anchored sheet with snap points, drag-to-close, backdrop dim, optional pinned footer CTA |
ActionSheet | iOS-style bottom action picker — title + description + ordered actions + cancel |
ConfirmDialog | Modal confirmation with destructive variant |
OtfDialog | Centered dialog (overrides Tamagui's default styling with kit tokens) |
OtfPopover | Anchored popover with auto-positioning |
OtfSelect | Native select bottom-sheet — long list, search, single/multi |
NotificationBanner | In-app toast banner, dismiss-on-swipe |
<BottomSheet
open={open}
onOpenChange={setOpen}
snapPoints={['33%', '66%']}
>
<H2>Choose a workout</H2>
<List>...</List>
</BottomSheet>Tabs + bars
| Pattern | What it is |
|---|---|
ChipsTabBar | Top-of-screen filter chips that act as tab nav |
TabBar | Bottom tab bar — 5 tabs, icon + label, active accent, hairline top border |
SearchBar | Search input with clear button + cancel transition |
AppHeader | Top header that pairs with useCollapsibleHeader for shrink-on-scroll |
FloatingActionButton | Bottom-right FAB — fitness-kit's theme switcher uses this |
Onboarding + auth
| Pattern | What it is |
|---|---|
OnboardingCarousel | Full-screen swipeable intro with dot indicators |
MultiStep | Wizard orchestrator wrapping StepPageLayout + ProgressSteps |
ProgressSteps | Top dot/bar indicator for wizards |
LoginScreen | Pre-built sign-in page (email + password + OAuth + demo button) — drop-in for kits |
OTPInput | One-time-passcode input row |
PasswordInput | Password input with reveal-toggle eye icon |
Lists + cards
| Pattern | What it is |
|---|---|
Carousel | Horizontal swipeable cards (paginated) |
CardScroller | Horizontal-scroll list of cards |
EventCard | Calendar-event card with date stripe + time + venue |
MediaCard | Cover-image card with title overlay |
ProductCard | E-commerce card — image, name, price, rating |
TestimonialCard | Quote + author + role + avatar |
Chip | Single chip (filter, tag, removable) |
AvatarGroup | Stacked overlapping avatars (+N more) |
Selectable | Tappable card row with checked/selected state |
Expandable | Card row that expands to show more content on tap |
SwipeableRow | Row with reveal actions on swipe (archive / delete / etc.) |
SwipeCards | Tinder-style swipeable card stack |
DataTable | Native data table with sort + scroll |
ChatBubble | Single chat message bubble (mine vs theirs) |
Inputs + pickers
| Pattern | What it is |
|---|---|
DatePicker | iOS/Android-styled date picker bottom sheet |
WheelPicker | iOS-style spinning wheel picker |
RulerScrubber | Horizontal "ruler" picker for height/weight (the fitness kit uses it for body measurements) |
Special-purpose
| Pattern | What it is |
|---|---|
PaywallScreen | Drop-in paywall — feature list, pricing toggle, primary CTA, fine print |
PricingTable | Multi-tier pricing comparison cards |
ProfileHeader | Avatar + name + email + stats — pre-baked profile-screen header |
SettingsScreen | Pre-built settings layout — sections + rows + dividers |
UserPreferences | Toggle group for common app prefs (notifications, units, language) |
ImmersiveMediaScreen | Full-bleed media-first screen layout |
FinanceDashboard | KPI grid + chart + recent-transactions list — finance-kit-shaped |
CountdownBanner | Time-bounded promo banner (red when expired) |
EmptyState | Native empty-state with icon + title + description + CTA |
Skeleton | Loading placeholder (matches the web Skeleton API) |
GlassCard | Frosted-glass card (BlurView-backed) |
AnimatedView | Reanimated wrapper with fade/slide presets |
AnimatedNumber | Count-up number, same props as web (from, duration, decimals, prefix, suffix, format); reduced-motion safe |
PullToRefresh | Pull-to-refresh wrapper with branded spinner |
A small example: BottomSheet + DatePicker
import { BottomSheet, DatePicker, Button, H2 } from '@otfdashkit/ui-native'
const [open, setOpen] = useState(false)
const [date, setDate] = useState<Date | null>(null)
<>
<Button onPress={() => setOpen(true)}>
{date ? date.toLocaleDateString() : 'Pick a date'}
</Button>
<BottomSheet open={open} onOpenChange={setOpen} snapPoints={['50%']}>
<H2>When?</H2>
<DatePicker value={date} onChange={(d) => { setDate(d); setOpen(false) }} />
</BottomSheet>
</>