From react-native-hifi
Provides native Jetpack Compose UI components including LazyColumn scrolling lists and XML vector icons for Android-specific screens in Expo React Native apps.
How this skill is triggered — by the user, by Claude, or both
Slash command
/react-native-hifi:expo-ui-jetpack-composeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`@expo/ui/jetpack-compose` provides Android-native Jetpack Compose views directly in React Native Expo apps. These components render using Android's modern declarative UI framework, giving you truly native Android look-and-feel without writing Kotlin.
@expo/ui/jetpack-compose provides Android-native Jetpack Compose views directly in React Native Expo apps. These components render using Android's modern declarative UI framework, giving you truly native Android look-and-feel without writing Kotlin.
Core principle: Use platform-specific @expo/ui imports to get native-quality components that follow each platform's design language. On Android, this means Jetpack Compose.
// Android-specific import
import { Button, LazyColumn, Icon } from '@expo/ui/jetpack-compose';
// For platform-specific rendering
import { Platform } from 'react-native';
function MyComponent() {
if (Platform.OS === 'android') {
return <AndroidView />;
}
return <IOSView />;
}
LazyColumn is the Jetpack Compose equivalent of RecyclerView. It lazily composes and lays out only the visible items, making it efficient for large datasets.
import { LazyColumn, Section, Item, Text } from '@expo/ui/jetpack-compose';
function ContactList() {
const contacts = [
{ id: '1', name: 'Alice', phone: '555-0101' },
{ id: '2', name: 'Bob', phone: '555-0102' },
{ id: '3', name: 'Charlie', phone: '555-0103' },
];
return (
<LazyColumn style={{ flex: 1 }}>
<Section header="Contacts">
{contacts.map((contact) => (
<Item key={contact.id}>
<Text style={{ fontSize: 16, fontWeight: 'bold' }}>
{contact.name}
</Text>
<Text style={{ fontSize: 14, color: '#666' }}>
{contact.phone}
</Text>
</Item>
))}
</Section>
</LazyColumn>
);
}
import { LazyColumn, Section, Item, Text } from '@expo/ui/jetpack-compose';
function SettingsScreen() {
return (
<LazyColumn style={{ flex: 1 }}>
<Section header="Account">
<Item onPress={() => console.log('Profile')}>
<Text>Profile</Text>
</Item>
<Item onPress={() => console.log('Security')}>
<Text>Security</Text>
</Item>
</Section>
<Section header="Preferences">
<Item onPress={() => console.log('Notifications')}>
<Text>Notifications</Text>
</Item>
<Item onPress={() => console.log('Theme')}>
<Text>Theme</Text>
</Item>
</Section>
<Section header="About">
<Item>
<Text>Version 1.0.0</Text>
</Item>
</Section>
</LazyColumn>
);
}
Render Android XML vector drawables as icons:
import { Icon } from '@expo/ui/jetpack-compose';
function NavigationBar() {
return (
<View style={{ flexDirection: 'row', justifyContent: 'space-around' }}>
<Icon
name="home"
size={24}
tintColor="#333"
/>
<Icon
name="search"
size={24}
tintColor="#333"
/>
<Icon
name="settings"
size={24}
tintColor="#333"
/>
</View>
);
}
import { Icon } from '@expo/ui/jetpack-compose';
// Material Design icons available by name
<Icon name="favorite" size={24} tintColor="red" />
<Icon name="share" size={24} tintColor="#1976D2" />
<Icon name="more_vert" size={24} tintColor="#757575" />
Native Material Design 3 buttons:
import { Button } from '@expo/ui/jetpack-compose';
function ActionButtons() {
return (
<View style={{ gap: 12 }}>
<Button
title="Filled Button"
onPress={() => console.log('Pressed')}
variant="filled"
/>
<Button
title="Outlined Button"
onPress={() => console.log('Pressed')}
variant="outlined"
/>
<Button
title="Text Button"
onPress={() => console.log('Pressed')}
variant="text"
/>
</View>
);
}
When you need to wrap Jetpack Compose views for use within React Native's view hierarchy:
import { requireNativeView } from 'expo';
// Wrap a native Android Compose component
const NativeComposeView = requireNativeView('MyComposeView');
function ComposeWrapper({ data, onAction }) {
return (
<NativeComposeView
style={{ flex: 1 }}
data={data}
onAction={onAction}
/>
);
}
If you need a Compose component not available in @expo/ui:
// android/src/main/java/com/myapp/MyComposeView.kt
package com.myapp
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class MyComposeModule : Module() {
override fun definition() = ModuleDefinition {
Name("MyComposeView")
View(MyComposeView::class) {
Prop("title") { view: MyComposeView, title: String ->
view.setTitle(title)
}
Events("onAction")
}
}
}
Pattern for using Jetpack Compose components alongside cross-platform code:
import { Platform, View, Text } from 'react-native';
// Lazy imports to avoid loading on wrong platform
const AndroidList = Platform.OS === 'android'
? require('@expo/ui/jetpack-compose').LazyColumn
: null;
function AdaptiveList({ items }) {
if (Platform.OS === 'android' && AndroidList) {
return (
<AndroidList style={{ flex: 1 }}>
{/* Android-native rendering */}
</AndroidList>
);
}
// Fallback for iOS/web
return (
<FlatList
data={items}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
);
}
For larger platform differences, use Expo Router's platform file extensions:
components/
SettingsList.tsx # Shared/default
SettingsList.android.tsx # Android with Jetpack Compose
SettingsList.ios.tsx # iOS with SwiftUI
// components/SettingsList.android.tsx
import { LazyColumn, Section, Item, Text } from '@expo/ui/jetpack-compose';
export function SettingsList({ sections }) {
return (
<LazyColumn style={{ flex: 1 }}>
{sections.map((section) => (
<Section key={section.title} header={section.title}>
{section.items.map((item) => (
<Item key={item.id} onPress={item.onPress}>
<Text>{item.label}</Text>
</Item>
))}
</Section>
))}
</LazyColumn>
);
}
| Mistake | Fix |
|---|---|
Importing @expo/ui/jetpack-compose on iOS | Guard imports with Platform.OS check or use platform file extensions |
Using FlatList patterns with LazyColumn | LazyColumn uses Section/Item children, not renderItem |
| Forgetting to rebuild after adding native dependency | Run eas build or npx expo run:android after adding @expo/ui |
| Mixing Compose and View styling | Compose components have their own styling; use props, not StyleSheet |
| Expecting identical rendering across platforms | Jetpack Compose follows Material Design; iOS follows Human Interface Guidelines |
| Task | Pattern |
|---|---|
| Import Compose components | import { ... } from '@expo/ui/jetpack-compose' |
| Scrolling list | <LazyColumn> with <Section> and <Item> |
| Icon | <Icon name="icon_name" size={24} /> |
| Button | <Button title="Label" variant="filled" onPress={fn} /> |
| Platform guard | Platform.OS === 'android' or .android.tsx file |
| Custom native view | requireNativeView('ViewName') |
| Host wrapping | Expo Modules API with Kotlin + Compose |
npx claudepluginhub bidah/react-native-hifi --plugin react-native-hifiIntegrates Jetpack Compose Views, modifiers, and components like LazyColumn and Icon into Expo apps using @expo/ui/jetpack-compose for Android UI in React Native.
Integrates Jetpack Compose views and modifiers into Expo apps using @expo/ui/jetpack-compose. Use for building Android-native UI with Compose components like LazyColumn, Button, and Text.
Guides building native Android UIs with Jetpack Compose, including state management via remember/mutableStateOf, state hoisting, and ViewModel integration.