RNHostView
A component that enables React Native views inside Jetpack Compose.
A component that enables proper layout behavior when React Native views are rendered inside Jetpack Compose components. It syncs layout information from Jetpack Compose back to React Native's Yoga layout system by updating the shadow node size.
When React Native views are placed inside Jetpack Compose components like ModalBottomSheet, Card, Row, Column and so on, the layout systems need to communicate. RNHostView bridges this gap:
- With
matchContents: The shadow node size is set to match the child React Native view's intrinsic size, allowing the Jetpack Compose parent to size itself based on the React Native content. - Without
matchContents: The shadow node size is set to match the parent Jetpack Compose view's size, allowing the React Native content to fill the available space (useful forflex: 1layouts).
Installation
- npx expo install @expo/uiIf you are installing this in an existing React Native app, make sure to install expo in your project.
Usage
Basic usage with matchContents
Use matchContents when you want the Jetpack Compose parent to size itself based on the React Native content.
import { useState } from 'react'; import { Pressable, Text as RNText, View } from 'react-native'; import { Host, Card, Column, Row, RNHostView, Text } from '@expo/ui/jetpack-compose'; import { fillMaxWidth, padding } from '@expo/ui/jetpack-compose/modifiers'; function Example() { const [counter, setCounter] = useState(0); return ( <Host style={{ flex: 1 }}> <Card modifiers={[fillMaxWidth()]}> <Column verticalArrangement={{ spacedBy: 12 }} modifiers={[padding(16, 16, 16, 16)]}> <Text>Mixing RN Components with Compose</Text> <Row horizontalArrangement={{ spacedBy: 24 }} verticalAlignment="center"> <RNHostView matchContents> <Pressable onPress={() => setCounter(prev => prev - 1)} style={{ height: 50, width: 50, borderRadius: 100, justifyContent: 'center', alignItems: 'center', backgroundColor: '#9B59B6', }}> <RNText style={{ color: 'white', fontSize: 24 }}>-</RNText> </Pressable> </RNHostView> <Text>{counter}</Text> <RNHostView matchContents> <Pressable onPress={() => setCounter(prev => prev + 1)} style={{ height: 50, width: 50, borderRadius: 100, justifyContent: 'center', alignItems: 'center', backgroundColor: '#9B59B6', }}> <RNText style={{ color: 'white', fontSize: 24 }}>+</RNText> </Pressable> </RNHostView> </Row> </Column> </Card> </Host> ); }
Flexible content without matchContents
When using flex: 1 in your React Native content, omit the matchContents prop so the content fills the available Jetpack Compose space.
import { Text as RNText, View } from 'react-native'; import { Host, Card, Column, Row, RNHostView, Text } from '@expo/ui/jetpack-compose'; import { fillMaxWidth, padding, size } from '@expo/ui/jetpack-compose/modifiers'; function Example() { return ( <Host style={{ flex: 1 }}> <Card modifiers={[fillMaxWidth()]}> <Column verticalArrangement={{ spacedBy: 12 }} modifiers={[padding(16, 16, 16, 16)]}> <Text>RN components with flex: 1 children</Text> <Row horizontalArrangement={{ spacedBy: 20 }} modifiers={[size(100, 100)]}> <RNHostView> <View style={{ flex: 1, backgroundColor: '#9B59B6', borderRadius: 10, }} /> </RNHostView> </Row> </Column> </Card> </Host> ); }
Usage with ModalBottomSheet
RNHostView works well inside ModalBottomSheet to display interactive React Native content.
import { useRef, useState } from 'react'; import { Pressable, Text as RNText, View } from 'react-native'; import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { padding } from '@expo/ui/jetpack-compose/modifiers'; function Example() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)}> <Column verticalArrangement={{ spacedBy: 16 }} modifiers={[padding(16, 16, 16, 16)]}> <Text>Mixing Compose + RN in a Bottom Sheet</Text> <RNHostView matchContents> <View> <RNText style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 8 }}> React Native Content </RNText> <Pressable style={{ backgroundColor: '#007AFF', padding: 12, borderRadius: 8, alignItems: 'center', }} onPress={hideSheet}> <RNText style={{ color: 'white', fontWeight: '600' }}>Close</RNText> </Pressable> </View> </RNHostView> </Column> </ModalBottomSheet> )} </Host> ); }
Flexible React Native content in a bottom sheet
Use RNHostView without matchContents to let the React Native view fill the remaining space inside the sheet. Combine with a height modifier on the parent Column to control the sheet size.
import { useRef, useState } from 'react'; import { Text as RNText, View } from 'react-native'; import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { height, padding } from '@expo/ui/jetpack-compose/modifiers'; function Example() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Flex Content Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)} skipPartiallyExpanded> <Column modifiers={[height(400), padding(16, 16, 16, 16)]}> <RNHostView> <View style={{ flex: 1, backgroundColor: '#9B59B6', borderRadius: 10 }}> <RNText style={{ color: 'white', fontSize: 18, fontWeight: 'bold', padding: 16, }}> React Native Content (flex: 1) </RNText> </View> </RNHostView> </Column> </ModalBottomSheet> )} </Host> ); }
API
import { RNHostView } from '@expo/ui/jetpack-compose';
Component
Type: React.Element<RNHostProps>
boolean • Default: falseWhen true, the RNHost will update its size in the Jetpack Compose view tree to match the children's size.
When false, the RNHost will use the size of the parent Jetpack Compose View.
Can be only set once on mount.