119 themed React Native components for Expo. Built-in light/dark theming, font scaling, and zero native dependencies.
npm install react-native-salt
npm install react react-native @react-native-async-storage/async-storage
Optional (for icons):
npm install @expo/vector-icons
Requirement
Version
React
>= 18.0.0
React Native
>= 0.72.0
Expo
SDK 49+
TypeScript
>= 5.0 (recommended)
Works in Expo Go — no dev build required.
import { SaltProvider , Screen , Stack , Title , Text , Button } from "react-native-salt" ;
export default function App ( ) {
return (
< SaltProvider >
< Screen scroll >
< Stack gap = "lg" style = { { padding : 16 } } >
< Title > Hello</ Title >
< Text > Welcome to Salt.</ Text >
< Button title = "Get Started" onPress = { ( ) => { } } />
</ Stack >
</ Screen >
</ SaltProvider >
) ;
}
Wrap your app root. Manages light/dark mode, font scaling, and persists preference to AsyncStorage.
< SaltProvider
defaultPreference = "system" // "system" | "light" | "dark"
defaultFontLevel = { 16 } // 8–18
>
{ children }
</ SaltProvider >
< SaltProvider customLight = { myLightTheme } customDark = { myDarkTheme } >
{ children }
</ SaltProvider >
Access theme tokens and controls in any component:
import { useTheme } from "react-native-salt" ;
function MyComponent ( ) {
const { theme, mode, preference, setPreference, fontLevel, setFontLevel, isThemeLoaded } = useTheme ( ) ;
const { colors, spacing, radius, fontSizes } = theme ;
return < View style = { { backgroundColor : colors . surface , padding : spacing . md , borderRadius : radius . md } } /> ;
}
spacing: { none: 0, xs: 4, sm: 8, md: 12, lg: 16, xl: 24, xxl: 32 }
radius: { none: 0, sm: 6, md: 10, lg: 14, xl: 20, xxl: 24, pill: 999 }
fontSizes: { xs: 10, sm: 14, md: 16, lg: 18, xl: 20, xxl: 24, "3xl": 32 }
(values shift based on fontLevel 8–18)
Token
Light
Dark
primary
#2563eb
#60a5fa
secondary
#7c3aed
#a78bfa
danger
#dc2626
#f87171
success
#16a34a
#4ade80
warning
#d97706
#fbbf24
info
#0ea5e9
#38bdf8
background
#f8fafc
#0f172a
surface
#ffffff
#1e293b
text
#0f172a
#f8fafc
muted
#64748b
#94a3b8
border
#e2e8f0
#334155
Each intent has an onX counterpart (e.g., onPrimary, onDanger) for text on colored backgrounds.
import type {
Variant , // "solid" | "outline" | "ghost" | "text" | "link"
Intent , // "primary" | "secondary" | "danger" | "success" | "warning" | "info"
SizeToken , // "sm" | "md" | "lg"
TypographySize , // "sm" | "md" | "lg"
Theme , // Full theme object
ThemeMode , // "light" | "dark"
ThemePreference , // "system" | "light" | "dark"
ThemeColors , // All color tokens
SpacingToken , // "none" | "xs" | "sm" | "md" | "lg" | "xl" | "xxl"
RadiusToken , // "none" | "sm" | "md" | "lg" | "xl" | "xxl" | "pill"
FontSizeToken , // "xs" | "sm" | "md" | "lg" | "xl" | "xxl" | "3xl"
ColorToken , // keyof ThemeColors
Elevation , // 0 | 1 | 2 | 3
Decoration , // "underline" | "strikethrough"
LineHeightToken , // "tight" | "normal" | "relaxed"
} from "react-native-salt" ;
Category
Count
Components
Layout
13
Screen, Stack, Row, Column, Spacer, Divider, Card, FormScreen, Gradient, SectionHeader, ListSeparator, PullIndicator, GestureHandle
Typography
5
Display, Title, Text, Label, Caption
Buttons
6
Button, ButtonGroup, FAB, SpeedDial, ActionFooter, ActionSheet
Forms
18
Input, TextArea, FormField, SearchBar, Select, DropdownSelect, Checkbox, Switch, RadioGroup, Slider, RangeSlider, NumericInput, DatePicker, TimePicker, OTPInput, PasswordInput, PhoneInput, CurrencyInput
Tags
3
ChipGroup, TagList, TagInput
Color & Media
5
ColorPalette, ColorPicker, ColorPickerTrigger, MediaPickerRow, Lightbox
Data Display
13
ListItem, InfoRow, KeyValueList, DataTable, ComparisonTable, SortHeader, StatGrid, MetricCard, SummaryCard, Leaderboard, Timeline, CodeBlock, ImageCard
Charts
3
BarChart, PieChart, LineChart
Navigation
7
ScreenHeader, Tabs, SegmentedControl, Breadcrumb, StepIndicator, PaginationBar, Drawer
Feedback
8
Toast, Snackbar, Banner, AlertDialog, Modal, Tooltip, Popover, Confetti
Progress
5
ProgressBar, ProgressRing, Loader, Skeleton, CountdownTimer
Status
4
Badge, StatusDot, StatusTracker, NetworkBanner
User
4
Avatar, AvatarGroup, ProfileHeader, SocialButton
Auth
1
AuthDivider
Chat
2
MessageBubble, TypingIndicator
Notification
3
NotificationItem, OnboardingSlide, PermissionCard
Commerce
4
PriceTag, QuantitySelector, RatingStars, PromoInput
Lists
3
SwipeableRow, DragList, Accordion
Tree
1
TreeView
Error
3
EmptyState, ErrorState, RetryView
Editor
3
FloatingToolbar, CanvasControlPanel, LayerListItem
Theme
3
ThemeSwitcher, FontLevelSwitcher, Icon
Carousel
2
Carousel, BottomSheet
SafeArea wrapper with optional scroll and keyboard avoidance.
< Screen scroll keyboardAware statusBarStyle = "dark" >
{ children }
</ Screen >
Prop
Type
Default
scroll
boolean
false
keyboardAware
boolean
false
statusBarStyle
"light" | "dark" | "default"
—
contentContainerStyle
StyleProp<ViewStyle>
—
Flex container with direction, gap, and optional dividers.
< Stack direction = "vertical" gap = "md" align = "center" divider >
{ children }
</ Stack >
Prop
Type
Default
direction
"horizontal" | "vertical"
"vertical"
gap
Spacing
—
align
ViewStyle["alignItems"]
—
justify
ViewStyle["justifyContent"]
—
wrap
boolean
false
divider
boolean | ReactNode
—
animated
boolean
false
responsive
{ breakpoint, direction, gap }
—
Horizontal flex shorthand.
< Row gap = "sm" align = "center" justify = "space-between" fill >
{ children }
</ Row >
Prop
Type
Default
gap
Spacing
—
align
ViewStyle["alignItems"]
—
justify
ViewStyle["justifyContent"]
—
wrap
boolean
false
fill
boolean
false
Vertical flex shorthand. Props identical to Row.
< Column gap = "md" align = "stretch" >
{ children }
</ Column >
Elevated container with optional press, header, footer, image.
< Card elevation = { 2 } onPress = { handlePress } >
< Text > Content</ Text >
</ Card >
Prop
Type
Default
elevation
Elevation
—
onPress
() => void
—
header
ReactNode
—
footer
ReactNode
—
image
ImageSourcePropType
—
imageHeight
number
—
< Spacer size = "lg" />
< Spacer flex / >
< Spacer horizontal size = "md" />
Prop
Type
Default
size
Spacing | ResponsiveSize
—
horizontal
boolean
false
flex
number | boolean
—
< Divider />
< Divider vertical thickness = { 2 } inset = "md" / >
Prop
Type
Default
vertical
boolean
false
thickness
number
—
color
string
—
inset
Spacing
—
margin
Spacing
—
SectionHeader
< SectionHeader title = "Settings" actionText = "See All" onActionPress = { go } collapsible >
{ children }
</ SectionHeader >
Prop
Type
Default
title
string
required
subtitle
string
—
icon
string
—
actionText
string
—
onActionPress
() => void
—
collapsible
boolean
false
defaultCollapsed
boolean
—
collapsed
boolean
—
onCollapsedChange
(collapsed: boolean) => void
—
sticky
boolean
false
Form wrapper with title, back, steps, footer, loading, and error.
< FormScreen title = "Sign Up" step = { { current : 1 , total : 3 } } bottomActions = { < Button title = "Next" /> } >
< Input label = "Email" />
</ FormScreen >
Prop
Type
Default
title
string
required
subtitle
string
—
onBackPress
() => void
—
bottomActions
ReactNode
—
scrollable
boolean
—
loading
boolean
—
error
string
—
step
{ current, total, labels? }
—
< Gradient colors = { [ "#4A90D9" , "#7c3aed" ] } direction = "diagonal" height = { 200 } >
< Title style = { { color : "#fff" } } > Hero</ Title >
</ Gradient >
Prop
Type
Default
colors
[string, string, ...string[]]
required
direction
"vertical" | "horizontal" | "diagonal"
"vertical"
steps
number
—
height
number
—
borderRadius
number
—
< ListSeparator inset = "lg" />
Prop
Type
Default
inset
Spacing
—
margin
Spacing
—
Pill bar handle for bottom sheets / modals.
< PullIndicator width = { 40 } height = { 4 } />
Prop
Type
Default
width
number
—
height
number
—
color
string
—
< GestureHandle position = "corner" variant = "dot" onDrag = { ( dx , dy ) => { } } />
Prop
Type
Default
position
"top" | "bottom" | "left" | "right" | "corner"
—
variant
"dot" | "bar" | "corner"
—
size
number
—
hitSlop
number
—
onDragStart
() => void
—
onDrag
(dx, dy) => void
—
onDragEnd
(dx, dy) => void
—
All 5 typography components share these props:
{
fontSize ?: "sm" | "md" | "lg" ; // default: "md"
align?: "left" | "center" | "right" ;
lines?: number ;
truncate?: "head" | "middle" | "tail" | "clip" ;
decoration?: "underline" | "strikethrough" ;
// + all React Native TextProps (style, onPress, etc.)
}
Hero text. Weight 700, color: text.
< Display fontSize = "lg" > Welcome</ Display >
Size
Pixels
sm
24
md
32
lg
40
Screen/section headers. Weight 600, color: text. Auto accessibilityRole="header".
< Title fontSize = "md" > Settings</ Title >
Size
Pixels
sm
18
md
20
lg
24
Body text. Weight 400, color: text.
< Text fontSize = "md" lineHeight = "relaxed" > Paragraph text here.</ Text >
Size
Pixels
sm
14
md
16
lg
18
Extra prop: lineHeight?: "tight" | "normal" | "relaxed" (default: "normal")
Form labels, chips, tags. Weight 500, color: text.
< Label fontSize = "sm" uppercase > Category</ Label >
Size
Pixels
sm
12
md
14
lg
16
Extra prop: uppercase?: boolean
Timestamps, footnotes, helper text. Weight 400, color: muted.
< Caption > 2 hours ago</ Caption >
Size
Pixels
sm
10
md
12
lg
14
< Button title = "Save" variant = "solid" intent = "primary" size = "md" onPress = { save } />
< Button title = "Delete" variant = "outline" intent = "danger" / >
< Button icon = "add" intent = "primary" /> { /* icon-only */ }
< Button title = "Next" iconRight = "arrow-forward" /> { /* with trailing icon */ }
< Button title = "Saving..." loading disabled />
Prop
Type
Default
title
string
—
variant
Variant
"solid"
intent
Intent
"primary"
size
SizeToken
"md"
onPress
() => void
—
disabled
boolean
false
loading
boolean
false
fullWidth
boolean
false
iconLeft
string
—
iconRight
string
—
icon
string
— (icon-only mode)
textStyle
StyleProp<TextStyle>
—
< ButtonGroup
items = { [ { key : "left" , label : "Left" } , { key : "center" , icon : "grid-outline" } ] }
selected = "left"
onSelect = { setAlign }
intent = "primary"
/>
Prop
Type
Default
items
ButtonItem[]
required
selected
string | string[]
required
onSelect
(key: string) => void
required
multiple
boolean
false
intent
Intent
—
size
SizeToken
—
fullWidth
boolean
false
ButtonItem: { key, label?, icon?, disabled? }
< FAB icon = "add" onPress = { create } intent = "primary" position = "bottom-right" />
Prop
Type
Default
icon
string
—
label
string
—
onPress
() => void
required
intent
Intent
—
size
SizeToken
—
position
"bottom-right" | "bottom-left" | "bottom-center"
"bottom-right"
disabled
boolean
false
< SpeedDial
icon = "add"
actions = { [
{ key : "photo" , icon : "camera-outline" , label : "Photo" , onPress : takePhoto } ,
{ key : "file" , icon : "document-outline" , label : "File" , onPress : pickFile } ,
] }
intent = "primary"
/>
Prop
Type
Default
actions
SpeedDialAction[]
required
icon
string
—
openIcon
string
—
intent
Intent
—
position
"bottom-right" | "bottom-left" | "bottom-center"
—
SpeedDialAction: { key, icon, label?, onPress, color? }
ActionFooter
Sticky bottom bar.
< ActionFooter >
< Button title = "Cancel" variant = "outline" />
< Button title = "Save" />
</ ActionFooter >
< ActionSheet
visible = { open }
onClose = { ( ) => setOpen ( false ) }
title = "Options"
options = { [
{ label : "Edit" , onPress : edit } ,
{ label : "Delete" , onPress : del , destructive : true } ,
] }
/>
Prop
Type
Default
visible
boolean
required
onClose
() => void
required
title
string
—
options
ActionOption[]
required
ActionOption: { label, onPress, destructive? }
< Input label = "Email" placeholder = "you@example.com" size = "md" error = { errors . email } required />
Prop
Type
Default
label
string
—
error
string
—
size
Size
—
fullWidth
boolean
—
required
boolean
—
containerStyle
StyleProp<ViewStyle>
—
+ all TextInputProps
Wraps Input or custom content with label, helper text, and error.
< FormField mode = "input" label = "Name" helperText = "As on your ID" required />
< FormField mode = "custom" label = "Color" error = { err} >
< ColorPalette . . . / >
</ FormField >
< SearchBar value = { query } onChangeText = { setQuery } placeholder = "Search..." size = "md" clearable />
Prop
Type
Default
value
string
required
onChangeText
(text) => void
required
placeholder
string
—
size
"sm" | "md" | "lg"
—
showIcon
boolean
—
clearable
boolean
—
onClear
() => void
—
autoFocus
boolean
—
< TextArea label = "Bio" maxLength = { 200 } showCount rows = { 4 } required />
Prop
Type
Default
label
string
—
error
string
—
maxLength
number
—
showCount
boolean
—
rows
number
—
fullWidth
boolean
—
required
boolean
—
< PasswordInput label = "Password" showStrength strength = "strong" required />
Prop
Type
Default
label
string
—
error
string
—
size
Size
—
fullWidth
boolean
—
showStrength
boolean
—
strength
"weak" | "fair" | "good" | "strong"
—
required
boolean
—
< OTPInput length = { 6 } value = { otp } onChange = { setOtp } autoFocus size = "md" />
Prop
Type
Default
length
number
—
value
string
required
onChange
(code) => void
required
autoFocus
boolean
—
secure
boolean
—
error
string
—
disabled
boolean
—
size
"sm" | "md" | "lg"
—
Modal picker with optional search.
< Select
options = { [ { key : "us" , label : "United States" } , { key : "uk" , label : "United Kingdom" } ] }
value = { country }
onChange = { setCountry }
placeholder = "Select country"
label = "Country"
searchable
/>
Prop
Type
Default
options
SelectOption[]
required
value
string | null
required
onChange
(key) => void
required
placeholder
string
—
label
string
—
error
string
—
disabled
boolean
—
searchable
boolean
—
size
Size
—
fullWidth
boolean
—
SelectOption: { key, label, icon?, disabled? }
Inline dropdown (no modal).
< DropdownSelect options = { options } value = { selected } onChange = { setSelected } label = "Category" maxVisible = { 5 } />
Prop
Type
Default
options
DropdownOption[]
required
value
string | null
required
onChange
(key) => void
required
placeholder
string
—
label
string
—
error
string
—
disabled
boolean
—
size
SizeToken
—
fullWidth
boolean
—
maxVisible
number
—
DropdownOption: { key, label, icon?, description?, disabled? }
< Switch value = { enabled } onValueChange = { setEnabled } size = "md" />
Prop
Type
Default
value
boolean
required
onValueChange
(value) => void
required
size
SizeToken
—
disabled
boolean
—
< Checkbox checked = { agreed } onToggle = { setAgreed } label = "I agree to terms" size = "md" />
Prop
Type
Default
checked
boolean
required
onToggle
(checked) => void
required
label
string
—
description
string
—
size
SizeToken
—
disabled
boolean
—
< RadioGroup
items = { [ { key : "s" , label : "Small" } , { key : "m" , label : "Medium" } , { key : "l" , label : "Large" } ] }
selected = { size }
onSelect = { setSize }
/>
Prop
Type
Default
items
RadioItem[]
required
selected
string | null
required
onSelect
(key) => void
required
RadioItem: { key, label, description? }
< Slider value = { 50 } onValueChange = { setValue } min = { 0 } max = { 100 } step = { 1 } showValue />
Prop
Type
Default
value
number
required
onValueChange
(value) => void
required
min
number
0
max
number
100
step
number
—
size
SizeToken
—
disabled
boolean
—
showValue
boolean
—
< RangeSlider low = { 20 } high = { 80 } onChangeRange = { ( l , h ) => { } } min = { 0 } max = { 100 } showValues />
Prop
Type
Default
low
number
required
high
number
required
onChangeRange
(low, high) => void
required
min
number
0
max
number
100
step
number
—
minGap
number
—
showValues
boolean
—
formatValue
(value) => string
—
intent
Intent
—
disabled
boolean
—
< DatePicker value = { date } onChange = { setDate } minDate = { new Date ( ) } />
Prop
Type
Default
value
Date
—
onChange
(date) => void
required
minDate
Date
—
maxDate
Date
—
< TimePicker value = { { hours : 14 , minutes : 30 } } onChange = { setTime } format = "12h" minuteStep = { 5 } />
Prop
Type
Default
value
{ hours, minutes }
—
onChange
(time) => void
required
format
"12h" | "24h"
—
minuteStep
number
—
< NumericInput value = { 42 } onChange = { setVal } min = { 0 } max = { 999 } step = { 1 } prefix = "$" />
Prop
Type
Default
value
number
required
onChange
(value) => void
required
min
number
—
max
number
—
step
number
—
label
string
—
error
string
—
disabled
boolean
—
size
SizeToken
—
fullWidth
boolean
—
prefix
string
—
suffix
string
—
< CurrencyInput value = { 29.99 } onChange = { setPrice } currency = "$" decimals = { 2 } label = "Price" />
Prop
Type
Default
value
number
required
onChange
(value) => void
required
currency
string
"$"
decimals
number
—
label
string
—
error
string
—
disabled
boolean
—
required
boolean
—
size
Size
—
fullWidth
boolean
—
min
number
—
max
number
—
< PhoneInput value = { phone } onChangeText = { setPhone } countryCode = "US" label = "Phone" required />
Prop
Type
Default
value
string
required
onChangeText
(text) => void
required
countryCode
string
—
onChangeCountry
(country) => void
—
label
string
—
error
string
—
disabled
boolean
—
required
boolean
—
fullWidth
boolean
—
Single-select filter chips with optional "All" chip.
< ChipGroup
items = { [ { key : "new" , label : "New" } , { key : "sale" , label : "Sale" } ] }
selected = { filter }
onSelect = { setFilter }
showAll
/>
Prop
Type
Default
items
ChipItem[]
required
selected
string | null
required
onSelect
(key | null) => void
required
showAll
boolean
—
allLabel
string
"All"
size
SizeToken
—
ChipItem: { key, label }
< TagInput tags = { tags } onChangeTags = { setTags } placeholder = "Add tag..." maxTags = { 10 } suggestions = { [ "react" , "expo" ] } />
Prop
Type
Default
tags
string[]
required
onChangeTags
(tags) => void
required
placeholder
string
—
label
string
—
error
string
—
maxTags
number
—
disabled
boolean
—
suggestions
string[]
—
fullWidth
boolean
—
< TagList tags = { [ { key : "1" , label : "React" , intent : "primary" } ] } onRemove = { removeTag } onAdd = { addTag } />
Prop
Type
Default
tags
TagItem[]
required
onRemove
(key) => void
—
onPress
(key) => void
—
onAdd
() => void
—
addLabel
string
—
size
SizeToken
—
TagItem: { key, label, color?, intent? }
< ColorPalette colors = { [ "#f00" , "#0f0" , "#00f" ] } value = { color } onChange = { setColor } columns = { 6 } showHex />
Prop
Type
Default
colors
string[]
required
value
string | null
required
onChange
(color) => void
required
columns
number
—
swatchSize
number
—
label
string
—
showHex
boolean
—
disabled
boolean
—
< ColorPicker color = "#2563eb" onColorChange = { setColor } showAlpha presets = { [ "#f00" , "#0f0" ] } />
Prop
Type
Default
color
string
—
onColorChange
(color) => void
—
showAlpha
boolean
—
alpha
number
—
onAlphaChange
(alpha) => void
—
colorSpace
"hcl" | "oklch"
—
presets
string[]
—
recentColors
string[]
—
showInput
boolean
—
< ColorPickerTrigger color = "#2563eb" onPress = { openPicker } label = "Fill" showHex size = "md" />
Prop
Type
Default
color
string
required
onPress
() => void
—
label
string
—
showHex
boolean
—
size
"sm" | "md" | "lg"
—
disabled
boolean
—
< Lightbox visible = { open } onClose = { close } source = { { uri : imageUrl } } title = "Photo" />
Prop
Type
Default
visible
boolean
required
onClose
() => void
required
source
ImageSourcePropType
required
title
string
—
description
string
—
< MediaPickerRow items = { media } onAdd = { pickMedia } onRemove = { removeMedia } maxItems = { 5 } />
Prop
Type
Default
items
MediaItem[]
required
onAdd
() => void
—
onRemove
(id) => void
—
onPress
(item) => void
—
maxItems
number
—
size
number
—
label
string
—
disabled
boolean
—
MediaItem: { uri, id }
< ListItem title = "Wi-Fi" subtitle = "Connected" left = { < Icon name = "wifi" /> } right = { < Switch . . . / > } o n P r e s s = { go} / >
Prop
Type
Default
title
string
required
subtitle
string
—
left
ReactNode
—
right
ReactNode
—
onPress
() => void
—
disabled
boolean
—
< ImageCard imageUrl = { url } title = "Mountain" subtitle = "Nepal" badge = "New" imageHeight = { 200 } onPress = { open } />
Prop
Type
Default
imageUrl
string | null
required
title
string
required
subtitle
string
—
badge
string
—
footer
ReactNode
—
onPress
() => void
—
imageHeight
number
—
< InfoRow title = "Status" value = "Active" subtitle = "Since Jan 2024" left = { < StatusDot status = "online" /> } />
Prop
Type
Default
title
string
required
value
string
required
subtitle
string
—
left
ReactNode
—
< MetricCard title = "Revenue" value = "$12,340" trend = "up" trendValue = "+12%" icon = "trending-up" intent = "success" />
Prop
Type
Default
title
string
required
value
string | number
required
subtitle
string
—
trend
"up" | "down" | "neutral"
—
trendValue
string
—
icon
string
—
intent
Intent
—
< SummaryCard label = "Total Orders" value = "1,234" subtitle = "+5% this week" />
Prop
Type
Default
label
string
required
value
string
required
subtitle
string
—
< CodeBlock code = { `const x = 1;` } language = "tsx" showLineNumbers copyable />
Prop
Type
Default
code
string
required
language
string
—
showLineNumbers
boolean
—
copyable
boolean
—
onCopy
(code) => void
—
maxHeight
number
—
Generic typed table with sorting, selection, striping.
< DataTable
columns = { [
{ key : "name" , title : "Name" , flex : 1 } ,
{ key : "age" , title : "Age" , width : 60 , sortable : true } ,
] }
data = { users }
keyExtractor = { ( u ) => u . id }
sortColumn = "age"
sortDirection = "asc"
onSort = { handleSort }
striped
/>
Prop
Type
Default
columns
Column<T>[]
required
data
T[]
required
keyExtractor
(item, index) => string
required
sortColumn
string
—
sortDirection
"asc" | "desc" | null
—
onSort
(column, direction) => void
—
selectedKeys
string[]
—
onSelectRow
(key) => void
—
striped
boolean
—
bordered
boolean
—
compact
boolean
—
emptyMessage
string
—
Column: { key, title, width?, flex?, align?, render?, sortable? }
< KeyValueList items = { [ { key : "1" , label : "Email" , value : "user@test.com" } ] } bordered compact />
Prop
Type
Default
items
KeyValueItem[]
required
bordered
boolean
—
compact
boolean
—
labelWidth
number
—
KeyValueItem: { key, label, value: string | ReactNode, icon?, onPress? }
< StatGrid items = { stats } columns = { 3 } compact bordered />
Prop
Type
Default
items
StatItem[]
required
columns
2 | 3 | 4
—
compact
boolean
—
bordered
boolean
—
StatItem: { key, label, value, icon?, color? }
< ComparisonTable
plans = { [ { key : "free" , name : "Free" } , { key : "pro" , name : "Pro" , highlight : true , badge : "Popular" } ] }
features = { [ { key : "storage" , label : "Storage" , values : { free : "5GB" , pro : "100GB" } } ] }
/>
Prop
Type
Default
plans
ComparisonPlan[]
required
features
ComparisonFeature[]
required
planWidth
number
—
featureLabelWidth
number
—
ComparisonPlan: { key, name, highlight?, badge? }
ComparisonFeature: { key, label, values: Record<string, boolean | string> }
< Leaderboard items = { players } showMedals highlightKey = "currentUser" />
Prop
Type
Default
items
LeaderboardItem[]
required
showMedals
boolean
—
highlightKey
string
—
startRank
number
—
scoreLabel
string
—
LeaderboardItem: { key, name, score, avatar?, subtitle? }
SortHeader
< SortHeader label = "Name" active direction = "asc" onPress = { toggleSort } />
Prop
Type
Default
label
string
required
active
boolean
—
direction
"asc" | "desc" | null
—
onPress
() => void
required
align
"left" | "center" | "right"
—
< Timeline
events = { [
{ key : "1" , title : "Order placed" , timestamp : "10:00 AM" , intent : "success" } ,
{ key : "2" , title : "Shipped" , timestamp : "2:00 PM" , icon : "airplane-outline" } ,
] }
linePosition = "left"
/>
Prop
Type
Default
events
TimelineEvent[]
required
linePosition
"left" | "center"
—
showConnector
boolean
—
TimelineEvent: { key, title, description?, timestamp?, icon?, intent?, content? }
All charts are pure React Native — no SVG dependency.
< BarChart
items = { [ { key : "jan" , label : "Jan" , value : 100 } , { key : "feb" , label : "Feb" , value : 150 } ] }
intent = "primary"
showValues
animated
/>
Prop
Type
Default
items
BarItem[]
required
maxValue
number
—
showValues
boolean
—
showLabels
boolean
—
barHeight
number
—
intent
Intent
—
animated
boolean
—
BarItem: { key, label, value, color?, intent? }
< LineChart data = { points } height = { 200 } showDots showGrid intent = "primary" fillOpacity = { 0.1 } />
Prop
Type
Default
data
DataPoint[]
required
height
number
—
showDots
boolean
—
showLabels
boolean
—
showValues
boolean
—
showGrid
boolean
—
intent
Intent
—
color
string
—
fillOpacity
number
—
DataPoint: { label, value }
< PieChart
slices = { [ { key : "a" , value : 40 , label : "Food" , color : "#f00" } ] }
donut
centerLabel = "Total"
centerValue = "$100"
showLegend
/>
Prop
Type
Default
slices
PieSlice[]
required
size
number
—
donut
boolean
—
donutWidth
number
—
centerLabel
string
—
centerValue
string
—
showLegend
boolean
—
PieSlice: { key, value, label, color }
ScreenHeader
< ScreenHeader
title = "Profile"
subtitle = "Edit your details"
onBack = { goBack }
actions = { [ { icon : "settings-outline" , onPress : openSettings } ] }
/>
Prop
Type
Default
title
string
required
subtitle
string
—
onBack
() => void
—
backIcon
string
—
actions
HeaderAction[]
—
transparent
boolean
—
centerTitle
boolean
—
HeaderAction: { icon, onPress, badge? }
< Tabs
items = { [ { key : "all" , label : "All" } , { key : "active" , label : "Active" , badge : 3 } ] }
selected = { tab }
onSelect = { setTab }
scrollable
/>
Prop
Type
Default
items
TabItem[]
required
selected
string
required
onSelect
(key) => void
required
size
SizeToken
—
scrollable
boolean
—
TabItem: { key, label, badge? }
< SegmentedControl
items = { [ { key : "day" , label : "Day" } , { key : "week" , label : "Week" } ] }
selected = { period }
onSelect = { setPeriod }
fullWidth
/>
Prop
Type
Default
items
SegmentItem[]
required
selected
string
required
onSelect
(key) => void
required
size
SizeToken
—
fullWidth
boolean
—
disabled
boolean
—
SegmentItem: { key, label }
< StepIndicator steps = { [ "Cart" , "Shipping" , "Payment" ] } currentStep = { 1 } />
Prop
Type
Default
steps
string[]
required
currentStep
number
required
< Breadcrumb items = { [ { key : "home" , label : "Home" , onPress : goHome } , { key : "settings" , label : "Settings" } ] } />
Prop
Type
Default
items
BreadcrumbItem[]
required
separator
string
—
separatorIcon
string
—
size
SizeToken
—
BreadcrumbItem: { key, label, icon?, onPress? }
PaginationBar
< PaginationBar currentPage = { 1 } totalPages = { 10 } onPageChange = { setPage } showPageNumbers />
Prop
Type
Default
currentPage
number
required
totalPages
number
required
onPageChange
(page) => void
required
showPageNumbers
boolean
—
maxVisiblePages
number
—
size
"sm" | "md" | "lg"
—
< Drawer
visible = { open }
onClose = { ( ) => setOpen ( false ) }
items = { [ { key : "home" , label : "Home" , icon : "home-outline" , onPress : goHome , active : true } ] }
header = { < ProfileHeader . . . / > }
position = "left"
/>
Prop
Type
Default
visible
boolean
required
onClose
() => void
required
items
DrawerItem[]
—
header
ReactNode
—
footer
ReactNode
—
children
ReactNode
—
position
"left" | "right"
—
width
number
—
DrawerItem: { key, label, icon?, onPress, active?, badge? }
< Modal
visible = { open }
onClose = { close }
title = "Confirm"
message = "Are you sure?"
confirmText = "Yes"
cancelText = "No"
onConfirm = { doIt }
destructive
/>
Prop
Type
Default
visible
boolean
required
onClose
() => void
required
title
string
—
message
string
—
children
ReactNode
—
confirmText
string
—
cancelText
string
—
onConfirm
() => void
—
onCancel
() => void
—
destructive
boolean
—
closable
boolean
—
< AlertDialog
visible = { open }
onClose = { close }
title = "Delete?"
message = "This cannot be undone."
intent = "danger"
icon = "warning-outline"
confirmText = "Delete"
onConfirm = { handleDelete }
destructive
/>
Prop
Type
Default
visible
boolean
required
onClose
() => void
required
title
string
required
message
string
—
icon
string
—
intent
Intent
—
confirmText
string
—
cancelText
string
—
onConfirm
() => void
—
onCancel
() => void
—
destructive
boolean
—
< Toast visible = { show } message = "Saved!" intent = "success" icon = "checkmark" position = "top" onDismiss = { hide } duration = { 3000 } />
Prop
Type
Default
visible
boolean
required
message
string
required
intent
Intent
—
icon
string
—
duration
number
3000
position
"top" | "bottom"
—
onDismiss
() => void
required
actionLabel
string
—
onActionPress
() => void
—
< Snackbar visible = { show } message = "Item deleted" actionLabel = "Undo" onAction = { undo } onDismiss = { hide } />
Prop
Type
Default
visible
boolean
required
message
string
required
actionLabel
string
—
onAction
() => void
—
onDismiss
() => void
required
duration
number
—
icon
string
—
position
"top" | "bottom"
—
< Banner title = "Update available" intent = "info" dismissible onDismiss = { hide } actionLabel = "Update" onAction = { update } />
Prop
Type
Default
title
string
required
message
string
—
intent
Intent
—
icon
string
—
dismissible
boolean
—
onDismiss
() => void
—
actionLabel
string
—
onAction
() => void
—
< Tooltip content = "Copy to clipboard" position = "top" >
< Button icon = "copy-outline" variant = "ghost" onPress = { copy } />
</ Tooltip >
Prop
Type
Default
content
string
required
children
ReactNode
required
position
"top" | "bottom" | "left" | "right"
—
visible
boolean
—
onToggle
(visible) => void
—
maxWidth
number
—
< Popover content = { < Menu /> } position = "bottom" closeOnPress >
< Button icon = "ellipsis-vertical" variant = "ghost" onPress = { ( ) => { } } />
</ Popover >
Prop
Type
Default
content
ReactNode
required
children
ReactNode
required
position
"top" | "bottom" | "left" | "right"
—
visible
boolean
—
onToggle
(visible) => void
—
closeOnPress
boolean
—
maxWidth
number
—
< Confetti active = { celebrate } count = { 50 } duration = { 3000 } />
Prop
Type
Default
active
boolean
required
count
number
—
duration
number
—
colors
string[]
—
< ProgressBar progress = { 0.75 } intent = "success" size = "md" animated />
Prop
Type
Default
progress
number (0–1)
required
intent
Intent
—
size
SizeToken
—
animated
boolean
—
< ProgressRing progress = { 0.75 } intent = "primary" size = "lg" label = "75%" sublabel = "Complete" />
Prop
Type
Default
progress
number (0–1)
required
intent
Intent
—
size
SizeToken
—
label
string
—
sublabel
string
—
< Loader label = "Loading..." size = "large" />
Prop
Type
Default
label
string
—
size
"small" | "large"
—
< Skeleton width = { 200 } height = { 20 } radius = { 6 } />
< Skeleton width = "100%" height = { 120 } / >
Prop
Type
Default
width
DimensionValue
—
height
DimensionValue
—
radius
number
—
< CountdownTimer targetDate = { new Date ( "2025-01-01" ) } showDays showLabels size = "lg" onComplete = { done } />
< CountdownTimer seconds = { 300 } running onComplete = { done} / >
Prop
Type
Default
targetDate
Date
—
seconds
number
—
onComplete
() => void
—
running
boolean
—
showDays
boolean
—
showLabels
boolean
—
separator
string
—
size
SizeToken
—
< Badge label = "New" intent = "success" variant = "solid" size = "sm" />
Prop
Type
Default
label
string
required
intent
Intent
—
variant
"solid" | "outline"
—
size
SizeToken
—
< StatusDot status = "online" size = "md" />
Prop
Type
Default
status
"online" | "offline" | "idle"
—
size
SizeToken
—
< StatusTracker
steps = { [ { label : "Ordered" , date : "Jan 1" } , { label : "Shipped" , date : "Jan 3" } , { label : "Delivered" } ] }
currentStep = { 1 }
/>
Prop
Type
Default
steps
StatusStep[]
required
currentStep
number
required
StatusStep: { label, description?, date? }
< NetworkBanner visible = { ! isOnline } message = "No internet connection" intent = "danger" />
Prop
Type
Default
visible
boolean
required
message
string
—
intent
Intent
—
< Avatar uri = "https://..." name = "John Doe" size = "lg" status = "online" />
Prop
Type
Default
uri
string | null
—
name
string
—
size
SizeToken
—
status
"online" | "offline" | "idle"
—
< AvatarGroup items = { users } max = { 5 } size = { 40 } overlap = { 12 } />
Prop
Type
Default
items
AvatarItem[]
required
max
number
—
size
number
—
overlap
number
—
AvatarItem: { key, name, source?: { uri } }
ProfileHeader
< ProfileHeader
avatar = "https://..."
name = "Jane Doe"
subtitle = "@janedoe"
stats = { [ { label : "Posts" , value : "42" } , { label : "Followers" , value : "1.2K" } ] }
action = { < Button title = "Follow" size = "sm" /> }
/>
Prop
Type
Default
avatar
string | null
—
name
string
required
subtitle
string
—
stats
{ label, value }[]
—
action
ReactNode
—
< SocialButton provider = "google" onPress = { loginGoogle } variant = "filled" fullWidth />
< SocialButton provider = "apple" onPress = { loginApple} variant = "outline" / >
Prop
Type
Default
provider
"google" | "facebook" | "apple" | "github" | "twitter" | "microsoft"
required
onPress
() => void
required
variant
"filled" | "outline" | "icon-only"
—
label
string
— (auto from provider)
loading
boolean
—
disabled
boolean
—
fullWidth
boolean
—
size
"sm" | "md" | "lg"
—
< AuthDivider text = "or continue with" />
Prop
Type
Default
text
string
—
< MessageBubble text = "Hello!" isOwn = { true } timestamp = "10:30 AM" />
Prop
Type
Default
text
string
required
isOwn
boolean
required
timestamp
string
—
Animated dots indicator.
No required props (only style and testID).
Notification & Onboarding
< NotificationItem
title = "New message"
message = "You have a new message from John"
timestamp = "2m ago"
icon = "mail-outline"
intent = "info"
read = { false }
onPress = { openNotif }
onDismiss = { dismissNotif }
/>
Prop
Type
Default
title
string
required
message
string
required
timestamp
string
required
icon
string
—
avatar
string
—
avatarName
string
—
intent
Intent
—
read
boolean
—
onPress
() => void
—
onDismiss
() => void
—
< OnboardingSlide
title = "Welcome"
description = "Get started with our app"
icon = "rocket-outline"
actionLabel = "Next"
onAction = { next }
skipLabel = "Skip"
onSkip = { skip }
step = { 1 }
totalSteps = { 3 }
/>
Prop
Type
Default
title
string
required
description
string
required
image
ImageSourcePropType
—
icon
string
—
iconSize
number
—
actionLabel
string
—
onAction
() => void
—
skipLabel
string
—
onSkip
() => void
—
step
number
—
totalSteps
number
—
< PermissionCard
title = "Camera Access"
description = "Required for taking photos"
status = "unknown"
icon = { < Icon name = "camera" size = { 24 } /> }
actionText = "Allow"
onActionPress = { requestPermission }
/>
Prop
Type
Default
title
string
required
description
string
—
status
"granted" | "denied" | "blocked" | "limited" | "unknown"
—
icon
ReactNode
—
actionText
string
—
secondaryActionText
string
—
onActionPress
() => void
—
onSecondaryActionPress
() => void
—
loading
boolean
—
disabled
boolean
—
fullWidth
boolean
—
elevated
boolean
—
< PriceTag amount = { 29.99 } currency = "$" original = { 49.99 } size = "lg" />
Prop
Type
Default
amount
number
required
currency
string
"$"
original
number
— (shows strikethrough)
size
SizeToken
—
< QuantitySelector value = { 1 } onValueChange = { setQty } min = { 1 } max = { 10 } size = "md" />
Prop
Type
Default
value
number
required
onValueChange
(value) => void
required
min
number
—
max
number
—
size
SizeToken
—
< RatingStars rating = { 4.5 } total = { 5 } size = "md" />
Prop
Type
Default
rating
number
required
total
number
5
size
SizeToken
—
< PromoInput value = { code } onChangeText = { setCode } onApply = { applyCode } status = "success" successMessage = "10% off!" />
Prop
Type
Default
value
string
required
onChangeText
(text) => void
required
onApply
(code) => void
required
status
"idle" | "loading" | "success" | "error"
—
successMessage
string
—
errorMessage
string
—
placeholder
string
—
disabled
boolean
—
< Accordion
items = { [ { key : "faq1" , title : "What is Salt?" , content : < Text > A UI library.</ Text > , icon : "help-circle-outline" } ] }
multiple
bordered
/>
Prop
Type
Default
items
AccordionItem[]
required
expandedKeys
string[]
—
onToggle
(key) => void
—
multiple
boolean
—
bordered
boolean
—
AccordionItem: { key, title, content: ReactNode, icon? }
< SwipeableRow
rightActions = { [ { key : "delete" , icon : "trash-outline" , label : "Delete" , color : "#dc2626" , onPress : del } ] }
>
< ListItem title = "Swipe me" />
</ SwipeableRow >
Prop
Type
Default
children
ReactNode
required
leftActions
SwipeAction[]
—
rightActions
SwipeAction[]
—
actionWidth
number
—
threshold
number
—
onSwipeOpen
(side) => void
—
SwipeAction: { key, icon, label?, color, onPress }
< DragList items = { [ { key : "1" , label : "First" , icon : "star" } ] } onReorder = { setItems } handlePosition = "right" />
Prop
Type
Default
items
DragItem[]
required
onReorder
(items) => void
required
renderItem
(item, index) => ReactNode
—
handlePosition
"left" | "right"
—
DragItem: { key, label, icon? }
< TreeView
nodes = { [ {
key : "src" , label : "src" , icon : "folder" ,
children : [ { key : "app" , label : "App.tsx" , icon : "document" } ] ,
} ] }
expandedKeys = { expanded }
onToggle = { toggleNode }
selectedKey = "app"
onSelect = { selectNode }
/>
Prop
Type
Default
nodes
TreeNode[]
required
expandedKeys
string[]
—
onToggle
(key) => void
—
onSelect
(node) => void
—
selectedKey
string
—
indentWidth
number
—
TreeNode: { key, label, icon?, children?: TreeNode[], data? }
< EmptyState title = "No results" description = "Try a different search" primaryAction = { { title : "Reset" , onPress : reset } } />
Prop
Type
Default
title
string
required
description
string
—
primaryAction
{ title, onPress }
—
secondaryAction
{ title, onPress }
—
< ErrorState title = "Something went wrong" description = "Please try again" onRetry = { retry } />
Prop
Type
Default
title
string
—
description
string
—
onRetry
() => void
—
Wraps children with loading/error/retry states.
< RetryView loading = { isLoading } error = { error } onRetry = { refetch } >
< DataTable . . . / >
</ RetryView >
Prop
Type
Default
loading
boolean
—
error
string | boolean
—
onRetry
() => void
—
children
ReactNode
required
loadingLabel
string
—
errorTitle
string
—
errorDescription
string
—
< FloatingToolbar
items = { [ { key : "pen" , icon : "pencil-outline" , label : "Pen" } , { key : "eraser" , icon : "trash-outline" } ] }
selected = "pen"
onSelect = { setTool }
position = "bottom"
/>
Prop
Type
Default
items
ToolbarItem[]
required
selected
string
—
onSelect
(key) => void
required
position
"top" | "bottom" | "left" | "right"
"bottom"
horizontal
boolean
true
ToolbarItem: { key, icon, label?, disabled? }
< CanvasControlPanel
title = "Properties"
sections = { [
{ key : "fill" , title : "Fill" , children : < ColorPicker /> , collapsed : false } ,
{ key : "stroke" , title : "Stroke" , children : < Slider /> , collapsed : true } ,
] }
onToggleSection = { toggleSection }
position = "right"
width = { 260 }
/>
Prop
Type
Default
sections
ControlSection[]
required
onToggleSection
(key) => void
—
title
string
—
position
"left" | "right"
"right"
width
number
260
ControlSection: { key, title, children: ReactNode, collapsed? }
< LayerListItem
title = "Background"
icon = "image-outline"
selected
visible
locked = { false }
onPress = { selectLayer }
onToggleVisibility = { toggleVis }
onToggleLock = { toggleLock }
/>
Prop
Type
Default
title
string
required
icon
string
—
thumbnail
ReactNode
—
selected
boolean
—
visible
boolean
—
locked
boolean
—
onPress
() => void
—
onToggleVisibility
() => void
—
onToggleLock
() => void
—
onDragStart
() => void
—
Light/dark/system toggle. Reads and sets preference via useTheme().
< ThemeSwitcher fullWidth />
Prop
Type
Default
fullWidth
boolean
—
Font size adjustment slider.
< FontLevelSwitcher size = "md" />
Prop
Type
Default
size
SizeToken
—
Ionicons wrapper with semantic aliases.
< Icon name = "search" size = { 20 } color = { colors . text } />
< Icon name = "chevron-forward" size = { 16 } color = { colors . muted} / >
Prop
Type
Default
name
IconName
required
size
number
—
color
string
—
Built-in aliases: search, close, back, add, edit, check, chevron-right, save. Any Ionicons name also works.
< Carousel autoPlay interval = { 3000 } showDots >
< View > < Text > Slide 1</ Text > </ View >
< View > < Text > Slide 2</ Text > </ View >
</ Carousel >
Prop
Type
Default
children
ReactNode[]
required
autoPlay
boolean
false
interval
number
3000
showDots
boolean
true
dotSize
number
8
itemWidth
number
— (auto: container width)
gap
number
0
< BottomSheet visible = { open } onClose = { close } title = "Options" height = { 400 } >
{ content }
</ BottomSheet >
Prop
Type
Default
visible
boolean
required
onClose
() => void
required
title
string
—
children
ReactNode
required
height
number
—
closable
boolean
—
No native dependencies — pure React Native + Expo Go compatible
Theme-driven — all colors, spacing, and radius from tokens, never hardcoded
AI-friendly — predictable naming, strong defaults, typed unions
Composable — every component accepts style and testID props
Accessible — accessibilityRole, accessibilityLabel, and accessibilityState on interactive components
Always wrap app root in <SaltProvider>
Typography uses fontSize not size — values: "sm" | "md" | "lg"
Buttons, badges, inputs use size — values: "sm" | "md" | "lg"
Colors come from theme — use const { colors } = useTheme().theme
Intent drives color for Button, Badge, Toast, Banner, ProgressBar, etc.
Variant drives style for Button: "solid" | "outline" | "ghost" | "text" | "link"
Icons use Ionicons names (e.g., "chevron-forward", "trash-outline", "home-outline")
Spacing props accept tokens ("xs", "sm", "md", "lg", "xl", "xxl") or numbers
MIT