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

@ddedic/expo-fancy-ota-updates

v1.5.0

Published

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

Readme

@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


🌐 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

CLI Publishing 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()

📖 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

| Prop | Type | Description | |------|------|-------------| | children | ReactNode | Your app content | | theme | Partial<OTATheme> | Custom theme (merged with defaults) | | translations | Partial<OTATranslations> | Custom translations (merged with defaults) | | config | OTAConfig | Provider 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

| Prop | Type | Description | |------|------|-------------| | style | object | Custom container style | | visible | boolean | Controlled visibility mode | | onDismiss | () => void | Called when banner is dismissed | | renderBanner | (props) => ReactNode | Custom 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

| Prop | Type | Description | |------|------|-------------| | mode | 'developer' \| 'user' | Screen mode (default: 'developer') | | onBack | () => void | Back navigation callback | | renderHeader | (props) => ReactNode | Custom header render | | renderInfo | (props) => ReactNode | Custom render for Info section | | renderActions | (props) => ReactNode | Custom render for Actions section | | renderChangelog | (props) => ReactNode | Custom render for Changelog section | | showRuntimeVersion | boolean | Toggle runtime version visibility | | showOtaVersion | boolean | Toggle OTA version visibility | | showReleaseDate | boolean | Toggle release date visibility | | showUpdateId | boolean | Toggle update ID visibility | | showCheckButton | boolean | Toggle "Check for Updates" button | | showDownloadButton | boolean | Toggle "Download" button | | showReloadButton | boolean | Toggle "Reload" button | | showDebugSection | boolean | Toggle debug actions section | | style | object | Custom 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.