npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oxyhq/services

v6.9.0

Published

OxyHQ Expo/React Native SDK — UI components, screens, and native features

Readme

@oxyhq/services

A 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.

Table of Contents

Features

  • 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

Architecture

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.

Installation

npm install @oxyhq/services @oxyhq/core

Peer Dependencies

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:

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

React Native/Expo Setup

For React Native and Expo projects, add the polyfill import at the very top of your entry file:

// 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.

Quick Start

Expo Apps (Native + Web)

import { OxyProvider, useAuth } from '@oxyhq/services';

function App() {
  return (
    <OxyProvider baseURL="https://api.oxy.so">
      <YourApp />
    </OxyProvider>
  );
}

function UserProfile() {
  const { user, isAuthenticated, signIn, signOut } = useAuth();

  if (!isAuthenticated) {
    return <Button onPress={() => signIn()} title="Sign In" />;
  }

  return (
    <View>
      <Text>Welcome, {user?.username}!</Text>
      <Button onPress={signOut} title="Sign Out" />
    </View>
  );
}

OxyProvider handles iOS, Android, and Expo web. Always use OxyProvider in Expo apps.

Typography - Inter Font

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.

Automatic Loading

If you are using OxyProvider, fonts are loaded automatically. No additional setup needed:

import { OxyProvider } from '@oxyhq/services';

function App() {
  return (
    <OxyProvider baseURL="https://api.oxy.so">
      <YourAppContent />
    </OxyProvider>
  );
}

Using Font Constants

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,
  },
});

Available Exports

  • 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.

Usage Patterns

1. OxyProvider + useAuth Hook (Recommended)

OxyProvider works on all platforms (iOS, Android, Web). Use useAuth for authentication.

import { OxyProvider, useAuth, OxySignInButton } from '@oxyhq/services';

// App.tsx - Setup the provider
function App() {
  return (
    <OxyProvider
      baseURL="https://api.oxy.so"
      onAuthStateChange={(user) => {
        console.log('Auth state changed:', user ? 'logged in' : 'logged out');
      }}
    >
      <YourApp />
    </OxyProvider>
  );
}

// Component.tsx - Use the hook
function UserProfile() {
  const { user, isAuthenticated, signIn, signOut, isLoading } = useAuth();

  if (isLoading) return <ActivityIndicator />;

  if (!isAuthenticated) {
    return (
      <View>
        <Text>Welcome! Please sign in.</Text>
        <OxySignInButton />
        {/* Or use signIn() directly: */}
        <Button onPress={() => signIn()} title="Sign In" />
      </View>
    );
  }

  return (
    <View>
      <Text style={styles.title}>Welcome, {user?.username}!</Text>
      <Button onPress={signOut} title="Sign Out" />
    </View>
  );
}

2. Direct Import (Non-React Files)

For utility functions, services, or non-React Native files:

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);
  }
};

3. Mixed Usage (Hooks + Direct Client)

You can use both hooks and the direct client in the same Expo app:

// App.tsx - React Native setup
import { OxyProvider } from '@oxyhq/services';

function App() {
  return (
    <OxyProvider baseURL="https://cloud.oxy.so">
      <YourApp />
    </OxyProvider>
  );
}

// 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
}

API Reference

Core Exports (from @oxyhq/core)

import {
  OxyServices,           // Main service class
  oxyClient,            // Pre-configured instance
  OXY_CLOUD_URL,        // Default API URL
  OxyAuthenticationError,
  OxyAuthenticationTimeoutError,
  KeyManager,
  SignatureService,
  RecoveryPhraseService
} from '@oxyhq/core';

React Native Exports (from @oxyhq/services)

import {
  OxyProvider,          // Context provider
  useOxy,              // React Native hook
  useAuth,             // Auth hook
  OxySignInButton,     // UI components
  Avatar,
  FollowButton
} from '@oxyhq/services';

OxyServices Methods

// 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

useOxy Hook

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();

Configuration

OxyProvider Props

<OxyProvider
  baseURL="https://api.oxy.so"           // API base URL
  storageKeyPrefix="oxy_session"          // Storage key prefix
  onAuthStateChange={(user) => {}}        // Auth state callback
  onError={(error) => {}}                 // Error callback
>
  {children}
</OxyProvider>

Environment Variables

# .env
EXPO_PUBLIC_API_URL=https://api.oxy.so

Custom Configuration

import { OxyServices } from '@oxyhq/core';

const oxy = new OxyServices({
  baseURL: process.env.OXY_API_URL || 'https://cloud.oxy.so'
});

Authentication

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.

Public Key Authentication

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();
}

Password Authentication (Web Gateway)

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:

import { oxyClient } from '@oxyhq/core';

const session = await oxyClient.signUp('username', '[email protected]', 'password');
const session2 = await oxyClient.signIn('username-or-email', 'password');

Cross-App Authentication (Sign in with Oxy)

For third-party apps that want to allow users to sign in with their Oxy identity:

import { OxySignInButton } from '@oxyhq/services';

function LoginScreen() {
  return <OxySignInButton variant="contained" />;
}

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.

Documentation

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.

UI Components

Built-in Components

import {
  OxySignInButton,
  Avatar,
  FollowButton,
  OxyLogo
} from '@oxyhq/services';

function MyComponent() {
  return (
    <View style={styles.container}>
      <OxyLogo />
      <Avatar userId="user123" size={40} />
      <FollowButton userId="user123" />
      <OxySignInButton />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
  },
});

Bottom Sheet Routing System

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:

import { useOxy } from '@oxyhq/services';

function MyComponent() {
  const { showBottomSheet } = useOxy();

  return (
    <Button
      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, AccountCenter
  • Profile, SessionManagement, PaymentGateway
  • And many more...

Documentation: For complete documentation, see Bottom Sheet Routing Guide.

Using OxyRouter Standalone

If you need to embed the router in your own modal or container instead of using showBottomSheet():

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 (
    <Modal visible onRequestClose={onRequestClose} animationType="slide">
      <OxyRouter
        oxyServices={oxyServices}
        initialScreen="OxyAuth"
        onClose={onRequestClose}
        onAuthenticated={onRequestClose}
        theme="light"
        containerWidth={360}
      />
    </Modal>
  );
}

Internationalization (i18n)

OxyHQ Services includes built-in language selection and storage. The selected language can be accessed and synced with your app's i18n system.

Getting Current Language

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 <Text>Current language: {currentLanguageName}</Text>;
}

Syncing with Your i18n Library

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.

Using OxyServices (Non-React)

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();

Language Utilities

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: '...', ... }

Troubleshooting

Common Issues

1. "useOxy must be used within an OxyContextProvider"

Solution: Wrap your app with OxyProvider

import { OxyProvider } from '@oxyhq/services';

function App() {
  return (
    <OxyProvider baseURL="https://cloud.oxy.so">
      <YourApp />
    </OxyProvider>
  );
}

2. FormData Issues in React Native/Expo

Solution: Add polyfill import at the very top of your entry file

// 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

<OxyProvider
  baseURL="https://api.oxy.so"
  storageKeyPrefix="my_app_oxy"  // Custom storage key
>
  {children}
</OxyProvider>

Error Handling

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);
  }
}

Requirements

  • React Native: 0.76+ (for mobile components)
  • Expo: 54+ (recommended)
  • TypeScript: 4.0+ (optional but recommended)

Peer Dependencies

For React Native/Expo projects:

npm install jwt-decode invariant

Note: react-native-url-polyfill is already included as a dependency in this package.

Examples

Complete React Native App

// App.tsx
import { OxyProvider } from '@oxyhq/services';

function App() {
  return (
    <OxyProvider baseURL="https://cloud.oxy.so">
      <UserDashboard />
    </OxyProvider>
  );
}

// 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 <Text>Please sign in</Text>;
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Welcome, {user?.name}!</Text>
      <Text>Followers: {followers.length}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    justifyContent: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 16,
  },
});

Documentation

Comprehensive documentation is available in the /docs directory:


License

This project is licensed under the MIT License. See the LICENSE file for details.