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>
  );
}
  • Installation — Set up Docyrus UI Native in your Expo project
  • Components — Browse all native components
  • Theming — Customize the native theme

On this page