---
title: "Capacitor App Updates: The Complete Guide"
description: Capacitor app updates made easy by combining the App Update and Live Update plugins to deliver both native store updates and OTA updates.
date:
  created: 2026-03-06
  updated: 2026-07-17
authors:
  - robingenz
categories:
  - Capacitor
  - Guides
  - SDKs
links:
  - App Update: sdks/capacitor/app-update.md
  - Live Update: sdks/capacitor/live-update.md
faq: true
---

# Capacitor App Updates: The Complete Guide

Keeping your users on the latest version of your app isn't just nice to have — it's critical for security, performance, and delivering the best experience.
In this guide, you'll learn how to build a complete update delivery strategy for your Capacitor app by combining two plugins: the [Capacitor App Update plugin](../../sdks/capacitor/app-update.md) for native app store updates and the [Capacitor Live Update plugin](../../sdks/capacitor/live-update.md) for Over-The-Air (OTA) updates.

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

## Understanding the Two Types of Updates

Before diving in, it helps to understand the two fundamentally different ways you can update a Capacitor app:

1. **Native App Updates** — Full binary updates distributed through the Google Play Store or Apple App Store. These are required whenever you make changes to native code, add or remove plugins, or update native dependencies. Users typically have to download the new version from the store.

2. **Over-The-Air (OTA) Updates** — Also known as live updates, these let you push changes to the web layer of your app (HTML, CSS, and JavaScript) directly to your users' devices. No app store review, no waiting for users to manually update.

The best update strategy uses both: native updates for binary changes, and OTA updates for everything else. This gives you the speed and flexibility of live updates while still being able to ship native changes when needed.

## Checking for Native App Updates

The [Capacitor App Update plugin](../../sdks/capacitor/app-update.md) lets you check whether a newer version of your app is available in the app store and prompt users to update.
This is especially useful for critical updates or when you need users to move off an older native version.

To install the plugin, refer to the [Installation](../../sdks/capacitor/app-update.md#installation) section in the plugin documentation.

### Getting Update Information

Use [`getAppUpdateInfo(...)`](../../sdks/capacitor/app-update.md#getappupdateinfo) to check whether an update is available:

```typescript
import { AppUpdate, AppUpdateAvailability } from '@capawesome/capacitor-app-update';

const checkForUpdate = async () => {
  const info = await AppUpdate.getAppUpdateInfo();
  if (info.updateAvailability === AppUpdateAvailability.UPDATE_AVAILABLE) {
    // An update is available
  }
};
```

This method returns details like the current and available version, update priority, and whether immediate or flexible updates are allowed on Android.

### Android: In-App Updates

On Android, the App Update plugin supports Google Play's in-app update flows, giving you two options.

#### Immediate Updates

An immediate update displays a full-screen experience that blocks the app until the update is installed. Use this for critical updates that users shouldn't skip — typically the release you ship right after your crash reporting surfaces a severe bug in production (if you don't have that wired up yet, see [Crash Reporting in a Capacitor App with Crashlytics](./capacitor-firebase-crashlytics-guide.md)):

```typescript
import { AppUpdate, AppUpdateAvailability } from '@capawesome/capacitor-app-update';

const performImmediateUpdate = async () => {
  const info = await AppUpdate.getAppUpdateInfo();
  if (info.updateAvailability !== AppUpdateAvailability.UPDATE_AVAILABLE) {
    return;
  }
  if (info.immediateUpdateAllowed) {
    await AppUpdate.performImmediateUpdate();
  }
};
```

#### Flexible Updates

A flexible update downloads the new version in the background while the user continues using the app. Once downloaded, you can prompt the user to restart:

```typescript
import { AppUpdate, AppUpdateAvailability } from '@capawesome/capacitor-app-update';

const startFlexibleUpdate = async () => {
  const info = await AppUpdate.getAppUpdateInfo();
  if (info.updateAvailability !== AppUpdateAvailability.UPDATE_AVAILABLE) {
    return;
  }
  if (info.flexibleUpdateAllowed) {
    await AppUpdate.startFlexibleUpdate();
  }
};

const completeFlexibleUpdate = async () => {
  await AppUpdate.completeFlexibleUpdate();
};
```

You can monitor the download progress using the [`addListener('onFlexibleUpdateStateChange', ...)`](../../sdks/capacitor/app-update.md#addlisteneronflexibleupdatestatechange) method.

### iOS: Opening the App Store

iOS doesn't support in-app update flows like Android. Instead, you can check for an available update and direct users to the App Store using [`openAppStore(...)`](../../sdks/capacitor/app-update.md#openappstore):

```typescript
import { AppUpdate, AppUpdateAvailability } from '@capawesome/capacitor-app-update';

const promptUpdate = async () => {
  const info = await AppUpdate.getAppUpdateInfo();
  if (info.updateAvailability === AppUpdateAvailability.UPDATE_AVAILABLE) {
    await AppUpdate.openAppStore();
  }
};
```

You might want to show a dialog before opening the App Store to let users know why the update is recommended.

## Delivering OTA Updates with Live Update

While native updates handle binary changes, most of your day-to-day updates — bug fixes, UI tweaks, new features in the web layer — can be delivered instantly using the [Capacitor Live Update plugin](../../sdks/capacitor/live-update.md).

To install the plugin, refer to the [Installation](../../sdks/capacitor/live-update.md#installation) section in the plugin documentation.

### What Are Live Updates?

Live updates let you push changes to your app's web assets (HTML, CSS, JavaScript) directly to users' devices without going through the app store. When your app launches, it checks for new bundles, downloads them, and applies them on the next restart.

Keep in mind that live updates only work for web layer changes. If you add a new Capacitor plugin, update native dependencies, or modify native code, you need a full native update through the store.

### Configuration

The Live Update plugin supports a background auto-update strategy that handles everything for you. Configure it in your `capacitor.config.json`:

```json
{
  "plugins": {
    "LiveUpdate": {
      "appId": "your-capawesome-cloud-app-id",
      "autoUpdateStrategy": "background",
      "readyTimeout": 10000,
      "autoBlockRolledBackBundles": true
    }
  }
}
```

Here's what each option does:

- **`autoUpdateStrategy`** — Setting this to `"background"` tells the plugin to automatically check for, download, and stage the latest bundle at app startup and whenever the app resumes after more than 15 minutes. The update is applied on the next app restart.
- **`readyTimeout`** — Gives your app 10 seconds (10000 ms) to signal that it loaded successfully. If the app doesn't call [`ready()`](../../sdks/capacitor/live-update.md#ready) within this time, the plugin automatically rolls back to the built-in bundle that shipped with the installed native app version — the one bundle guaranteed to run on that binary. This is your safety net against broken updates.
- **`autoBlockRolledBackBundles`** — When set to `true`, the plugin automatically blocks bundles that caused a rollback. This prevents the same broken update from being downloaded and applied again.

### Versioned Channels

When you release a new native version of your app, the live update bundles from the previous native version might not be compatible. Versioned channels solve this by automatically tying each native version to its own update channel.

Instead of setting a static channel in your Capacitor config, configure it natively so it includes your build version:

**Android** (`android/app/build.gradle`):

```groovy
android {
    defaultConfig {
        resValue "string", "capawesome_live_update_default_channel", "production-" + defaultConfig.versionCode
    }
}
```

**iOS** (`ios/App/App/Info.plist`):

```xml
<key>CapawesomeLiveUpdateDefaultChannel</key>
<string>production-$(CURRENT_PROJECT_VERSION)</string>
```

With this setup, an app with version code `10` automatically receives updates from the `production-10` channel, while version `11` receives updates from `production-11`. This ensures that each native version only gets compatible live updates.

### Signaling App Readiness

When using `readyTimeout`, you need to call [`ready()`](../../sdks/capacitor/live-update.md#ready) early in your app's startup to tell the plugin that the app loaded successfully:

```typescript
import { LiveUpdate } from '@capawesome/capacitor-live-update';

const signalReady = async () => {
  const result = await LiveUpdate.ready();
  if (result.rollback) {
    console.log('App was rolled back to the default bundle.');
  }
};
```

Call this as soon as your app has initialized and is in a working state. If you don't call `ready()` within the configured timeout, the plugin assumes the update is broken and rolls back automatically.

## Putting It All Together

With both plugins in place, your update strategy looks like this:

1. **Live updates run automatically in the background.** The `background` strategy handles checking, downloading, and staging new bundles without any additional code.
2. **At app startup, signal readiness.** Call [`ready()`](../../sdks/capacitor/live-update.md#ready) to confirm the current bundle works and prevent unnecessary rollbacks.
3. **Check for native updates.** Use the App Update plugin to prompt users when a new native version is available in the store.

Here's an example of how this comes together in your app's initialization:

```typescript
import { Capacitor } from '@capacitor/core';
import { AppUpdate, AppUpdateAvailability } from '@capawesome/capacitor-app-update';
import { LiveUpdate } from '@capawesome/capacitor-live-update';

const initializeApp = async () => {
  await LiveUpdate.ready();
  await checkForNativeUpdate();
};

const checkForNativeUpdate = async () => {
  const info = await AppUpdate.getAppUpdateInfo();
  if (info.updateAvailability !== AppUpdateAvailability.UPDATE_AVAILABLE) {
    return;
  }
  if (Capacitor.getPlatform() === 'android') {
    if (info.immediateUpdateAllowed) {
      await AppUpdate.performImmediateUpdate();
    }
  } else {
    await AppUpdate.openAppStore();
  }
};
```

With this setup, your web layer updates are delivered silently through live updates, while native updates are handled through in-app prompts. Your users always get the latest version — whether it requires a store download or not.

## FAQ

### Can Live Updates ship a new Capacitor plugin without an app store review?

No. Live Updates only cover the web layer — HTML, CSS, and JavaScript. Adding or removing a Capacitor plugin, updating a native dependency, or changing native code always requires a full native update through the App Store or Google Play, since those changes live outside what the Live Update plugin can patch.

### What happens if a live update bundle is broken?

The `readyTimeout` setting is the safety net. If your app doesn't call `ready()` within the configured window (10 seconds in the example config), the Live Update plugin assumes the update failed and automatically rolls back to the built-in bundle that shipped with the installed native version — the one bundle guaranteed to run on that binary. With `autoBlockRolledBackBundles` set to `true`, that same broken bundle is also blocked from being downloaded and applied again.

### Do I need to force users to install native updates, or can it be optional?

Both are supported, and the right choice depends on platform and severity. On Android, the App Update plugin offers immediate updates (a full-screen blocking flow for critical fixes) and flexible updates (downloaded in the background, applied on user confirmation). On iOS, there's no in-app update flow — `openAppStore()` sends the user to the App Store, so any "forcing" has to happen through your own in-app messaging rather than a native blocking screen.

### Why would I need versioned channels instead of a single live update channel?

Because a live update bundle built against one native app version isn't guaranteed to be compatible with an older native version still installed on some users' devices. Versioned channels tie each native build (via its version code or `CURRENT_PROJECT_VERSION`) to its own channel automatically, so an app on native version 10 only ever receives bundles built for version 10, even after you've shipped version 11 to new installs.

### Do I have to call `ready()` manually, or does the plugin detect app health on its own?

You have to call it manually. The plugin has no way to know your app initialized successfully on its own — `ready()` is how your code confirms that. Skipping this call inside the `readyTimeout` window is indistinguishable, from the plugin's perspective, from the app actually failing to load, which triggers an unnecessary rollback.

[Try Capawesome Cloud Free](/){ .md-button .md-button--primary }

## Conclusion

By combining the [Capacitor App Update plugin](../../sdks/capacitor/app-update.md) and [Capacitor Live Update plugin](../../sdks/capacitor/live-update.md) plugins, you can build a solid update delivery strategy that covers every scenario. Use live updates for fast, friction-free web layer changes, and the App Update plugin to nudge users toward new native versions when needed.

If you want to learn more about how live updates work under the hood, check out the blog post [How Live Updates for Capacitor Work](./how-live-updates-for-capacitor-work.md).
If you're currently using `server.url` in production and considering Live Updates instead, read [The Right Way to Update Your Capacitor App Remotely](./the-right-way-to-update-your-capacitor-app-remotely.md).
Join the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"} if you have questions or want to connect with the community, and subscribe to the [Capawesome newsletter](/newsletter/){:target="_blank"} to stay updated on the latest news.
