Guide
Examples & Recipes
Practical patterns and recipes for building React Native apps with Docyrus UI Native.
Real-world patterns that combine multiple Docyrus UI Native components. These examples assume you have the Expo + Docyrus UI Native setup from the installation guide.
List with Search and Filters
A searchable list view with pull-to-refresh and empty state handling:
import { useState, useCallback } from 'react';
import { View, FlatList, RefreshControl } from 'react-native';
import { SearchInput } from '@/components/docyrus-native/search-input';
import { ListBox } from '@/components/docyrus-native/list-box';
import { EmptyState } from '@/components/docyrus-native/empty-state';
import { Badge } from '@/components/docyrus-native/badge';
import { Spinner } from '@/components/docyrus-native/spinner';
export function ContactList() {
const [search, setSearch] = useState('');
const [refreshing, setRefreshing] = useState(false);
const filtered = contacts.filter((c) =>
c.name.toLowerCase().includes(search.toLowerCase())
);
const onRefresh = useCallback(async () => {
setRefreshing(true);
await fetchContacts();
setRefreshing(false);
}, []);
return (
<View className="flex-1">
<View className="px-4 pb-2 pt-4">
<SearchInput
value={search}
onChangeText={setSearch}
placeholder="Search contacts..."
/>
</View>
{filtered.length === 0 ? (
<EmptyState
icon="search"
title="No contacts found"
description="Try a different search term."
/>
) : (
<FlatList
data={filtered}
keyExtractor={(item) => item.id}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
renderItem={({ item }) => (
<ListBox.Item
title={item.name}
subtitle={item.email}
trailing={
<Badge variant={item.active ? 'default' : 'secondary'}>
{item.active ? 'Active' : 'Inactive'}
</Badge>
}
onPress={() => router.push(`/contact/${item.id}`)}
/>
)}
/>
)}
</View>
);
}Form Screen
A multi-field form with validation using Docyrus form fields:
import { View, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
import { Input } from '@/components/docyrus-native/input';
import { Textarea } from '@/components/docyrus-native/textarea';
import { Select } from '@/components/docyrus-native/select';
import { Switch } from '@/components/docyrus-native/switch';
import { Button } from '@/components/docyrus-native/button';
import { Label } from '@/components/docyrus-native/label';
const priorities = [
{ label: 'Low', value: 'low' },
{ label: 'Medium', value: 'medium' },
{ label: 'High', value: 'high' },
];
export function CreateTaskScreen() {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [priority, setPriority] = useState('medium');
const [notify, setNotify] = useState(true);
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
className="flex-1"
>
<ScrollView className="flex-1 px-4 pt-4" keyboardShouldPersistTaps="handled">
<View className="gap-4">
<View className="gap-1.5">
<Label>Title</Label>
<Input
value={title}
onChangeText={setTitle}
placeholder="Task title"
/>
</View>
<View className="gap-1.5">
<Label>Description</Label>
<Textarea
value={description}
onChangeText={setDescription}
placeholder="What needs to be done?"
numberOfLines={4}
/>
</View>
<View className="gap-1.5">
<Label>Priority</Label>
<Select
value={priority}
onValueChange={setPriority}
options={priorities}
/>
</View>
<View className="flex-row items-center justify-between">
<Label>Send notification</Label>
<Switch checked={notify} onCheckedChange={setNotify} />
</View>
</View>
</ScrollView>
<View className="border-t border-border px-4 py-3">
<Button onPress={handleSubmit} disabled={!title}>
Create Task
</Button>
</View>
</KeyboardAvoidingView>
);
}Dashboard with Stats and Charts
A mobile dashboard combining stats cards, charts, and quick actions:
import { ScrollView, View } from 'react-native';
import { AwesomeStats } from '@/components/docyrus-native/awesome-stats';
import { AwesomeCard } from '@/components/docyrus-native/awesome-card';
import { Button } from '@/components/docyrus-native/button';
import { Badge } from '@/components/docyrus-native/badge';
import { Separator } from '@/components/docyrus-native/separator';
export function DashboardScreen() {
return (
<ScrollView className="flex-1 bg-background">
{/* Stats row */}
<View className="px-4 pt-4">
<AwesomeStats
stats={[
{ label: 'Revenue', value: '$12.4K', change: 8.2 },
{ label: 'Orders', value: '284', change: -2.1 },
{ label: 'Users', value: '1.2K', change: 15.3 },
]}
layout="horizontal"
/>
</View>
<Separator className="my-4" />
{/* Quick actions */}
<View className="flex-row gap-3 px-4">
<Button variant="outline" size="sm" className="flex-1">
New Order
</Button>
<Button variant="outline" size="sm" className="flex-1">
Add Product
</Button>
<Button variant="outline" size="sm" className="flex-1">
Reports
</Button>
</View>
{/* Recent activity */}
<View className="mt-4 px-4">
<AwesomeCard title="Recent Activity">
{recentItems.map((item) => (
<View key={item.id} className="flex-row items-center gap-3 py-2">
<Badge variant={item.type === 'sale' ? 'default' : 'secondary'} size="sm">
{item.type}
</Badge>
<View className="flex-1">
<Text className="text-sm text-foreground">{item.title}</Text>
<Text className="text-xs text-muted-foreground">{item.time}</Text>
</View>
</View>
))}
</AwesomeCard>
</View>
</ScrollView>
);
}Data Table with Actions
A native data table with sorting, row actions, and delete confirmation:
import { View } from 'react-native';
import { DataTable } from '@/components/docyrus-native/data-table';
import { DeleteConfirmDialog } from '@/components/docyrus-native/delete-confirm-dialog';
import { ActionSheet } from '@/components/docyrus-native/action-sheet';
import { Toast } from '@/components/docyrus-native/toast';
export function ProductsScreen() {
const [deleteId, setDeleteId] = useState<string | null>(null);
const [actionItem, setActionItem] = useState<Product | null>(null);
const columns = [
{ key: 'name', title: 'Product', sortable: true },
{ key: 'price', title: 'Price', sortable: true, align: 'right' },
{ key: 'stock', title: 'Stock', sortable: true, align: 'right' },
];
return (
<View className="flex-1">
<DataTable
data={products}
columns={columns}
onRowPress={(row) => router.push(`/product/${row.id}`)}
onRowLongPress={setActionItem}
/>
<ActionSheet
open={!!actionItem}
onOpenChange={() => setActionItem(null)}
title={actionItem?.name}
actions={[
{ label: 'Edit', onPress: () => router.push(`/product/${actionItem?.id}/edit`) },
{ label: 'Duplicate', onPress: () => duplicateProduct(actionItem?.id) },
{ label: 'Delete', onPress: () => setDeleteId(actionItem?.id ?? null), destructive: true },
]}
/>
<DeleteConfirmDialog
open={!!deleteId}
onOpenChange={() => setDeleteId(null)}
onConfirm={async () => {
await deleteProduct(deleteId);
Toast.show({ title: 'Product deleted', variant: 'success' });
setDeleteId(null);
}}
/>
</View>
);
}Tab Navigation with Stepper
Multi-step wizard flow using tabs and a stepper component:
import { useState } from 'react';
import { View, ScrollView } from 'react-native';
import { Tabs } from '@/components/docyrus-native/tabs';
import { Stepper } from '@/components/docyrus-native/stepper';
import { Button } from '@/components/docyrus-native/button';
import { Input } from '@/components/docyrus-native/input';
import { Label } from '@/components/docyrus-native/label';
const steps = [
{ title: 'Account', description: 'Basic info' },
{ title: 'Profile', description: 'Your details' },
{ title: 'Review', description: 'Confirm' },
];
export function OnboardingScreen() {
const [step, setStep] = useState(0);
return (
<View className="flex-1 bg-background">
<View className="px-4 pt-4">
<Stepper steps={steps} currentStep={step} />
</View>
<ScrollView className="flex-1 px-4 pt-6">
{step === 0 && (
<View className="gap-4">
<View className="gap-1.5">
<Label>Email</Label>
<Input placeholder="you@example.com" keyboardType="email-address" />
</View>
<View className="gap-1.5">
<Label>Password</Label>
<Input placeholder="Create a password" secureTextEntry />
</View>
</View>
)}
{step === 1 && (
<View className="gap-4">
<View className="gap-1.5">
<Label>Full Name</Label>
<Input placeholder="Your name" />
</View>
<View className="gap-1.5">
<Label>Company</Label>
<Input placeholder="Company name" />
</View>
</View>
)}
{step === 2 && (
<View className="rounded-lg border border-border bg-card p-4">
{/* Review summary */}
</View>
)}
</ScrollView>
<View className="flex-row gap-3 border-t border-border px-4 py-3">
{step > 0 && (
<Button variant="outline" className="flex-1" onPress={() => setStep(step - 1)}>
Back
</Button>
)}
<Button
className="flex-1"
onPress={() => (step < 2 ? setStep(step + 1) : handleComplete())}
>
{step < 2 ? 'Next' : 'Complete'}
</Button>
</View>
</View>
);
}Related
- Installation — Set up Docyrus UI Native in your Expo project
- Components — Browse all native components
- Theming — Customize the native theme