@smilodon/react-native
v1.9.2
Published
React Native adapter for Smilodon
Readme
@smilodon/react-native
React Native adapter for Smilodon.
This package provides two implementations behind one import:
- Native mobile: renders Smilodon inside a
react-native-webviewbridge - Web: mounts the same core custom element directly for React Native Web environments
That split lets you keep one API surface while still using the DOM-based Smilodon core on native platforms.
📖 Documentation
Live documentation & interactive examples: navidrezadoost.github.io/Smilodon
Framework integration (Vue 3, Nuxt 4, React, SSR, Vite): docs/FRAMEWORK-INTEGRATION.md
For comprehensive documentation covering installation, Expo and bare setup, native/web architecture, styling, grouped items, diagnostics, performance, and troubleshooting:
👉 Complete React Native Guide 👈
The complete guide includes:
- ✅ Native and React Native Web setup flows
- ✅ Simple, medium, advanced, and production usage patterns
- ✅ WebView bridge architecture and styling model
- ✅ Controlled and uncontrolled data flows
- ✅ Performance and diagnostics guidance
- ✅ Detailed troubleshooting notes
Installation
npm install @smilodon/react-native react-native-webviewIf your app does not already depend on React Native Web, install the packages required by your platform stack as usual.
When to use this adapter
Choose @smilodon/react-native if you want:
- one select abstraction shared between native mobile and web targets
- searchable, grouped, multi-select behavior backed by the same core runtime
- clear imperative control over opening, closing, replacing data, and resetting values
- access to diagnostic and limitation-policy hooks from React Native screens
Quick start
import { useState } from 'react'
import { View } from 'react-native'
import { Select } from '@smilodon/react-native'
export default function ExampleScreen() {
const [value, setValue] = useState<string | number>('')
return (
<View style={{ padding: 16 }}>
<Select
items={[
{ value: 'ios', label: 'iOS' },
{ value: 'android', label: 'Android' },
{ value: 'web', label: 'Web' },
]}
value={value}
onChange={(next) => setValue(next as string)}
searchable
clearable
placeholder="Choose a platform"
/>
</View>
)
}Architecture
Native path
On iOS and Android, the adapter:
- embeds the built core bundle into an HTML document,
- loads that document inside
WebView, - synchronizes items, value, config, and styling through a bridge payload,
- forwards events like
change,search,open,close,clear, anddiagnosticback to React Native.
Web path
On web, the adapter:
- loads the core custom element bundle directly,
- creates an
enhanced-selectelement in the DOM, - forwards props through
updateConfig(),setItems(), andsetSelectedValues(), - exposes the same imperative handle as the native variant.
Important props
Data and selection
itemsgroupedItemsvaluedefaultValuemultiplemaxSelections
Interaction
searchabledirectionclearableclearSelectionOnClearclearSearchOnClearclearAriaLabelclearIconinfiniteScrollpageSizevirtualizeddisabledOptionBehaviorshowSelectedIndicator
Clear control visibility
clearable does not reserve an empty action area. The control appears only when an enabled target—a selected value or search query—can be cleared, and the input/arrow layout returns to normal afterward. The native WebView and React Native Web paths share this behavior, including multi-select and RTL layouts.
<Select
items={items}
value={value}
onChange={setValue}
searchable
clearable
clearSelectionOnClear
clearSearchOnClear
onClear={({ clearedSelection, clearedSearch }) => {
console.log({ clearedSelection, clearedSearch })
}}
/>For an always-present disabled-when-empty action, pass config={{ clearControl: { hideWhenEmpty: false } }}. On web, style it with ::part(clear-button), ::part(clear-icon), and --select-clear-* tokens rather than permanent padding.
Example:
<Select
items={items}
direction="rtl"
removeButtonIcon="−"
disabledOptionBehavior={{ hoverable: true, focusable: true, selectable: false }}
showSelectedIndicator={false}
/>Core config parity
The React Native adapter accepts the same major runtime behavior groups as the shared core through adapter props and the full config prop.
Supported advanced config props
selectionConfigmultiSelectDisplayscrollToSelectedstylesconfig
These are merged into the bridge payload on native and forwarded through updateConfig() on React Native Web.
Example: multi-select chip layout and icon styling
<Select
items={items}
value={value}
onChange={setValue}
multiple
searchable
multiSelectDisplay={{
mode: 'horizontal',
maxHeight: '56px',
overflowX: 'auto',
overflowY: 'hidden',
dragScroll: true,
}}
selectionConfig={{
closeOnSelect: false,
toggleOnTriggerClick: true,
}}
styles={{
badgeRemoveIcon: {
color: '#dc2626',
transform: 'scale(1.1)',
},
}}
removeButtonIcon="−"
selectStyle={{
'--select-badge-bg': '#eff6ff',
'--select-badge-remove-icon-font-size': '14px',
}}
/>Example: full config prop
<Select
items={items}
multiple
config={{
dropdownPlacement: { mode: 'auto' },
scrollToSelected: {
enabled: true,
multiSelectTarget: 'last',
},
multiSelectDisplay: {
mode: 'vertical',
maxHeight: '120px',
},
selection: {
allowDeselect: true,
closeOnSelect: false,
},
styles: {
badge: {
background: '#eff6ff',
border: '1px solid #bfdbfe',
color: '#1d4ed8',
},
badgeRemoveIcon: {
color: '#1d4ed8',
},
},
}}
/>Global defaults note
For React Native, prefer per-instance config and convenience props. The native implementation runs inside its own embedded runtime, so app-level configureSelect() globals are not the recommended integration point here.
Native layout control
collapsedHeightexpandedHeightcontainerStyleselectStyle
selectStyle is forwarded into the embedded select element and is the main place to set Smilodon CSS variables from React Native.
<Select
items={items}
selectStyle={{
'--select-accent': '#2563eb',
'--select-border-focus': '#2563eb',
'--select-input-min-height': '52px',
}}
/>Imperative handle
The component exposes a ref handle with these methods:
open()close()clear()clearSearch()setItems()setGroupedItems()setValue()updateConfig()
import { useRef } from 'react'
import { Button } from 'react-native'
import { Select, type SelectHandle } from '@smilodon/react-native'
const ref = useRef<SelectHandle>(null)
<>
<Select ref={ref} items={items} />
<Button title="Open" onPress={() => ref.current?.open()} />
<Button title="Clear Search" onPress={() => ref.current?.clearSearch()} />
</>React Native Web additionally benefits from the underlying DOM runtime, so web builds also inherit the same config parity behavior as the other web adapters.
Events
The adapter forwards the core event model into React Native callbacks:
onChangeonSelectonOpenonCloseonSearchonLoadMoreonCreateonClearonDiagnostic
For controlled usage, onChange is the main callback to mirror back into state.
Grouped items
Use groupedItems when you want native and web targets to render stable category sections.
<Select
groupedItems={[
{
label: 'Frontend',
options: [
{ value: 'react', label: 'React' },
{ value: 'solid', label: 'SolidJS' },
],
},
{
label: 'Mobile',
options: [
{ value: 'ios', label: 'iOS' },
{ value: 'android', label: 'Android' },
],
},
]}
multiple
/>Diagnostics and tracking
The React Native adapter forwards the core tracking switches:
trackingEnabledtrackEventstrackStylingtrackLimitationsemitDiagnosticstrackingMaxEntrieslimitationPoliciesautoMitigateRuntimeModeSwitch
Use these when you need runtime observability during QA or production debugging.
Practical limitations
- Native rendering depends on
WebView, so your UX and performance characteristics are still ultimately powered by an embedded web runtime. selectStylecustomizes the internal select through CSS variables, not React Native style props.- Browser-only DOM hooks should not be assumed from native code; use the exported handle and callbacks instead.
