UI Kit
Import from lakebed-native/ui:
import {
Router, Stack, Tabs, Route, Link,
navigate, useRoute, useParams, useNavigate,
Screen, Text, Button, Input, TextArea,
List, SectionList, Image, Avatar, Card,
Row, Column, Spacer, Divider,
Modal, Sheet, Toast, Spinner,
EmptyState, ErrorState,
Form, Field, Switch, Checkbox, Select, DatePicker
} from "lakebed-native/ui";
Use lakebed-native/ui first. Raw React Native is allowed as an escape hatch. Add testID on interactive elements for agent-device automation.
Router
<Router>
<Tabs>
<Route path="/" title="Home" element={<Home />} />
<Route path="/settings" title="Settings" element={<Settings />} />
<Route path="/items/:id" tab={false} title="Item" element={<Item />} />
</Tabs>
</Router>
Set tab={false} on detail/stack routes so they do not appear in the tab bar. Use <Stack> inside a tab for push detail screens. <Screen> applies top safe-area insets automatically.
Component props (canonical)
Layout
| Component | Props |
|---|---|
Screen |
{ children, title?, testID? } |
Card |
{ children, testID? } |
Row / Column |
{ children, style? } |
Spacer |
{ size?: number } (default 16) |
Divider |
— |
Typography & media
| Component | Props |
|---|---|
Text |
{ children, style?, testID? } |
Image |
{ source, style?, testID? } |
Avatar |
{ uri?, size?: number, label? } |
Inputs
| Component | Props |
|---|---|
Button |
{ title, onPress?, disabled?, testID?, variant?: "primary" | "secondary" | "danger" } |
Input |
{ value?, onChangeText?, placeholder?, testID?, secureTextEntry?, returnKeyType?, onSubmitEditing?, autoCorrect?, blurOnSubmit? } |
TextArea |
Input props + { rows?: number } |
Checkbox |
{ checked, onChange?, label?, testID? } |
Switch |
{ value, onValueChange?, testID? } |
Select |
{ value, options, onChange?, testID? } |
Lists & states
| Component | Props |
|---|---|
List<T> |
{ data: T[], renderItem: ({ item, index }) => ReactElement, keyExtractor: (item, index) => string, testID?, ListEmptyComponent? } |
Spinner |
{ testID? } |
EmptyState |
{ title, message? } |
ErrorState |
{ title, message?, onRetry? } |
Loading / error pattern
const { data, loading, error } = useQuery<Item[]>("listItems");
if (loading) return <Spinner testID="loading" />;
if (error) return <ErrorState title="Failed to load" message={error.message} />;
if (!data?.length) return <EmptyState title="No items yet" />;
return (
<List
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text testID={`item-${item.id}`}>{item.title}</Text>}
/>
);
Stack detail screen
<Router>
<Stack>
<Route path="/" element={<Home />} />
<Route path="/edit/:id" element={<EditScreen />} />
</Stack>
</Router>
Use useParams() and useNavigate() for detail/edit flows.