Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

@ddedic/expo-fancy-ota-updates

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ddedic/expo-fancy-ota-updates

Highly customizable OTA update UI components for Expo apps. Includes animated banners, info screens, and full theming support.

latest
Source
npmnpm
Version
1.5.0
Version published
Weekly downloads
9
-25%
Maintainers
1
Weekly downloads
 
Created
Source

@ddedic/expo-fancy-ota-updates

A highly customizable OTA (Over-The-Air) update UI package for Expo apps. Includes animated banners, info screens, and full theming support.

npm version License: MIT Documentation

📚 Full Documentation | 📦 npm Package | 💻 GitHub


🏆 Proudly Used In

Photo Trim

Photo Trim — Smart Photo Library Cleaner

See exactly what's eating your storage — and fix it in seconds

Photo Trim gives you complete visibility into your photo library clutter. Discover large files, old media, and forgotten videos buried in both your device and iCloud. Smart grouping and powerful filters make it effortless to identify and delete unnecessary photos and videos — reclaiming gigabytes of storage with just a few taps.


📊
Storage Insights
See what's cluttering device & iCloud
🔍
Sort by Size
Find large photos & videos instantly
☁️
iCloud vs Device
Clear visibility of where files live
📅
Filter by Date
Target old, forgotten media
🗑️
Batch Delete
Clean up hundreds in one tap
🔒
Privacy First
All analysis stays on your device

🌐 Visit photo-trim.com   ·   📱 Download on App Store



✨ Features

UI Components

  • 🎨 Fully Customizable — Theme colors, gradients, border radius, and animations
  • 🌍 i18n Ready — Pass your own translations or use English defaults
  • Animated Banner — Beautiful gradient banner with pulse animation
  • 🧱 Gradient Fallback — Uses solid background automatically when expo-linear-gradient is not installed
  • 📱 Info Screen — Full debug/info screen with changelog display
  • 🔌 Drop-in Ready — Works out of the box with sensible defaults
  • 🎯 Render Props — Override any component with your own implementation
  • 🎨 Icon Support — Uses lucide-react-native with text fallbacks
  • 📡 Channel Surfing — Switch update channels at runtime (Expo SDK 54+)

CLI Publishing Tool

  • 🚀 Easy Publishing — Simple ota-publish command to publish OTA updates
  • ⚙️ Configurable — Customize via ota-updates.config.js
  • 📊 Multiple Version Strategies — Semver, build number, or date-based
  • 📝 Smart Changelog — Auto-generate from git, manual input, file, or custom hook
  • 🧩 Per-Channel Templates — Channel-specific version/message formats
  • ✂️ Short Channel Aliases — Use {channelAlias} for compact versions like 1.1.6-p42
  • 🎯 Interactive Mode — Guided prompts for easy publishing
  • 🔍 Dry Run — Preview changes before publishing
  • 🪝 Hooks System — Run custom logic and override changelog/message/version
  • 📦 Multi-Channel — Support for dev, preview, production channels
  • ↩️ Revert Command — Safely republish a previous update to roll back a channel
  • Promote Command — Copy an update group from one channel to another

📸 Screenshots

UI Components in Action

Update Banner
Animated Update Banner
Beautiful gradient with pulse animation
Download Progress
Download Progress
Real-time download status
OTA Info Screen
OTA Info Screen
Full debug & version details

CLI Publishing Tool

CLI Tool

Interactive CLI — Publish OTA updates with guided prompts, version tracking, and changelog generation

📦 Installation

# Using pnpm
pnpm add @ddedic/expo-fancy-ota-updates

# Using npm
npm install @ddedic/expo-fancy-ota-updates

# Using yarn
yarn add @ddedic/expo-fancy-ota-updates

Peer Dependencies

The following dependencies are required:

pnpm add expo expo-updates expo-device react react-native react-native-reanimated react-native-safe-area-context

Optional dependencies for enhanced visuals:

# For gradient banners (optional; falls back to solid View if missing)
pnpm add expo-linear-gradient

# For beautiful icons (recommended)
pnpm add lucide-react-native react-native-svg

🚀 Quick Start

1. Create Version File

Create an ota-version.json file in your project root (this gets updated by your OTA publishing workflow):

{
  "version": "1.0.0",
  "buildNumber": 1,
  "releaseDate": "2026-01-01T00:00:00.000Z",
  "changelog": ["Initial release"]
}

2. Wrap Your App

// App.tsx or _layout.tsx
import { 
  OTAUpdatesProvider, 
  UpdateBanner 
} from '@ddedic/expo-fancy-ota-updates';
import versionData from './ota-version.json';

export default function App() {
  return (
    <OTAUpdatesProvider config={{ versionData }}>
      {/* Banner auto-shows when update is available */}
      <UpdateBanner />
      
      {/* Your app content */}
      <YourApp />
    </OTAUpdatesProvider>
  );
}

That's it! The banner will automatically appear when an OTA update is detected.

📚 Documentation

📖 View Full Documentation →

Comprehensive documentation with guides, examples, and API reference:

🧪 Expo Showcase Demo

Want to feel the package before integrating it?

Run the full demo app from this repo:

pnpm install
pnpm --dir examples/demo start

It demonstrates:

  • dynamic provider controls (minCheckIntervalMs, recordSkippedChecks, foreground checks)
  • runtime channel switching ("channel surfing") with interactive UI
  • default and custom banner renderers
  • OTAInfoScreen in modal mode
  • OTAInfoScreen in tab mode with native-header coordination
  • iOS native-tabs compatibility fallback strategy
  • live telemetry via useOTAUpdates()
Live Demo
Live Demo
Update banner, status, and lifecycle controls
Info Screen
Info Screen
Version details, changelog, and update actions
Settings
Settings
Customization, mode toggle, and channel surfing

📖 Demo Docs: Expo Showcase Demo

📡 Channel Surfing

Switch the update channel your app pulls from at runtime — perfect for letting QA/stakeholders preview updates from staging or preview channels without rebuilding.

Requires Expo SDK 54+ (Updates.setUpdateRequestHeadersOverride()).

import { useOTAUpdates } from '@ddedic/expo-fancy-ota-updates';

function ChannelPicker() {
  const { channel, isSwitchingChannel, switchChannel } = useOTAUpdates();

  const handleSwitch = async (name: string) => {
    const result = await switchChannel(name);
    if (result.isSkipped) {
      console.log('Skipped:', result.reason);
    } else if (result.success) {
      console.log(`Switched from ${result.previousChannel} to ${result.newChannel}`);
    }
  };

  return (
    <View>
      <Text>Current channel: {channel ?? 'N/A'}</Text>
      {isSwitchingChannel && <ActivityIndicator />}
      <Button title="Production" onPress={() => handleSwitch('production')} />
      <Button title="Staging" onPress={() => handleSwitch('staging')} />
      <Button title="Preview" onPress={() => handleSwitch('preview')} />
    </View>
  );
}

The method follows the same guard pattern as checkForUpdate — it skips gracefully in DEV mode, simulators, and when updates are disabled, returning a SwitchChannelResult with the skip reason.

🚀 CLI Publishing Tool

This package includes a powerful CLI for publishing OTA updates with version tracking.

Quick Start

In your project (recommended):

# When installed in your project, just use:
npx ota-publish --channel production

Global usage:

# If running outside a project, specify the package:
npx -p @ddedic/expo-fancy-ota-updates ota-publish --channel production

# Or install globally:
pnpm add -g @ddedic/expo-fancy-ota-updates
ota-publish --channel production

Using in Your App

After publishing to npm:

npx ota-publish --channel development

For local development (before npm publish):

Add to your package.json:

{
  "scripts": {
    "ota:dev": "node node_modules/@ddedic/expo-fancy-ota-updates/bin/ota-publish.js --channel development",
    "ota:preview": "node node_modules/@ddedic/expo-fancy-ota-updates/bin/ota-publish.js --channel preview",
    "ota:prod": "node node_modules/@ddedic/expo-fancy-ota-updates/bin/ota-publish.js --channel production"
  }
}

Then run: npm run ota:dev, npm run ota:preview, or npm run ota:prod

Release Management Commands

# Revert a channel to a previous update group (interactive picker)
npx ota-publish revert --channel production

# Promote from preview to production (interactive picker)
npx ota-publish promote --from preview --to production

# Safe preview first
npx ota-publish revert --channel production --dry-run
npx ota-publish promote --from preview --to production --dry-run

Shorter Production Version Example

If 1.1.6-production.1 is too long, use channel aliasing:

// ota-updates.config.mjs
export default {
  channelAliases: { production: 'p' },
  versionFormatByChannel: {
    production: '{major}.{minor}.{patch}-p{build}',
  },
};

Output: 1.1.6-p1

📖 Full CLI Documentation →

📖 API Reference

<OTAUpdatesProvider>

The main provider that enables OTA update functionality throughout your app.

Props

PropTypeDescription
childrenReactNodeYour app content
themePartial<OTATheme>Custom theme (merged with defaults)
translationsPartial<OTATranslations>Custom translations (merged with defaults)
configOTAConfigProvider behavior configuration

Config Options

interface OTAConfig {
  checkOnMount?: boolean;      // Check for updates on mount (default: true)
  checkOnForeground?: boolean; // Check when app comes to foreground (default: true)
  minCheckIntervalMs?: number; // Throttle checks (default: 0)
  recordSkippedChecks?: boolean; // Update lastCheck + reason for skipped checks (default: true)
  autoDownload?: boolean;      // Auto-download when available (default: false)
  autoReload?: boolean;        // Auto-reload after download (default: false)
  versionData?: OTAVersionData; // Version info from ota-version.json
  debug?: boolean;             // Enable debug logging (default: __DEV__)
}

Full Example

<OTAUpdatesProvider
  theme={{
    colors: {
      primary: '#6366F1',
      primaryLight: '#818CF8',
      background: '#0B0B0F',
      text: '#FFFFFF',
    },
    bannerGradient: ['#6366F1', '#818CF8'],
    borderRadius: 16,
  }}
  translations={{
    banner: {
      updateAvailable: 'New Update Available!',
      updateButton: 'Update Now',
    },
  }}
  config={{
    checkOnMount: true,
    checkOnForeground: true,
    minCheckIntervalMs: 30000,
    recordSkippedChecks: true,
    autoDownload: false,
    versionData: require('./ota-version.json'),
  }}
>
  {children}
</OTAUpdatesProvider>

useOTAUpdates() Hook

Access OTA update state and actions from any component within the provider.

import { useOTAUpdates } from '@ddedic/expo-fancy-ota-updates';

function MyComponent() {
  const {
    // State
    isUpdateAvailable,  // boolean - Update is available to download
    isDownloading,      // boolean - Currently downloading
    isDownloaded,       // boolean - Download complete, ready to apply
    status,             // 'idle' | 'checking' | 'available' | 'downloading' | 'downloaded' | 'error'
    checkError,         // Error | null
    downloadError,      // Error | null
    lastCheck,          // Date | null
    lastSkippedReason,  // string | null (DEV/simulator/throttled/disabled)
    
    // expo-updates Metadata
    currentUpdateId,    // string | null
    channel,            // string | null
    runtimeVersion,     // string | null
    isEmbeddedUpdate,   // boolean

    // Channel Switching
    isSwitchingChannel, // boolean - Channel switch in progress
    switchChannel,      // (channel: string) => Promise<SwitchChannelResult>

    // Version Data
    otaVersion,         // string - e.g., "1.0.0-production.29"
    otaBuildNumber,     // number
    otaReleaseDate,     // string - ISO date
    otaChangelog,       // string[]

    // Actions
    checkForUpdate,     // () => Promise<CheckResult>
    downloadUpdate,     // () => Promise<void>
    reloadApp,          // () => Promise<void>
    simulateUpdate,     // () => void - For testing

    // Theming
    theme,              // OTATheme
    translations,       // OTATranslations
  } = useOTAUpdates();
}

<UpdateBanner>

Animated banner that appears when updates are available.

Props

PropTypeDescription
styleobjectCustom container style
visiblebooleanControlled visibility mode
onDismiss() => voidCalled when banner is dismissed
renderBanner(props) => ReactNodeCustom render function

Basic Usage

// Auto-shows when update available
<UpdateBanner />

Controlled Mode

const [showBanner, setShowBanner] = useState(false);

<UpdateBanner 
  visible={showBanner} 
  onDismiss={() => setShowBanner(false)} 
/>

Custom Render

<UpdateBanner
  renderBanner={({ 
    isDownloaded, 
    isDownloading,
    otaVersion,
    onUpdate, 
    onRestart, 
    onDismiss,
    theme 
  }) => (
    <View style={{ backgroundColor: theme.colors.primary }}>
      <Text>{isDownloaded ? 'Ready!' : `v${otaVersion} available`}</Text>
      <Button 
        title={isDownloaded ? 'Restart' : 'Update'} 
        onPress={isDownloaded ? onRestart : onUpdate} 
      />
      <Button title="Later" onPress={onDismiss} />
    </View>
  )}
/>

<OTAInfoScreen>

Full debug/info screen for OTA updates. Great for settings or debug menus.

Props

PropTypeDescription
mode'developer' | 'user'Screen mode (default: 'developer')
onBack() => voidBack navigation callback
renderHeader(props) => ReactNodeCustom header render
renderInfo(props) => ReactNodeCustom render for Info section
renderActions(props) => ReactNodeCustom render for Actions section
renderChangelog(props) => ReactNodeCustom render for Changelog section
showRuntimeVersionbooleanToggle runtime version visibility
showOtaVersionbooleanToggle OTA version visibility
showReleaseDatebooleanToggle release date visibility
showUpdateIdbooleanToggle update ID visibility
showCheckButtonbooleanToggle "Check for Updates" button
showDownloadButtonbooleanToggle "Download" button
showReloadButtonbooleanToggle "Reload" button
showDebugSectionbooleanToggle debug actions section
styleobjectCustom container style

Usage

import { OTAInfoScreen } from '@ddedic/expo-fancy-ota-updates';

// Simple usage
<OTAInfoScreen 
  mode="user"
  onBack={() => navigation.goBack()} 
/>

// Advanced usage with custom sections
<OTAInfoScreen
  renderInfo={({ theme }) => (
    <View style={{ padding: 20 }}>
      <Text style={{ color: theme.colors.text }}>My Custom Header</Text>
    </View>
  )}
/>

Sub-Components

You can also import sub-components directly if you want to compose your own screen:

import { OTAUpdateInfo, OTAUpdateActions, OTAUpdateChangelog } from '@ddedic/expo-fancy-ota-updates';

🎨 Theming

Theme Structure

interface OTATheme {
  colors: {
    primary: string;           // Main brand color
    primaryLight: string;      // Lighter variant
    background: string;        // Screen background
    backgroundSecondary: string;
    backgroundTertiary: string;
    text: string;              // Primary text
    textSecondary: string;
    textTertiary: string;
    border: string;
    error: string;
    success: string;
    warning: string;
  };
  bannerGradient?: [string, string];  // Gradient start/end
  borderRadius?: number;               // Card border radius
  buttonBorderRadius?: number;         // Button border radius
  animation?: {
    duration?: number;       // Enter/exit animation (ms)
    pulseDuration?: number;  // Pulse animation cycle (ms)
  };
}

Using Built-in Themes

import { 
  OTAUpdatesProvider, 
  defaultTheme,  // Dark indigo theme
  lightTheme,    // Light theme
} from '@ddedic/expo-fancy-ota-updates';

// Use light theme
<OTAUpdatesProvider theme={lightTheme}>

// Or customize from defaults
<OTAUpdatesProvider theme={{
  ...defaultTheme,
  colors: {
    ...defaultTheme.colors,
    primary: '#10B981',
  },
}}>

🌍 Translations

Translation Structure

interface OTATranslations {
  banner: {
    updateAvailable: string;
    updateReady: string;
    downloading: string;
    versionAvailable: string;
    restartToApply: string;
    updateButton: string;
    restartButton: string;
  };
  infoScreen: {
    title: string;
    statusTitle: string;
    embeddedBuild: string;
    otaUpdate: string;
    runtimeVersion: string;
    otaVersion: string;
    releaseDate: string;
    updateId: string;
    channel: string;
    whatsNew: string;
    checkForUpdates: string;
    downloadUpdate: string;
    reloadApp: string;
    debugTitle: string;
    simulateUpdate: string;
    devMode: string;
    notAvailable: string;
    none: string;
  };
}

German Example

<OTAUpdatesProvider
  translations={{
    banner: {
      updateAvailable: 'Neue Version verfügbar',
      updateReady: 'Update bereit',
      downloading: 'Download läuft...',
      versionAvailable: 'Eine neue Version ist verfügbar',
      restartToApply: 'Neustart zum Anwenden',
      updateButton: 'Aktualisieren',
      restartButton: 'Neustart',
    },
    infoScreen: {
      title: 'OTA Updates',
      checkForUpdates: 'Nach Updates suchen',
      // ... more translations
    },
  }}
>

🔧 Advanced Usage

With App Theme Integration

import { useTheme } from './your-theme-context';

function AppProviders({ children }) {
  const { colors, isDark } = useTheme();
  
  return (
    <OTAUpdatesProvider
      theme={{
        colors: {
          primary: colors.primary,
          background: colors.background,
          text: colors.text,
          // Map your theme colors
        },
      }}
    >
      {children}
    </OTAUpdatesProvider>
  );
}

With i18n Library

import { useTranslation } from 'react-i18next';

function AppProviders({ children }) {
  const { t } = useTranslation('ota');
  
  return (
    <OTAUpdatesProvider
      translations={{
        banner: {
          updateAvailable: t('banner.updateAvailable'),
          updateButton: t('banner.updateButton'),
          // ...
        },
      }}
    >
      {children}
    </OTAUpdatesProvider>
  );
}

📄 License

MIT License © 2025-2026 Danijel Dedic, Technabit e.U.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Keywords

expo

FAQs

Package last updated on 11 Feb 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts