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.