How It Works
This guide explains how React Native Jetpack Compose integrates native Jetpack Compose components into your React Native app using the New Architecture.
Architecture Overview
Section titled “Architecture Overview”┌─────────────────────────────────────────────────────────────┐│ React Native App │├─────────────────────────────────────────────────────────────┤│ TypeScript/JavaScript ││ ┌─────────────────────────────────────────────────────┐ ││ │ <DatePicker value={date} onConfirm={setDate} /> │ ││ └─────────────────────────────────────────────────────┘ ││ │ ││ Fabric Bridge ││ │ │├─────────────────────────────────────────────────────────────┤│ Native Android (Kotlin) ││ ┌─────────────────────────────────────────────────────┐ ││ │ DatePickerView : InlineComposeView │ ││ │ └── @Composable DatePicker() │ ││ └─────────────────────────────────────────────────────┘ ││ │ ││ Jetpack Compose ││ │ ││ ┌─────────────────────────────────────────────────────┐ ││ │ Material 3 DatePickerDialog │ ││ └─────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘Key Technologies
Section titled “Key Technologies”React Native New Architecture (Fabric)
Section titled “React Native New Architecture (Fabric)”The library uses React Native’s Fabric renderer, which provides:
- Direct native calls: No bridge serialization overhead
- Synchronous operations: Faster prop updates and event handling
- Codegen: Type-safe bindings generated from TypeScript specs
Jetpack Compose
Section titled “Jetpack Compose”Jetpack Compose is Android’s modern declarative UI toolkit:
- Kotlin-based: Native Android development
- Material 3: Built-in Material Design 3 components
- Dynamic theming: Material You with dynamic colors on Android 12+
Codegen
Section titled “Codegen”The library uses React Native’s codegen to generate native bindings:
- TypeScript specs define props and events
- Codegen generates Java interfaces and C++ descriptors
- Kotlin implementations fulfill these interfaces
Component Structure
Section titled “Component Structure”Each component follows a consistent pattern:
1. TypeScript Spec
Section titled “1. TypeScript Spec”Defines the native component interface:
import type { ViewProps } from 'react-native';import type { DirectEventHandler, Double } from 'react-native/Libraries/Types/CodegenTypes';import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
interface NativeProps extends ViewProps { value?: Double; minDate?: Double; maxDate?: Double; onConfirm?: DirectEventHandler<{ value: Double }>;}
export default codegenNativeComponent<NativeProps>('DatePicker');2. TypeScript Wrapper
Section titled “2. TypeScript Wrapper”Provides a user-friendly API:
export function DatePicker({ value, onConfirm, ...props }: DatePickerProps) { // Convert Date to epoch milliseconds for native const nativeValue = value?.getTime();
// Convert native epoch back to Date const handleConfirm = (event: { nativeEvent: { value: number } }) => { onConfirm?.(new Date(event.nativeEvent.value)); };
return ( <DatePickerNative value={nativeValue} onConfirm={handleConfirm} {...props} /> );}3. ViewManager (Kotlin)
Section titled “3. ViewManager (Kotlin)”Implements the codegen-generated interface:
class DatePickerViewManager : SimpleViewManager<DatePickerView>(), DatePickerViewManagerInterface<DatePickerView> {
override fun getName() = "DatePicker"
override fun createViewInstance(context: ThemedReactContext) = DatePickerView(context)
override fun setValue(view: DatePickerView, value: Double?) { view.setValue(value?.toLong()) }
// ... more prop setters}4. View Implementation (Kotlin)
Section titled “4. View Implementation (Kotlin)”The actual Compose-based view:
class DatePickerView(context: Context) : InlineComposeView(context) { private var selectedDate: Long? by mutableStateOf(null)
fun setValue(value: Long?) { selectedDate = value }
@Composable override fun Content() { val datePickerState = rememberDatePickerState( initialSelectedDateMillis = selectedDate )
DatePickerDialog( onConfirm = { dispatchEvent(ConfirmEvent(id, datePickerState.selectedDateMillis)) }, // ... ) }}Event Flow
Section titled “Event Flow”Events flow from native to JavaScript:
- User interacts with Compose UI (e.g., selects a date)
- Compose callback fires in Kotlin
- Event dispatched via
dispatchEvent() - Fabric delivers event to JavaScript
- TypeScript wrapper transforms and calls
onConfirm
// Native sidedispatchEvent(ConfirmEvent(viewId, dateMillis))
// Event classclass ConfirmEvent(viewId: Int, private val value: Long) : Event<ConfirmEvent>(viewId) { override fun getEventName() = "topConfirm" override fun getEventData() = mapOf("value" to value.toDouble())}// JavaScript side receives{ nativeEvent: { value: 1705276800000 } }Prop Updates
Section titled “Prop Updates”Props flow from JavaScript to native:
- React state changes (e.g.,
setValue(newDate)) - Fabric detects prop change
- ViewManager setter called (e.g.,
setValue()) - Compose state updated, triggering recomposition
// JavaScript<DatePicker value={date} />// Native - ViewManageroverride fun setValue(view: DatePickerView, value: Double?) { view.setValue(value?.toLong())}
// Native - View (triggers recomposition)fun setValue(value: Long?) { selectedDate = value // MutableState}Styling Integration
Section titled “Styling Integration”Components use Material 3 theming from the Android system:
- Colors: Dynamic colors on Android 12+, Material defaults otherwise
- Typography: System Material 3 type scale
- Shapes: Material 3 shape system
React Native style props control:
- Container dimensions and positioning
- Margins and padding (via container)
<DatePicker style={{ marginBottom: 16, // Applied to container marginHorizontal: 8, }}/>Performance Considerations
Section titled “Performance Considerations”Why Native Components?
Section titled “Why Native Components?”- No JS thread blocking: UI renders on native thread
- Native animations: 60fps animations without JS bridge
- Platform consistency: Identical to native Android apps
Best Practices
Section titled “Best Practices”- Minimize prop updates: Avoid unnecessary re-renders
- Use callbacks properly: Memoize handlers with
useCallback - Batch state updates: Combine related state changes
// Good: Memoized callbackconst handleConfirm = useCallback((date: Date | null) => { setDate(date);}, []);
// Avoid: Inline function creates new reference each render<DatePicker onConfirm={(date) => setDate(date)} />Debugging
Section titled “Debugging”View Hierarchy
Section titled “View Hierarchy”Use Android Studio’s Layout Inspector to see:
- React Native view tree
- Compose component tree
- Actual rendered UI
Logging
Section titled “Logging”Enable detailed logs:
adb logcat -s ReactNative:V ReactNativeJS:VCommon Issues
Section titled “Common Issues”- Component not rendering: Check New Architecture is enabled
- Props not updating: Verify ViewManager implements setter
- Events not firing: Check event name matches (
top+ PascalCase)