OxyHQ Expo/React Native SDK — UI components, screens, and native features
npm install @oxyhq/servicesA comprehensive TypeScript UI library for the Oxy API providing authentication, user management, and UI components for React Native and Expo applications.
> For web apps (Vite, Next.js, CRA): Use @oxyhq/auth for authentication and @oxyhq/core for types and services.
>
> For backend / Node.js: Use @oxyhq/core only.
>
> For the full platform guide: See PLATFORM_GUIDE.md.
- Features
- Installation
- Quick Start
- Typography - Inter Font
- Usage Patterns
- API Reference
- Configuration
- Authentication
- UI Components
- Internationalization (i18n)
- Troubleshooting
- Requirements
- Zero-Config Authentication: Automatic token management and refresh
- Cross-Domain SSO: Sign in once, authenticated everywhere (FedCM, popup, redirect)
- Universal Provider: Single OxyProvider works on iOS, Android, and Expo Web
- UI Components: Pre-built components for auth, profiles, and more
- Inter Font Included: Default Oxy ecosystem font with automatic loading
- Cross-Platform: Works in Expo and React Native (iOS, Android, Web)
- Multi-Session Support: Manage multiple user sessions simultaneously
- TypeScript First: Full type safety and IntelliSense support
- Performance Optimized: Automatic caching with TanStack Query
- Production Ready: Error handling, retry logic, and security best practices
The OxyHQ SDK is split into three packages:
| Package | Use Case | Dependencies |
|---------|----------|--------------|
| @oxyhq/services | Expo / React Native apps | Full (RN, Expo) |
| @oxyhq/auth | Web apps (Vite, Next.js) | React only |
| @oxyhq/core | All platforms (types, API client, crypto) | None |
This package (@oxyhq/services) is for Expo and React Native applications. It provides OxyProvider, UI components, screens, bottom sheet routing, fonts, and hooks.
See PLATFORM_GUIDE.md for the complete architecture guide.
``bash`
npm install @oxyhq/services @oxyhq/core
To avoid duplicate native modules and ensure smooth integration across apps, install (or ensure your app already includes) the following peer dependencies:
- react: >=18, react-native: >=0.76
- react-native-reanimated: >=3.16, react-native-gesture-handler: >=2.16
- react-native-safe-area-context: ^5.4.0, react-native-svg: >=13
- Expo projects: expo, expo-font, expo-image, expo-linear-gradient
- Navigation (if you use the provided screens): @react-navigation/native
Example for Expo:
`bash`
npm i react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-svg \
expo expo-font expo-image expo-linear-gradient @react-navigation/native
For React Native and Expo projects, add the polyfill import at the very top of your entry file:
`javascript`
// index.js or App.js (very first line)
import 'react-native-url-polyfill/auto';
Note: This polyfill is already included in the package dependencies, but you need to import it to activate it.
`typescript
import { OxyProvider, useAuth } from '@oxyhq/services';
function App() {
return (
);
}
function UserProfile() {
const { user, isAuthenticated, signIn, signOut } = useAuth();
if (!isAuthenticated) {
return
return (
);
}
`
OxyProvider handles iOS, Android, and Expo web. Always use OxyProvider in Expo apps.
Inter is the default font for all Oxy ecosystem apps. This package includes the Inter font family and provides automatic font loading for both web and native platforms.
If you are using OxyProvider, fonts are loaded automatically. No additional setup needed:
`typescript
import { OxyProvider } from '@oxyhq/services';
function App() {
return (
);
}
`
`typescript
import { fontFamilies, fontStyles } from '@oxyhq/services';
const styles = StyleSheet.create({
title: {
...fontStyles.titleLarge, // Pre-defined style (54px, Bold)
color: '#000000',
},
customText: {
fontFamily: fontFamilies.interSemiBold, // Cross-platform font family
fontSize: 18,
},
});
`
- setupFonts() - Function to manually load fonts (called automatically by OxyProvider)
- fontFamilies - Object with all Inter weight variants (inter, interLight, interMedium, interSemiBold, interBold, interExtraBold, interBlack)
- fontStyles - Pre-defined text styles (titleLarge, titleMedium, titleSmall, buttonText)
See FONTS.md for the complete typography guide, including detailed usage examples, all available font weights, platform-specific handling, best practices, and migration guide.
OxyProvider works on all platforms (iOS, Android, Web). Use useAuth for authentication.
`typescript
import { OxyProvider, useAuth, OxySignInButton } from '@oxyhq/services';
// App.tsx - Setup the provider
function App() {
return (
onAuthStateChange={(user) => {
console.log('Auth state changed:', user ? 'logged in' : 'logged out');
}}
>
);
}
// Component.tsx - Use the hook
function UserProfile() {
const { user, isAuthenticated, signIn, signOut, isLoading } = useAuth();
if (isLoading) return
if (!isAuthenticated) {
return (
{/ Or use signIn() directly: /}
);
}
return (
);
}
`
For utility functions, services, or non-React Native files:
`typescript
import { oxyClient } from '@oxyhq/core';
// utils/api.ts
export const userUtils = {
async fetchUserById(userId: string) {
return await oxyClient.getUserById(userId);
},
async fetchProfileByUsername(username: string) {
return await oxyClient.getProfileByUsername(username);
},
async updateUserProfile(updates: any) {
return await oxyClient.updateProfile(updates);
}
};
`
You can use both hooks and the direct client in the same Expo app:
`typescript
// App.tsx - React Native setup
import { OxyProvider } from '@oxyhq/services';
function App() {
return (
);
}
// utils/api.ts - Direct import
import { oxyClient } from '@oxyhq/core';
export const apiUtils = {
async fetchData() {
return await oxyClient.getCurrentUser();
}
};
// Component.tsx - React Native hook
import { useOxy } from '@oxyhq/services';
function Component() {
const { oxyServices } = useOxy();
// Both oxyServices and oxyClient share the same tokens
}
`
`typescript`
import {
OxyServices, // Main service class
oxyClient, // Pre-configured instance
OXY_CLOUD_URL, // Default API URL
OxyAuthenticationError,
OxyAuthenticationTimeoutError,
KeyManager,
SignatureService,
RecoveryPhraseService
} from '@oxyhq/core';
`typescript`
import {
OxyProvider, // Context provider
useOxy, // React Native hook
useAuth, // Auth hook
OxySignInButton, // UI components
Avatar,
FollowButton
} from '@oxyhq/services';
`typescript
// Authentication (Public Key Based)
await oxyClient.register(publicKey, username, signature, timestamp, email?);
await oxyClient.requestChallenge(publicKey);
await oxyClient.verifyChallenge(publicKey, challenge, signature, timestamp, deviceName?, deviceFingerprint?);
await oxyClient.checkPublicKeyRegistered(publicKey);
await oxyClient.getUserByPublicKey(publicKey);
// User Management
const user = await oxyClient.getCurrentUser(); // Get current user
const userById = await oxyClient.getUserById('user123'); // Get user by ID
const profileByUsername = await oxyClient.getProfileByUsername('john_doe'); // Get profile by username
await oxyClient.updateProfile({ name: 'John Doe' }); // Update current user
await oxyClient.updateUser('user123', { name: 'John' }); // Update user by ID (admin)
// Session Management
const userBySession = await oxyClient.getUserBySession('session123'); // Get user by session
const sessions = await oxyClient.getSessionsBySessionId('session123'); // Get all sessions
await oxyClient.logoutSession('session123'); // Logout specific session
await oxyClient.logoutAllSessions('session123'); // Logout all sessions
// Social Features
await oxyClient.followUser('user123'); // Follow user
await oxyClient.unfollowUser('user123'); // Unfollow user
const followStatus = await oxyClient.getFollowStatus('user123'); // Check follow status
const followers = await oxyClient.getUserFollowers('user123'); // Get user followers
const following = await oxyClient.getUserFollowing('user123'); // Get user following
// Notifications
const notifications = await oxyClient.getNotifications(); // Get notifications
const unreadCount = await oxyClient.getUnreadCount(); // Get unread count
await oxyClient.markNotificationAsRead('notification123'); // Mark as read
await oxyClient.markAllNotificationsAsRead(); // Mark all as read
await oxyClient.deleteNotification('notification123'); // Delete notification
// File Management
const fileData = await oxyClient.uploadFile(file); // Upload file
const file = await oxyClient.getFile('file123'); // Get file info
await oxyClient.deleteFile('file123'); // Delete file
const downloadUrl = oxyClient.getFileDownloadUrl('file123', 'thumb'); // Get download/stream URL
const userFiles = await oxyClient.listUserFiles('user123'); // List user files
// Payments
const payment = await oxyClient.createPayment(paymentData); // Create payment
const paymentInfo = await oxyClient.getPayment('payment123'); // Get payment info
const userPayments = await oxyClient.getUserPayments(); // Get user payments
// Karma System
const karma = await oxyClient.getUserKarma('user123'); // Get user karma
await oxyClient.giveKarma('user123', 10, 'helpful comment'); // Give karma
const karmaTotal = await oxyClient.getUserKarmaTotal('user123'); // Get karma total
const karmaHistory = await oxyClient.getUserKarmaHistory('user123'); // Get karma history
const leaderboard = await oxyClient.getKarmaLeaderboard(); // Get leaderboard
const rules = await oxyClient.getKarmaRules(); // Get karma rules
// Location Services
await oxyClient.updateLocation(40.7128, -74.0060); // Update location
const nearby = await oxyClient.getNearbyUsers(1000); // Get nearby users
// Analytics
await oxyClient.trackEvent('user_action', { action: 'click' }); // Track event
const analytics = await oxyClient.getAnalytics('2024-01-01', '2024-01-31'); // Get analytics
// Device Management
await oxyClient.registerDevice(deviceData); // Register device
const devices = await oxyClient.getUserDevices(); // Get user devices
await oxyClient.removeDevice('device123'); // Remove device
const deviceSessions = await oxyClient.getDeviceSessions('session123'); // Get device sessions
await oxyClient.logoutAllDeviceSessions('session123'); // Logout device sessions
await oxyClient.updateDeviceName('session123', 'iPhone 15'); // Update device name
// Utilities
const metadata = await oxyClient.fetchLinkMetadata('https://example.com'); // Fetch link metadata
`
`typescript
const {
// Service instance
oxyServices,
// Authentication state
user,
isAuthenticated,
isLoading,
error,
// Identity management (Public Key Authentication)
createIdentity, // Create new identity with recovery phrase
importIdentity, // Import identity from recovery phrase
signIn, // Sign in with stored identity
hasIdentity, // Check if identity exists on device
getPublicKey, // Get stored public key
// Session management
logout,
// Session management
sessions,
activeSessionId,
switchSession,
removeSession
} = useOxy();
`
`typescript`
storageKeyPrefix="oxy_session" // Storage key prefix
onAuthStateChange={(user) => {}} // Auth state callback
onError={(error) => {}} // Error callback
>
{children}
`bash`.env
EXPO_PUBLIC_API_URL=https://api.oxy.so
`typescript
import { OxyServices } from '@oxyhq/core';
const oxy = new OxyServices({
baseURL: process.env.OXY_API_URL || 'https://cloud.oxy.so'
});
`
Oxy supports public/private key cryptography (ECDSA secp256k1) as the primary identity system, with optional password-based accounts for the web gateway. Users manage their cryptographic identity in the Oxy Accounts app, and other apps can integrate "Sign in with Oxy" for seamless authentication.
`typescript
import { useOxy } from '@oxyhq/services';
function AuthScreen() {
const { createIdentity, importIdentity, signIn, hasIdentity } = useOxy();
// Create new identity (in Oxy Accounts app)
const handleCreate = async () => {
const { user, recoveryPhrase } = await createIdentity('username', 'email');
// Show recoveryPhrase to user - they must save it!
};
// Import existing identity
const handleImport = async (phrase: string) => {
const user = await importIdentity(phrase, 'username', 'email');
};
// Sign in with stored identity
const handleSignIn = async () => {
const user = await signIn();
};
// Check if identity exists
const hasStoredIdentity = await hasIdentity();
}
`
For web-only or legacy flows, Oxy also supports password sign-in (email/username + password). Use the auth gateway (/login, /signup, /recover) for browser-based flows, or call the API directly:
`typescript
import { oxyClient } from '@oxyhq/core';
const session = await oxyClient.signUp('username', 'email@example.com', 'password');
const session2 = await oxyClient.signIn('username-or-email', 'password');
`
For third-party apps that want to allow users to sign in with their Oxy identity:
`typescript
import { OxySignInButton } from '@oxyhq/services';
function LoginScreen() {
return
}
`
This displays:
- A QR code that users can scan with Oxy Accounts
- A button to open Oxy Accounts directly via deep link
Web fallback: send users to the auth gateway at https://accounts.oxy.so/authorize?token=... to approve the session.
See Complete Public Key Authentication Guide for architecture, concepts, user flows, developer integration, crypto module API reference, security best practices, and migration from password auth.
`typescript
import {
OxySignInButton,
Avatar,
FollowButton,
OxyLogo
} from '@oxyhq/services';
function MyComponent() {
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
});
`
The bottom sheet routing system provides a clean, professional way to display authentication screens, account management, and other UI flows within a modal bottom sheet.
Quick Example:
`typescript
import { useOxy } from '@oxyhq/services';
function MyComponent() {
const { showBottomSheet } = useOxy();
return (
onPress={() => showBottomSheet('OxyAuth')}
title="Sign in with Oxy"
/>
);
}
`
Features:
- Full navigation history with back button support
- Step-based screen navigation (multi-step flows)
- Keyboard-aware (automatically adjusts for keyboard)
- Dynamic sizing (fits content automatically)
- Type-safe route names
- 25+ pre-built screens available
Available Screens:
- OxyAuth (Sign in with Oxy - for third-party apps)AccountOverview
- , AccountSettings, AccountCenterProfile
- , SessionManagement, PaymentGateway
- And many more...
Documentation:
For complete documentation, see Bottom Sheet Routing Guide.
If you need to embed the router in your own modal or container instead of using showBottomSheet():
`typescript
import { Modal } from 'react-native';
import { useOxy, OxyRouter } from '@oxyhq/services';
function AuthModal({ visible, onRequestClose }: { visible: boolean; onRequestClose: () => void }) {
const { oxyServices } = useOxy();
if (!visible || !oxyServices) return null;
return (
initialScreen="OxyAuth"
onClose={onRequestClose}
onAuthenticated={onRequestClose}
theme="light"
containerWidth={360}
/>
);
}
`
OxyHQ Services includes built-in language selection and storage. The selected language can be accessed and synced with your app's i18n system.
`typescript
import { useOxy } from '@oxyhq/services';
function MyComponent() {
const {
currentLanguage, // 'en-US'
currentLanguageName, // 'English'
currentNativeLanguageName, // 'Espanol' (if Spanish is selected)
currentLanguageMetadata // Full metadata object
} = useOxy();
return
}
`
To integrate with react-i18next, i18n-js, next-intl, or other i18n libraries, see the comprehensive guide:
See the language utilities and useOxy() hook sections above for integration with your i18n system.
`typescript
import { OxyServices } from '@oxyhq/core';
const oxy = new OxyServices({ baseURL: 'https://api.oxy.so' });
// Get current language code
const languageCode = await oxy.getCurrentLanguage();
// Get language name
const languageName = await oxy.getCurrentLanguageName();
// Get full metadata
const metadata = await oxy.getCurrentLanguageMetadata();
`
`typescript
import {
SUPPORTED_LANGUAGES,
getLanguageName,
getLanguageMetadata
} from '@oxyhq/services';
// Get all supported languages
const languages = SUPPORTED_LANGUAGES;
// Get language name from code
const name = getLanguageName('es-ES'); // 'Spanish'
// Get full metadata
const metadata = getLanguageMetadata('es-ES');
// { id: 'es-ES', name: 'Spanish', nativeName: 'Espanol', flag: '...', ... }
`
#### 1. "useOxy must be used within an OxyContextProvider"
Solution: Wrap your app with OxyProvider
`typescript
import { OxyProvider } from '@oxyhq/services';
function App() {
return (
);
}
`
#### 2. FormData Issues in React Native/Expo
Solution: Add polyfill import at the very top of your entry file
`javascript`
// index.js or App.js (very first line)
import 'react-native-url-polyfill/auto';
Why needed: Your app uses file uploads which require FormData. React Native with Hermes engine does not include FormData natively, so it needs to be polyfilled.
#### 3. Authentication Not Persisting
Solution: Check storage configuration
`typescript`
storageKeyPrefix="my_app_oxy" // Custom storage key
>
{children}
`typescript
import { OxyAuthenticationError } from '@oxyhq/core';
try {
await oxyClient.getCurrentUser();
} catch (error) {
if (error instanceof OxyAuthenticationError) {
// Handle authentication errors
console.log('Auth error:', error.message);
} else {
// Handle other errors
console.log('Other error:', error.message);
}
}
`
- React Native: 0.76+ (for mobile components)
- Expo: 54+ (recommended)
- TypeScript: 4.0+ (optional but recommended)
For React Native/Expo projects:
`bash`
npm install jwt-decode invariant
Note: react-native-url-polyfill is already included as a dependency in this package.
`typescript
// App.tsx
import { OxyProvider } from '@oxyhq/services';
function App() {
return (
);
}
// UserDashboard.tsx
import { useOxy } from '@oxyhq/services';
import { View, Text, StyleSheet } from 'react-native';
function UserDashboard() {
const { user, isAuthenticated, oxyServices } = useOxy();
const [followers, setFollowers] = useState([]);
useEffect(() => {
if (isAuthenticated && user) {
oxyServices.getUserFollowers(user.id).then(setFollowers);
}
}, [isAuthenticated, user]);
if (!isAuthenticated) {
return
}
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
justifyContent: 'center',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
});
`
---
Comprehensive documentation is available in the /docs` directory:
- Getting Started - Quick start guide for new developers
- Platform Guide - Platform-specific setup guide
- CROSS_DOMAIN_AUTH.md - SSO deep dive
---
This project is licensed under the MIT License. See the LICENSE file for details.