@pubflow/react-native ​
React Native integration for Flowfull Clients with AsyncStorage, offline support, and native components optimized for mobile.
Expo Support
This package is optimized for Expo and works seamlessly with:
- ✅ Expo (Recommended) - Full support with SecureStore and AsyncStorage
- ✅ Expo Router - File-based routing with Expo
- ✅ React Native CLI - Works with bare React Native projects
Secure Storage: Automatically uses Expo SecureStore when available, with AsyncStorage fallback.
Installation ​
npm install @pubflow/core @pubflow/react-native swr
npm install @react-native-async-storage/async-storageFor Expo projects:
npx expo install @pubflow/core @pubflow/react-native swr @react-native-async-storage/async-storage expo-secure-storeQuick Start ​
import { PubflowProvider, useAuth, useBridgeQuery } from '@pubflow/react-native';
import { SafeAreaView, Text, Button, FlatList } from 'react-native';
export default function App() {
return (
<PubflowProvider url={process.env.EXPO_PUBLIC_BACKEND_URL!}>
<SafeAreaView style={{ flex: 1 }}>
<Dashboard />
</SafeAreaView>
</PubflowProvider>
);
}
function Dashboard() {
const { user, logout } = useAuth();
const { data, error, isLoading } = useBridgeQuery('/api/users');
if (isLoading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
return (
<>
<Text>Welcome, {user?.name}!</Text>
<Button title="Logout" onPress={logout} />
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
</>
);
}Provider ​
PubflowProvider ​
Same API as React, but with mobile-optimized storage.
import { PubflowProvider } from '@pubflow/react-native';
<PubflowProvider
url={process.env.EXPO_PUBLIC_BACKEND_URL!}
instanceId="default"
onAuthError={(error) => console.error('Auth error:', error)}
onSessionExpired={() => {
// Navigate to login screen
navigation.navigate('Login');
}}
>
<App />
</PubflowProvider>Hooks ​
All hooks from @pubflow/react are available with the same API:
useAuth()- Authentication state and methodsuseBridgeQuery()- Data fetching with SWRuseBridgeMutation()- Data mutationsuseBridgeCrud()- Complete CRUD operationsuseBridgeApi()- Direct API access
See @pubflow/react documentation for detailed hook documentation.
Components ​
BridgeView ​
Mobile-optimized data display component.
import { BridgeView } from '@pubflow/react-native';
import { FlatList, Text } from 'react-native';
<BridgeView
endpoint="/api/users"
render={(data) => (
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
)}
loading={<Text>Loading users...</Text>}
error={(error) => <Text>Error: {error.message}</Text>}
/>BridgeList ​
FlatList with automatic data fetching, pull-to-refresh, and infinite scroll.
import { BridgeList } from '@pubflow/react-native';
import { View, Text } from 'react-native';
<BridgeList
endpoint="/api/users"
renderItem={(user) => (
<View style={{ padding: 10 }}>
<Text style={{ fontSize: 18 }}>{user.name}</Text>
<Text style={{ color: '#666' }}>{user.email}</Text>
</View>
)}
emptyMessage="No users found"
pullToRefresh
infiniteScroll
/>Props ​
| Prop | Type | Required | Description |
|---|---|---|---|
endpoint | string | Yes | API endpoint to fetch from |
renderItem | (item: T) => ReactNode | Yes | Render function for each item |
emptyMessage | string | No | Message when list is empty |
pullToRefresh | boolean | No | Enable pull-to-refresh |
infiniteScroll | boolean | No | Enable infinite scroll |
pageSize | number | No | Items per page (default: 20) |
ListHeaderComponent | ReactNode | No | Header component |
ListFooterComponent | ReactNode | No | Footer component |
BridgeForm ​
Form component with native inputs and validation.
import { BridgeForm } from '@pubflow/react-native';
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
});
<BridgeForm
endpoint="/api/users"
method="POST"
schema={userSchema}
fields={[
{ name: 'name', label: 'Name', type: 'text' },
{ name: 'email', label: 'Email', type: 'email', keyboardType: 'email-address' },
]}
onSuccess={(data) => {
console.log('User created:', data);
navigation.goBack();
}}
submitLabel="Create User"
/>OfflineIndicator ​
Display connection status with native styling.
import { OfflineIndicator } from '@pubflow/react-native';
<OfflineIndicator
position="top"
message="You are offline"
style={{ backgroundColor: '#ff0000' }}
textStyle={{ color: '#fff' }}
/>AdvancedFilter ​
Advanced filtering component for mobile.
import { AdvancedFilter } from '@pubflow/react-native';
<AdvancedFilter
fields={[
{ name: 'name', label: 'Name', type: 'text' },
{ name: 'status', label: 'Status', type: 'select', options: ['active', 'inactive'] },
]}
onFilter={(filters) => console.log('Filters:', filters)}
/>Storage ​
SecureStorageAdapter ​
Automatically uses Expo SecureStore for sensitive data with AsyncStorage fallback.
import { SecureStorageAdapter } from '@pubflow/react-native';
const storage = new SecureStorageAdapter();
// Session IDs are stored in SecureStore (encrypted)
await storage.setItem('pubflow_session_id', 'ses_...');
// User data is stored in AsyncStorage (not encrypted)
await storage.setItem('pubflow_user_data', JSON.stringify(user));Storage Keys ​
pubflow_session_id- Session ID (SecureStore)pubflow_user_data- User data (AsyncStorage)pubflow_instance_id- Instance ID (AsyncStorage)
Offline Support ​
React Native package includes built-in offline support:
Offline Queue ​
Mutations are queued when offline and executed when connection is restored.
import { useBridgeMutation } from '@pubflow/react-native';
function CreatePost() {
const { trigger, isMutating } = useBridgeMutation('/api/posts', 'POST');
const handleCreate = async () => {
// This will be queued if offline
await trigger({ title: 'New Post', content: 'Content...' });
};
return <Button title="Create Post" onPress={handleCreate} disabled={isMutating} />;
}Network Detection ​
Automatically detects network status and shows offline indicator.
import { useNetworkStatus } from '@pubflow/react-native';
function MyComponent() {
const { isOnline, isOffline } = useNetworkStatus();
return (
<View>
{isOffline && <Text>You are offline</Text>}
{isOnline && <Text>You are online</Text>}
</View>
);
}Examples ​
Login Screen ​
import { useAuth } from '@pubflow/react-native';
import { useState } from 'react';
import { View, TextInput, Button, Text, StyleSheet } from 'react-native';
export function LoginScreen({ navigation }) {
const { login, isLoading, error } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async () => {
try {
await login({ email: email.toLowerCase(), password });
navigation.replace('Home');
} catch (err) {
console.error('Login failed:', err);
}
};
return (
<View style={styles.container}>
<TextInput
style={styles.input}
value={email}
onChangeText={(text) => setEmail(text.toLowerCase())}
placeholder="Email"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
<TextInput
style={styles.input}
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
/>
{error && <Text style={styles.error}>{error.message}</Text>}
<Button
title={isLoading ? 'Logging in...' : 'Login'}
onPress={handleLogin}
disabled={isLoading}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { padding: 20 },
input: {
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
marginBottom: 10,
borderRadius: 5,
},
error: { color: 'red', marginBottom: 10 },
});User List with Pull-to-Refresh ​
import { BridgeList } from '@pubflow/react-native';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
export function UserListScreen({ navigation }) {
return (
<BridgeList
endpoint="/api/users"
renderItem={(user) => (
<TouchableOpacity
style={styles.item}
onPress={() => navigation.navigate('UserDetail', { userId: user.id })}
>
<Text style={styles.name}>{user.name}</Text>
<Text style={styles.email}>{user.email}</Text>
</TouchableOpacity>
)}
emptyMessage="No users found"
pullToRefresh
infiniteScroll
/>
);
}
const styles = StyleSheet.create({
item: {
padding: 15,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
name: { fontSize: 18, fontWeight: 'bold' },
email: { fontSize: 14, color: '#666', marginTop: 5 },
});Create User Form ​
import { BridgeForm } from '@pubflow/react-native';
import { View } from 'react-native';
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters'),
email: z.string().email('Invalid email address'),
phone: z.string().optional(),
});
export function CreateUserScreen({ navigation }) {
return (
<View style={{ padding: 20 }}>
<BridgeForm
endpoint="/api/users"
method="POST"
schema={userSchema}
fields={[
{ name: 'name', label: 'Name', type: 'text' },
{ name: 'email', label: 'Email', type: 'email', keyboardType: 'email-address' },
{ name: 'phone', label: 'Phone', type: 'text', keyboardType: 'phone-pad' },
]}
onSuccess={(data) => {
console.log('User created:', data);
navigation.goBack();
}}
submitLabel="Create User"
/>
</View>
);
}Environment Variables ​
For Expo projects, use EXPO_PUBLIC_ prefix:
# .env
EXPO_PUBLIC_BACKEND_URL=https://your-backend.com// Usage
<PubflowProvider url={process.env.EXPO_PUBLIC_BACKEND_URL!}>TypeScript Support ​
Full TypeScript support with type inference:
import { useBridgeQuery } from '@pubflow/react-native';
interface User {
id: string;
name: string;
email: string;
}
function UserList() {
const { data } = useBridgeQuery<User[]>('/api/users');
// data is typed as User[] | undefined
}Next Steps ​
- Authentication Guide - Learn about authentication
- Offline Support - Master offline functionality
- Examples - See more examples
- @pubflow/react - React documentation (same hooks API)