---
description: Capacitor LLM plugin to run on-device AI models on Android and iOS, powered by Gemini Nano and Apple Intelligence, with chat and streaming APIs.
title: Capacitor LLM Plugin for Android & iOS - Capawesome
image: https://capawesome.io/docs/assets/images/social/sdks/capacitor/llm.png
---

<!doctype html> 

[Skip to content ](#capacitor-llm-plugin) 

[📲 Introducing **Build Sharing** — get your builds onto testers' devices with a link & QR code. No account required. ](/blog/share-mobile-app-builds-with-testers/) 

* [ SDKs ](/docs/sdks/)
* [ Formbricks ](/docs/sdks/capacitor/formbricks/)
* [ Geocoder ](/docs/sdks/capacitor/geocoder/)
* [ Geofences ](/docs/sdks/capacitor/geofences/)
* [ Google Sign-In ](/docs/sdks/capacitor/google-sign-in/)
* [ Grafana Faro ](/docs/sdks/capacitor/grafana-faro/)
* [ Gyroscope ](/docs/sdks/capacitor/gyroscope/)
* [ Haptics ](/docs/sdks/capacitor/haptics/)
* [ Health ](/docs/sdks/capacitor/health/)
* [ Home Indicator ](/docs/sdks/capacitor/home-indicator/)
* [ In-App Browser ](/docs/sdks/capacitor/in-app-browser/)
* [ Install Referrer ](/docs/sdks/capacitor/install-referrer/)
* [ Intercom ](/docs/sdks/capacitor/intercom/)
* [ Intune ](/docs/sdks/capacitor/intune/)
* [ Keep Awake ](/docs/sdks/capacitor/keep-awake/)
* [ libSQL ](/docs/sdks/capacitor/libsql/)
* [ Light Sensor ](/docs/sdks/capacitor/light-sensor/)
* [ Live Update ](/docs/sdks/capacitor/live-update/)
* LLM [ LLM ](/docs/sdks/capacitor/llm/)
* [ iOS ](#ios)
* [ Configuration ](#configuration)
* [ Usage ](#usage)
* [ API ](#api)
* [ Type Aliases ](#type-aliases)
* [ FAQ ](#faq)
* [ Related Plugins ](#related-plugins)
* [ Newsletter ](#newsletter)
* [ Changelog ](#changelog)
* [ Breaking Changes ](#breaking-changes)
* [ License ](#license)
* [ Localization ](/docs/sdks/capacitor/localization/)
* [ Mail Composer ](/docs/sdks/capacitor/mail-composer/)
* [ Managed Configurations ](/docs/sdks/capacitor/managed-configurations/)
* [ MapLibre ](/docs/sdks/capacitor/maplibre/)
* [ Maps Launcher ](/docs/sdks/capacitor/maps-launcher/)
* [ Media Session ](/docs/sdks/capacitor/media-session/)
* [ ML Kit ](/docs/sdks/capacitor/mlkit/)
* [ Navigation Bar ](/docs/sdks/capacitor/navigation-bar/)
* [ Network ](/docs/sdks/capacitor/network/)
* [ NFC ](/docs/sdks/capacitor/nfc/)
* [ Node.js ](/docs/sdks/capacitor/nodejs/)
* [ OAuth ](/docs/sdks/capacitor/oauth/)
* [ Passkeys ](/docs/sdks/capacitor/passkeys/)
* [ Password Autofill ](/docs/sdks/capacitor/password-autofill/)
* [ PDF Generator ](/docs/sdks/capacitor/pdf-generator/)
* [ PDF Viewer ](/docs/sdks/capacitor/pdf-viewer/)
* [ Pedometer ](/docs/sdks/capacitor/pedometer/)
* [ Permissions ](/docs/sdks/capacitor/permissions/)
* [ Phone Dialer ](/docs/sdks/capacitor/phone-dialer/)
* [ Photo Editor ](/docs/sdks/capacitor/photo-editor/)
* [ Photo Manipulator ](/docs/sdks/capacitor/photo-manipulator/)
* [ PixLive ](/docs/sdks/capacitor/pixlive/)
* [ PostHog ](/docs/sdks/capacitor/posthog/)
* [ Printer ](/docs/sdks/capacitor/printer/)
* [ Privacy Screen ](/docs/sdks/capacitor/privacy-screen/)
* [ Proximity Sensor ](/docs/sdks/capacitor/proximity-sensor/)
* [ Purchases ](/docs/sdks/capacitor/purchases/)
* [ RealtimeKit ](/docs/sdks/capacitor/realtimekit/)
* [ Root Detection ](/docs/sdks/capacitor/root-detection/)
* [ Screen Brightness ](/docs/sdks/capacitor/screen-brightness/)
* [ Screen Orientation ](/docs/sdks/capacitor/screen-orientation/)
* [ Screen Reader ](/docs/sdks/capacitor/screen-reader/)
* [ Screenshot ](/docs/sdks/capacitor/screenshot/)
* [ Secure Preferences ](/docs/sdks/capacitor/secure-preferences/)
* [ Settings Launcher ](/docs/sdks/capacitor/settings-launcher/)
* [ Shake ](/docs/sdks/capacitor/shake/)
* [ Silent Mode ](/docs/sdks/capacitor/silent-mode/)
* [ SIM ](/docs/sdks/capacitor/sim/)
* [ SMS Composer ](/docs/sdks/capacitor/sms-composer/)
* [ Speech Recognition ](/docs/sdks/capacitor/speech-recognition/)
* [ Speech Synthesis ](/docs/sdks/capacitor/speech-synthesis/)
* [ Share Target ](/docs/sdks/capacitor/share-target/)
* [ Square Mobile Payments ](/docs/sdks/capacitor/square-mobile-payments/)
* [ SQLite ](/docs/sdks/capacitor/sqlite/)
* [ Superwall ](/docs/sdks/capacitor/superwall/)
* [ System WebView ](/docs/sdks/capacitor/system-webview/)
* [ Tauri ](/docs/sdks/capacitor/tauri/)
* [ Text Interaction ](/docs/sdks/capacitor/text-interaction/)
* [ Text Zoom ](/docs/sdks/capacitor/text-zoom/)
* [ Thermal State ](/docs/sdks/capacitor/thermal-state/)
* [ Toast ](/docs/sdks/capacitor/toast/)
* [ Torch ](/docs/sdks/capacitor/torch/)
* [ Vault ](/docs/sdks/capacitor/vault/)
* [ Volume ](/docs/sdks/capacitor/volume/)
* [ Wallet ](/docs/sdks/capacitor/wallet/)
* [ Watch ](/docs/sdks/capacitor/watch/)
* [ Wifi ](/docs/sdks/capacitor/wifi/)
* [ YouTube Player ](/docs/sdks/capacitor/youtube-player/)
* [ Zip ](/docs/sdks/capacitor/zip/)
* [ Cordova ](/docs/sdks/cordova/)
* [ Cloud ](/docs/cloud/)
* [ Integrations ](/docs/cloud/live-updates/integrations/)
* Concepts
* Reference
* [ Troubleshooting ](/docs/cloud/live-updates/troubleshooting/)
* [ FAQ ](/docs/cloud/live-updates/faq/)
* [ Native Builds ](/docs/cloud/native-builds/)
* [ Set Up Environments ](/docs/cloud/native-builds/environments/)
* [ Set Up Native Configurations ](/docs/cloud/native-builds/native-configurations/)
* [ Auto-Increment Build Numbers ](/docs/cloud/native-builds/auto-incrementing-build-numbers/)
* [ Configure the Web Build Script ](/docs/cloud/native-builds/web-build-script/)
* [ Build from a Monorepo ](/docs/cloud/native-builds/monorepo/)
* [ Use pnpm, Yarn, or bun ](/docs/cloud/native-builds/package-managers/)
* [ Install Private npm Packages ](/docs/cloud/native-builds/npm-private-registry/)
* [ Override the Java Version ](/docs/cloud/native-builds/override-java-version/)
* [ Custom iOS Provisioning Profiles ](/docs/cloud/native-builds/custom-ios-provisioning-profiles/)
* [ Build without Git ](/docs/cloud/native-builds/build-without-git/)
* [ Access Git Behind a Firewall ](/docs/cloud/native-builds/firewall-access/)
* [ Integrations ](/docs/cloud/native-builds/integrations/)
* Reference
* [ Troubleshooting ](/docs/cloud/native-builds/troubleshooting/)
* [ FAQ ](/docs/cloud/native-builds/faq/)
* [ App Store Publishing ](/docs/cloud/app-store-publishing/)
* [ Submit a Build ](/docs/cloud/app-store-publishing/submit-a-build/)
* [ Submit Automatically After a Build ](/docs/cloud/app-store-publishing/submit-automatically/)
* [ Troubleshooting ](/docs/cloud/app-store-publishing/troubleshooting/)
* [ FAQ ](/docs/cloud/app-store-publishing/faq/)
* [ Automations ](/docs/cloud/automations/)
* [ Reference ](/docs/cloud/automations/reference/)
* [ Troubleshooting ](/docs/cloud/automations/troubleshooting/)
* [ FAQ ](/docs/cloud/automations/faq/)
* [ Assist ](/docs/cloud/assist/)
* [ CLI ](/docs/cloud/cli/)
* APIs and SDKs
* [ Webhooks ](/docs/cloud/webhooks/)
* [ Integrations ](/docs/cloud/integrations/)
* Notifications
* Account
* [ Organization ](/docs/cloud/organizations/)
* [ Two-Factor Enforcement ](/docs/cloud/organizations/two-factor-authentication/)
* [ Network Restrictions ](/docs/cloud/organizations/network-restrictions/)
* [ Audit Logs ](/docs/cloud/organizations/audit-logs/)
* [ Billing ](/docs/cloud/organizations/billing/)
* [ License Keys ](/docs/cloud/license-keys/)
* [ AI ](/docs/ai/)
* [ Insiders ](/docs/insiders/)
* [ Billing & Plans ](/docs/insiders/billing-and-plans/)
* [ FAQ ](/docs/insiders/faq/)
* [ License ](https://capawesome.io/legal/eula/)
* [ Support ](/docs/support/)
* [ Contributing ](/docs/contributing/)
* Contributing code
* [ Code of Conduct ](/docs/contributing/code-of-conduct/)
* [ Questions ](https://docs.github.com/en/discussions/collaborating-with-your-community-using-discussions/participating-in-a-discussion#creating-a-discussion)
* [ Blog ](/blog/)
* Categories

* [ iOS ](#ios)
* [ Configuration ](#configuration)
* [ Usage ](#usage)
* [ API ](#api)
* [ Type Aliases ](#type-aliases)
* [ FAQ ](#faq)
* [ Related Plugins ](#related-plugins)
* [ Newsletter ](#newsletter)
* [ Changelog ](#changelog)
* [ Breaking Changes ](#breaking-changes)
* [ License ](#license)

Build and Ship Mobile Apps Faster

Cloud builds, OTA live updates, and automated store releases — everything your mobile team needs in one platform.

[Start for free ](https://console.cloud.capawesome.io/?utm%5Fsource=docs&utm%5Fmedium=sidebar%5Fcta&utm%5Fcampaign=docs%5Fcta) [See our plans ](https://capawesome.io/pricing/?utm%5Fsource=docs&utm%5Fmedium=sidebar%5Fcta&utm%5Fcampaign=docs%5Fcta) 

# Capacitor LLM Plugin[¶](#capacitor-llm-plugin "Permanent link")

Capacitor plugin for running on-device large language models (LLMs) on Android and iOS. Uses the system-provided models Apple Intelligence (Foundation Models) and Gemini Nano (AICore) with chat sessions, token streaming and cancellation.

[ ![Deliver Live Updates to your Capacitor app with Capawesome Cloud](../../../assets/external/cloud.capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.69628c3f.png) ](https://cloud.capawesome.io/) 

## Features[¶](#features "Permanent link")

The Capacitor LLM plugin brings the platform's own on-device AI models to your Capacitor app. Here are some of the key features:

* 🧠 **System Models**: Uses the models that ship with the operating system (Apple Intelligence on iOS, Gemini Nano on Android). No model files to bundle, no API keys, no cloud calls.
* 🔒 **Private by Design**: All inference runs on the device. Prompts and responses never leave the user's phone.
* 💬 **Chat Sessions**: Create chats with instructions (system prompt) that keep the conversation context across multiple generations.
* 🌊 **Token Streaming**: Stream the response chunk by chunk via events for a responsive UI.
* ✋ **Cancellation**: Cancel an in-flight generation at any time.
* 🚦 **Typed Availability**: Check the model availability with typed status values and get notified about changes.
* ⤵️ **Model Download**: Trigger the Gemini Nano model download on Android with progress events.
* 🔓 **Public APIs Only**: Built exclusively on public platform APIs, so it is safe for App Review and resilient to OS updates.
* 🤝 **Compatibility**: Works hand in hand with the [Speech Recognition](https://capawesome.io/docs/sdks/capacitor/speech-recognition/) and [Speech Synthesis](https://capawesome.io/docs/sdks/capacitor/speech-synthesis/) plugins.
* 📦 **CocoaPods & SPM**: Supports CocoaPods and Swift Package Manager for iOS.
* 🔁 **Up-to-date**: Always supports the latest Capacitor version.
* ⭐️ **Support**: Priority support from the Capawesome Team.
* ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated.

Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look!

## Use Cases[¶](#use-cases "Permanent link")

The LLM plugin is typically used whenever an app needs AI text generation without sending data to a server, for example:

* **Summarization**: Summarize notes, articles or messages on the device.
* **Smart Replies**: Suggest replies for chats and emails.
* **Rewriting**: Rephrase, shorten or proofread user-written text.
* **Offline Assistants**: Build chat assistants that work without an internet connection.

## Compatibility[¶](#compatibility "Permanent link")

| Plugin Version | Capacitor Version | Status         |
| -------------- | ----------------- | -------------- |
| 0.x.x          | \>=8.x.x          | Active support |

## Demo[¶](#demo "Permanent link")

| Android | iOS |
| ------- | --- |

## Installation[¶](#installation "Permanent link")

This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands:

`[](#%5F%5Fcodelineno-0-1)npm config set @capawesome-team:registry https://npm.registry.capawesome.io
[](#%5F%5Fcodelineno-0-2)npm config set //npm.registry.capawesome.io/:_authToken <YOUR_LICENSE_KEY>
`

**Attention**: Replace `<YOUR_LICENSE_KEY>` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/).

Next, you can use our **AI-Assisted Setup** to install the plugin. Add the [Capawesome Skills](https://github.com/capawesome-team/skills) to your AI tool using the following command:

`[](#%5F%5Fcodelineno-1-1)npx skills add capawesome-team/skills --skill capacitor-plugins
`

Then use the following prompt:

`` [](#%5F%5Fcodelineno-2-1)Use the `capacitor-plugins` skill from `capawesome-team/skills` to install the `@capawesome-team/capacitor-llm` plugin in my project.
 ``

If you prefer **Manual Setup**, install the plugin by running the following commands and follow the platform-specific instructions below:

`[](#%5F%5Fcodelineno-3-1)npm install @capawesome-team/capacitor-llm
[](#%5F%5Fcodelineno-3-2)npx cap sync
`

### Android[¶](#android "Permanent link")

The plugin uses the [ML Kit GenAI Prompt API](https://developers.google.com/ml-kit/genai/prompt/android) to run Gemini Nano via AICore. The SDK is declared as a regular Gradle dependency and fetched from Google's Maven repository when your app is built. The model itself is managed by the system (AICore) and is **not** bundled with your app.

**Attention**:

* Gemini Nano is only available on [Gemini Nano-capable devices](https://developers.google.com/ml-kit/genai#supported%5Fdevices) (for example the Google Pixel 9 series or Samsung Galaxy S25 series) with AICore. The list of supported devices is still short, so always check `getAvailability()` at runtime and design a fallback.
* The ML Kit GenAI Prompt SDK is still in **beta**, so breaking changes in the underlying SDK are possible.
* Apps using Gemini Nano are subject to Google's [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy).

#### Minimum SDK Version[¶](#minimum-sdk-version "Permanent link")

The ML Kit GenAI Prompt SDK requires a minimum SDK version of `26`. Make sure that the `minSdkVersion` in your `android/variables.gradle` file is set to at least `26`:

`[](#%5F%5Fcodelineno-4-1)ext {
[](#%5F%5Fcodelineno-4-2)    minSdkVersion = 26
[](#%5F%5Fcodelineno-4-3)}
`

#### Proguard[¶](#proguard "Permanent link")

If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file:

`[](#%5F%5Fcodelineno-5-1)-keep class io.capawesome.capacitorjs.plugins.** { *; }
`

#### Variables[¶](#variables "Permanent link")

If needed, you can define the following project variables in your app’s `variables.gradle` file to change the default versions of the dependencies:

* `$mlkitGenaiPromptVersion` version of `com.google.mlkit:genai-prompt` (default: `1.0.0-beta2`)
* `$kotlinVersion` version of `org.jetbrains.kotlin:kotlin-gradle-plugin` (default: `2.1.20`)
* `$kotlinxCoroutinesVersion` version of `org.jetbrains.kotlinx:kotlinx-coroutines-android` (default: `1.10.2`)

This can be useful if you encounter dependency conflicts with other plugins in your project.

### iOS[¶](#ios "Permanent link")

The plugin uses the [Foundation Models](https://developer.apple.com/documentation/foundationmodels) framework, which is part of the operating system. No additional dependencies or configuration are required.

**Attention**:

* The Foundation Models framework requires **iOS 26 or later** on an [Apple Intelligence-enabled device](https://www.apple.com/apple-intelligence/) (iPhone 15 Pro or later) with Apple Intelligence turned on. On older iOS versions, all methods except `getAvailability()` reject as unavailable.
* Building the plugin requires **Xcode 26 or later**.

## Configuration[¶](#configuration "Permanent link")

No configuration required for this plugin.

## Usage[¶](#usage "Permanent link")

The following examples show how to check the model availability, download the model, create and delete chats, generate text, stream the response, cancel a generation, and tune the generation parameters.

### Check the model availability[¶](#check-the-model-availability "Permanent link")

The plugin uses the model that ships with the operating system, so its availability depends on the device and OS version. Always call `getAvailability()` before you generate text and handle every status. Listen for the `availabilityChange` event to be notified when the status changes:

| Status              | Meaning                                             | What you can do                                                   |
| ------------------- | --------------------------------------------------- | ----------------------------------------------------------------- |
| available           | The model is ready to use.                          | Start generating.                                                 |
| device-not-eligible | The device hardware does not support the model.     | Offer a fallback (for example a cloud-based or custom model).     |
| downloadable        | The model can be downloaded.                        | Call downloadModel() to start the download.                       |
| downloading         | The model is currently being downloaded.            | Wait and listen for availabilityChange events.                    |
| not-enabled         | The model is disabled on the device.                | Ask the user to enable Apple Intelligence in the system settings. |
| not-ready           | The model is not ready yet.                         | Try again later.                                                  |
| unavailable         | No system model exists on this platform/OS version. | Offer a fallback or hide the feature.                             |

On Android, only `available`, `downloadable`, `downloading` and `unavailable` are reported. On iOS, only `available`, `device-not-eligible`, `not-enabled`, `not-ready` and `unavailable` are reported:

`` [](#%5F%5Fcodelineno-6-1)import { Llm } from '@capawesome-team/capacitor-llm';
[](#%5F%5Fcodelineno-6-2)
[](#%5F%5Fcodelineno-6-3)const getAvailability = async () => {
[](#%5F%5Fcodelineno-6-4)  const { status } = await Llm.getAvailability();
[](#%5F%5Fcodelineno-6-5)  return status;
[](#%5F%5Fcodelineno-6-6)};
[](#%5F%5Fcodelineno-6-7)
[](#%5F%5Fcodelineno-6-8)const addAvailabilityChangeListener = async () => {
[](#%5F%5Fcodelineno-6-9)  await Llm.addListener('availabilityChange', event => {
[](#%5F%5Fcodelineno-6-10)    console.log(`Availability changed: ${event.status}`);
[](#%5F%5Fcodelineno-6-11)  });
[](#%5F%5Fcodelineno-6-12)};
 ``

### Download the model[¶](#download-the-model "Permanent link")

If the availability status is `downloadable`, the model must be downloaded before it can be used. On Android, trigger the download explicitly and follow its progress via the `downloadProgress` event. On iOS, the download is managed by the system and cannot be triggered by the app. Only available on Android:

`` [](#%5F%5Fcodelineno-7-1)import { Llm } from '@capawesome-team/capacitor-llm';
[](#%5F%5Fcodelineno-7-2)
[](#%5F%5Fcodelineno-7-3)const downloadModel = async () => {
[](#%5F%5Fcodelineno-7-4)  const { status } = await Llm.getAvailability();
[](#%5F%5Fcodelineno-7-5)  if (status !== 'downloadable') {
[](#%5F%5Fcodelineno-7-6)    return;
[](#%5F%5Fcodelineno-7-7)  }
[](#%5F%5Fcodelineno-7-8)  await Llm.addListener('downloadProgress', event => {
[](#%5F%5Fcodelineno-7-9)    console.log(`Download progress: ${event.progress * 100}%`);
[](#%5F%5Fcodelineno-7-10)  });
[](#%5F%5Fcodelineno-7-11)  await Llm.downloadModel();
[](#%5F%5Fcodelineno-7-12)};
 ``

### Create and delete a chat[¶](#create-and-delete-a-chat "Permanent link")

A chat keeps the conversation context across multiple generations and can be given instructions (system prompt) that guide the model's responses. Chats live in memory until they are deleted with `deleteChat(...)`, so delete the chats you no longer need to free the associated native resources. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-8-1)import { Llm } from '@capawesome-team/capacitor-llm';
[](#%5F%5Fcodelineno-8-2)
[](#%5F%5Fcodelineno-8-3)const createChat = async () => {
[](#%5F%5Fcodelineno-8-4)  const { id } = await Llm.createChat({
[](#%5F%5Fcodelineno-8-5)    instructions: 'You are a helpful assistant that answers briefly.',
[](#%5F%5Fcodelineno-8-6)  });
[](#%5F%5Fcodelineno-8-7)  return id;
[](#%5F%5Fcodelineno-8-8)};
[](#%5F%5Fcodelineno-8-9)
[](#%5F%5Fcodelineno-8-10)const deleteChat = async (chatId: string) => {
[](#%5F%5Fcodelineno-8-11)  await Llm.deleteChat({ id: chatId });
[](#%5F%5Fcodelineno-8-12)};
`

### Generate text[¶](#generate-text "Permanent link")

Generate a response for a prompt and wait for the complete text. Only one generation can be in flight per chat at a time. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-9-1)import { Llm } from '@capawesome-team/capacitor-llm';
[](#%5F%5Fcodelineno-9-2)
[](#%5F%5Fcodelineno-9-3)const generateText = async (chatId: string) => {
[](#%5F%5Fcodelineno-9-4)  const { text } = await Llm.generateText({
[](#%5F%5Fcodelineno-9-5)    chatId,
[](#%5F%5Fcodelineno-9-6)    prompt: 'Why is the sky blue?',
[](#%5F%5Fcodelineno-9-7)  });
[](#%5F%5Fcodelineno-9-8)  return text;
[](#%5F%5Fcodelineno-9-9)};
`

### Stream the response[¶](#stream-the-response "Permanent link")

Stream the response chunk by chunk via the `textChunk` event for a more responsive UI. The returned promise resolves with the complete response text. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-10-1)import { Llm } from '@capawesome-team/capacitor-llm';
[](#%5F%5Fcodelineno-10-2)
[](#%5F%5Fcodelineno-10-3)const streamText = async (chatId: string) => {
[](#%5F%5Fcodelineno-10-4)  await Llm.addListener('textChunk', event => {
[](#%5F%5Fcodelineno-10-5)    if (event.chatId === chatId) {
[](#%5F%5Fcodelineno-10-6)      console.log(event.text); // Append the chunks to your UI
[](#%5F%5Fcodelineno-10-7)    }
[](#%5F%5Fcodelineno-10-8)  });
[](#%5F%5Fcodelineno-10-9)  const { text } = await Llm.streamText({
[](#%5F%5Fcodelineno-10-10)    chatId,
[](#%5F%5Fcodelineno-10-11)    prompt: 'Tell me a short story about a magical dog.',
[](#%5F%5Fcodelineno-10-12)  });
[](#%5F%5Fcodelineno-10-13)  return text; // The complete response text
[](#%5F%5Fcodelineno-10-14)};
`

### Cancel a generation[¶](#cancel-a-generation "Permanent link")

Cancel the in-flight generation of a chat. The pending promise rejects with the `GENERATION_CANCELED` error code. On iOS, the generation is canceled immediately. On Android, cancellation is best-effort, so a few more chunks may be emitted. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-11-1)import { Llm } from '@capawesome-team/capacitor-llm';
[](#%5F%5Fcodelineno-11-2)
[](#%5F%5Fcodelineno-11-3)const cancelGeneration = async (chatId: string) => {
[](#%5F%5Fcodelineno-11-4)  await Llm.cancelGeneration({ chatId });
[](#%5F%5Fcodelineno-11-5)};
`

### Tune the generation parameters[¶](#tune-the-generation-parameters "Permanent link")

`maxOutputTokens` and `temperature` can be set per chat with `createChat(...)` and overridden per request with `generateText(...)` or `streamText(...)`. Only available on Android and iOS:

`[](#%5F%5Fcodelineno-12-1)import { Llm } from '@capawesome-team/capacitor-llm';
[](#%5F%5Fcodelineno-12-2)
[](#%5F%5Fcodelineno-12-3)const createChatWithParameters = async () => {
[](#%5F%5Fcodelineno-12-4)  const { id } = await Llm.createChat({
[](#%5F%5Fcodelineno-12-5)    maxOutputTokens: 256,
[](#%5F%5Fcodelineno-12-6)    temperature: 0.7,
[](#%5F%5Fcodelineno-12-7)  });
[](#%5F%5Fcodelineno-12-8)  const { text } = await Llm.generateText({
[](#%5F%5Fcodelineno-12-9)    chatId: id,
[](#%5F%5Fcodelineno-12-10)    prompt: 'Write a haiku about the sea.',
[](#%5F%5Fcodelineno-12-11)    temperature: 0.2, // Overrides the chat's default value
[](#%5F%5Fcodelineno-12-12)  });
[](#%5F%5Fcodelineno-12-13)  return text;
[](#%5F%5Fcodelineno-12-14)};
`

The platforms enforce different limits on the generation parameters:

| Parameter       | Android (Gemini Nano)                | iOS (Apple Intelligence)                      |
| --------------- | ------------------------------------ | --------------------------------------------- |
| maxOutputTokens | Limited to a maximum of 4096 tokens. | No documented hard limit.                     |
| temperature     | Must be between 0.0 and 1.0.         | Values greater than 1.0 are allowed.          |
| Context size    | Input must be under \~4,000 tokens.  | Context window of \~4,096 tokens per session. |

If the context limit of a chat is exceeded, the generation rejects with the `GENERATION_FAILED` error code. In that case, create a new chat.

## API[¶](#api "Permanent link")

* [cancelGeneration(...)](#cancelgeneration)
* [createChat(...)](#createchat)
* [deleteChat(...)](#deletechat)
* [downloadModel()](#downloadmodel)
* [generateText(...)](#generatetext)
* [getAvailability()](#getavailability)
* [streamText(...)](#streamtext)
* [addListener('availabilityChange', ...)](#addlisteneravailabilitychange-)
* [addListener('downloadProgress', ...)](#addlistenerdownloadprogress-)
* [addListener('textChunk', ...)](#addlistenertextchunk-)
* [removeAllListeners()](#removealllisteners)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)

### cancelGeneration(...)[¶](#cancelgeneration "Permanent link")

`[](#%5F%5Fcodelineno-13-1)cancelGeneration(options: CancelGenerationOptions) => Promise<void>
`

Cancel the in-flight text generation of a chat.

The pending `generateText(...)` or `streamText(...)` promise rejects with the `GENERATION_CANCELED` error code.

On **Android**, cancellation is best-effort. The generation is interrupted as soon as possible but a few more chunks may be emitted.

Only available on Android and iOS.

| Param       | Type                                                |
| ----------- | --------------------------------------------------- |
| **options** | [CancelGenerationOptions](#cancelgenerationoptions) |

**Since:** 0.0.1

---

### createChat(...)[¶](#createchat "Permanent link")

`[](#%5F%5Fcodelineno-14-1)createChat(options?: CreateChatOptions | undefined) => Promise<CreateChatResult>
`

Create a new chat session.

A chat keeps the conversation context across multiple generations until it is deleted with `deleteChat(...)`.

If a chat with the provided identifier already exists, the promise rejects with the `CHAT_ALREADY_EXISTS` error code.

On **iOS**, each chat is backed by a native language model session that maintains the conversation context.

On **Android**, the conversation history is kept in memory by the plugin and included in each prompt since the system API does not provide native multi-turn sessions.

Only available on Android and iOS.

| Param       | Type                                    |
| ----------- | --------------------------------------- |
| **options** | [CreateChatOptions](#createchatoptions) |

**Returns:** `Promise<[CreateChatResult](#createchatresult)>`

**Since:** 0.0.1

---

### deleteChat(...)[¶](#deletechat "Permanent link")

`[](#%5F%5Fcodelineno-15-1)deleteChat(options: DeleteChatOptions) => Promise<void>
`

Delete a chat session and free the associated native resources.

An in-flight generation of the chat is canceled.

Only available on Android and iOS.

| Param       | Type                                    |
| ----------- | --------------------------------------- |
| **options** | [DeleteChatOptions](#deletechatoptions) |

**Since:** 0.0.1

---

### downloadModel()[¶](#downloadmodel "Permanent link")

`[](#%5F%5Fcodelineno-16-1)downloadModel() => Promise<void>
`

Download the on-device model.

Call this method if `getAvailability()` returns the `downloadable`status. The returned promise resolves when the download is complete. The download progress is emitted via the `downloadProgress` event.

Only available on Android.

**Since:** 0.0.1

---

### generateText(...)[¶](#generatetext "Permanent link")

`[](#%5F%5Fcodelineno-17-1)generateText(options: GenerateTextOptions) => Promise<GenerateTextResult>
`

Generate text for a prompt and resolve with the complete response.

Only one generation can be in flight per chat at a time. Starting another one rejects with the `GENERATION_IN_PROGRESS` error code.

Only available on Android and iOS.

| Param       | Type                                        |
| ----------- | ------------------------------------------- |
| **options** | [GenerateTextOptions](#generatetextoptions) |

**Returns:** `Promise<[GenerateTextResult](#generatetextresult)>`

**Since:** 0.0.1

---

### getAvailability()[¶](#getavailability "Permanent link")

`[](#%5F%5Fcodelineno-18-1)getAvailability() => Promise<GetAvailabilityResult>
`

Get the availability status of the on-device model.

This method never rejects. On platforms or OS versions without a system model it resolves with the `unavailable` status.

**Returns:** `Promise<[GetAvailabilityResult](#getavailabilityresult)>`

**Since:** 0.0.1

---

### streamText(...)[¶](#streamtext "Permanent link")

`[](#%5F%5Fcodelineno-19-1)streamText(options: StreamTextOptions) => Promise<StreamTextResult>
`

Generate text for a prompt and stream the response.

Incremental chunks are emitted via the `textChunk` event while the generation is running. The returned promise resolves with the complete response text when the generation is finished.

Only one generation can be in flight per chat at a time. Starting another one rejects with the `GENERATION_IN_PROGRESS` error code.

Only available on Android and iOS.

| Param       | Type                                    |
| ----------- | --------------------------------------- |
| **options** | [StreamTextOptions](#streamtextoptions) |

**Returns:** `Promise<[StreamTextResult](#streamtextresult)>`

**Since:** 0.0.1

---

### addListener('availabilityChange', ...)[¶](#addlisteneravailabilitychange "Permanent link")

`[](#%5F%5Fcodelineno-20-1)addListener(eventName: 'availabilityChange', listenerFunc: (event: AvailabilityChangeEvent) => void) => Promise<PluginListenerHandle>
`

Called when the availability status of the on-device model changes.

The plugin watches the availability status only while at least one listener for this event is attached.

Only available on Android and iOS.

| Param            | Type                                                                 |
| ---------------- | -------------------------------------------------------------------- |
| **eventName**    | 'availabilityChange'                                                 |
| **listenerFunc** | (event: [AvailabilityChangeEvent](#availabilitychangeevent)) => void |

**Returns:** `Promise<[PluginListenerHandle](#pluginlistenerhandle)>`

**Since:** 0.0.1

---

### addListener('downloadProgress', ...)[¶](#addlistenerdownloadprogress "Permanent link")

`[](#%5F%5Fcodelineno-21-1)addListener(eventName: 'downloadProgress', listenerFunc: (event: DownloadProgressEvent) => void) => Promise<PluginListenerHandle>
`

Called while the on-device model is being downloaded.

Only available on Android.

| Param            | Type                                                             |
| ---------------- | ---------------------------------------------------------------- |
| **eventName**    | 'downloadProgress'                                               |
| **listenerFunc** | (event: [DownloadProgressEvent](#downloadprogressevent)) => void |

**Returns:** `Promise<[PluginListenerHandle](#pluginlistenerhandle)>`

**Since:** 0.0.1

---

### addListener('textChunk', ...)[¶](#addlistenertextchunk "Permanent link")

`[](#%5F%5Fcodelineno-22-1)addListener(eventName: 'textChunk', listenerFunc: (event: TextChunkEvent) => void) => Promise<PluginListenerHandle>
`

Called when a new text chunk is generated during a streaming generation started with `streamText(...)`.

Only available on Android and iOS.

| Param            | Type                                               |
| ---------------- | -------------------------------------------------- |
| **eventName**    | 'textChunk'                                        |
| **listenerFunc** | (event: [TextChunkEvent](#textchunkevent)) => void |

**Returns:** `Promise<[PluginListenerHandle](#pluginlistenerhandle)>`

**Since:** 0.0.1

---

### removeAllListeners()[¶](#removealllisteners "Permanent link")

`[](#%5F%5Fcodelineno-23-1)removeAllListeners() => Promise<void>
`

Remove all listeners for this plugin.

**Since:** 0.0.1

---

### Interfaces[¶](#interfaces "Permanent link")

#### CancelGenerationOptions[¶](#cancelgenerationoptions "Permanent link")

| Prop       | Type   | Description                                                               | Since |
| ---------- | ------ | ------------------------------------------------------------------------- | ----- |
| **chatId** | string | The identifier of the chat whose in-flight generation should be canceled. | 0.0.1 |

#### CreateChatResult[¶](#createchatresult "Permanent link")

| Prop   | Type   | Description                                | Since |
| ------ | ------ | ------------------------------------------ | ----- |
| **id** | string | The unique identifier of the created chat. | 0.0.1 |

#### CreateChatOptions[¶](#createchatoptions "Permanent link")

| Prop                | Type   | Description                                                                                                                                                                                                                                      | Since |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- |
| **id**              | string | The unique identifier of the chat. If not provided, a random UUID is generated.                                                                                                                                                                  | 0.0.1 |
| **instructions**    | string | The instructions (system prompt) that guide the model's responses in this chat.                                                                                                                                                                  | 0.0.1 |
| **maxOutputTokens** | number | The default maximum number of tokens the model may generate per response in this chat. Can be overridden per request. On **Android**, the value is limited to a maximum of 4096 tokens.                                                          | 0.0.1 |
| **temperature**     | number | The default sampling temperature for responses in this chat. Higher values produce more creative results, lower values produce more deterministic results. Can be overridden per request. On **Android**, the value must be between 0.0 and 1.0. | 0.0.1 |

#### DeleteChatOptions[¶](#deletechatoptions "Permanent link")

| Prop   | Type   | Description                           | Since |
| ------ | ------ | ------------------------------------- | ----- |
| **id** | string | The identifier of the chat to delete. | 0.0.1 |

#### GenerateTextResult[¶](#generatetextresult "Permanent link")

| Prop     | Type   | Description         | Since |
| -------- | ------ | ------------------- | ----- |
| **text** | string | The generated text. | 0.0.1 |

#### GenerateTextOptions[¶](#generatetextoptions "Permanent link")

| Prop                | Type   | Description                                                                                                                                                                 | Since |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **chatId**          | string | The identifier of the chat to generate the text in.                                                                                                                         | 0.0.1 |
| **maxOutputTokens** | number | The maximum number of tokens the model may generate for this request. Overrides the chat's default value. On **Android**, the value is limited to a maximum of 4096 tokens. | 0.0.1 |
| **prompt**          | string | The prompt to generate a response for.                                                                                                                                      | 0.0.1 |
| **temperature**     | number | The sampling temperature for this request. Overrides the chat's default value. On **Android**, the value must be between 0.0 and 1.0.                                       | 0.0.1 |

#### GetAvailabilityResult[¶](#getavailabilityresult "Permanent link")

| Prop       | Type                                      | Description                                     | Since |
| ---------- | ----------------------------------------- | ----------------------------------------------- | ----- |
| **status** | [AvailabilityStatus](#availabilitystatus) | The availability status of the on-device model. | 0.0.1 |

#### StreamTextResult[¶](#streamtextresult "Permanent link")

| Prop     | Type   | Description                  | Since |
| -------- | ------ | ---------------------------- | ----- |
| **text** | string | The complete generated text. | 0.0.1 |

#### StreamTextOptions[¶](#streamtextoptions "Permanent link")

| Prop                | Type   | Description                                                                                                                                                                 | Since |
| ------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **chatId**          | string | The identifier of the chat to generate the text in.                                                                                                                         | 0.0.1 |
| **maxOutputTokens** | number | The maximum number of tokens the model may generate for this request. Overrides the chat's default value. On **Android**, the value is limited to a maximum of 4096 tokens. | 0.0.1 |
| **prompt**          | string | The prompt to generate a response for.                                                                                                                                      | 0.0.1 |
| **temperature**     | number | The sampling temperature for this request. Overrides the chat's default value. On **Android**, the value must be between 0.0 and 1.0.                                       | 0.0.1 |

#### PluginListenerHandle[¶](#pluginlistenerhandle "Permanent link")

| Prop       | Type                |
| ---------- | ------------------- |
| **remove** | () => Promise<void> |

#### AvailabilityChangeEvent[¶](#availabilitychangeevent "Permanent link")

| Prop       | Type                                      | Description                                         | Since |
| ---------- | ----------------------------------------- | --------------------------------------------------- | ----- |
| **status** | [AvailabilityStatus](#availabilitystatus) | The new availability status of the on-device model. | 0.0.1 |

#### DownloadProgressEvent[¶](#downloadprogressevent "Permanent link")

| Prop         | Type   | Description                                       | Since |
| ------------ | ------ | ------------------------------------------------- | ----- |
| **progress** | number | The download progress as a value between 0 and 1. | 0.0.1 |

#### TextChunkEvent[¶](#textchunkevent "Permanent link")

| Prop       | Type   | Description                                                                                                            | Since |
| ---------- | ------ | ---------------------------------------------------------------------------------------------------------------------- | ----- |
| **chatId** | string | The identifier of the chat the chunk belongs to.                                                                       | 0.0.1 |
| **text**   | string | The newly generated text chunk. Append the chunks in the order they are received to reconstruct the complete response. | 0.0.1 |

### Type Aliases[¶](#type-aliases "Permanent link")

#### AvailabilityStatus[¶](#availabilitystatus "Permanent link")

The availability status of the on-device model.

* `available`: The model is downloaded and ready to use.
* `device-not-eligible`: The device does not support the on-device model.
* `downloadable`: The model can be downloaded. On Android, call `downloadModel()` to trigger the download.
* `downloading`: The model is currently being downloaded.
* `not-enabled`: The on-device model is disabled. On iOS, the user must enable Apple Intelligence in the system settings.
* `not-ready`: The model is not ready yet, for example because the system is still preparing it. Try again later.
* `unavailable`: The model is not available on this platform or OS version.

`'available' | 'device-not-eligible' | 'downloadable' | 'downloading' | 'not-enabled' | 'not-ready' | 'unavailable'`

## FAQ[¶](#faq "Permanent link")

### How is this plugin different from other similar plugins?[¶](#how-is-this-plugin-different-from-other-similar-plugins "Permanent link")

It runs entirely on the platform's own on-device models — Apple Intelligence on iOS and Gemini Nano on Android — so prompts and responses never leave the device and there are no model files to bundle, API keys or cloud calls. You get chat sessions that keep context, token streaming, cancellation and typed availability checks through one fully typed API that is actively maintained against the latest OS and Capacitor versions, and it's backed by dedicated support. If you just need occasional cloud completions, a plain HTTP call may be enough; if you want private, offline text generation with a complete on-device workflow, this plugin is built for exactly that.

### Is text generation available on the web?[¶](#is-text-generation-available-on-the-web "Permanent link")

No. Browsers do not provide a system language model, so all methods except `getAvailability()` reject with an unimplemented error on the web. `getAvailability()` resolves with the `unavailable` status.

### Which devices support on-device text generation?[¶](#which-devices-support-on-device-text-generation "Permanent link")

On iOS, all Apple Intelligence-enabled devices (iPhone 15 Pro or later) running iOS 26 or later. On Android, only [Gemini Nano-capable devices](https://developers.google.com/ml-kit/genai#supported%5Fdevices) with AICore, for example the Google Pixel 9 series or Samsung Galaxy S25 series. Always check `getAvailability()` at runtime.

### Can I use my own model files?[¶](#can-i-use-my-own-model-files "Permanent link")

No. The plugin deliberately supports only the system-provided models. This keeps your app small and inference fast. Support for custom model runtimes may be added in the future.

### Why does a generation reject with `GENERATION_FAILED`?[¶](#why-does-a-generation-reject-with-generation%5Ffailed "Permanent link")

The most common reasons are an exceeded context window (create a new chat in that case), content that is blocked by the platform's safety guardrails, or an exceeded AICore quota on Android. The error message contains the platform-specific reason.

### Can I use this plugin with Ionic, React, Vue or Angular?[¶](#can-i-use-this-plugin-with-ionic-react-vue-or-angular "Permanent link")

Yes, the plugin is framework-agnostic. It works in any Capacitor app regardless of the web framework, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects.

## Related Plugins[¶](#related-plugins "Permanent link")

* [Secure Preferences](https://capawesome.io/docs/sdks/capacitor/secure-preferences/): Store chat data securely on the device.
* [Speech Recognition](https://capawesome.io/docs/sdks/capacitor/speech-recognition/): Turn the user's voice into prompts.
* [Speech Synthesis](https://capawesome.io/docs/sdks/capacitor/speech-synthesis/): Read the generated responses aloud.

## Newsletter[¶](#newsletter "Permanent link")

Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/).

## Changelog[¶](#changelog "Permanent link")

See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/llm/CHANGELOG.md).

## Breaking Changes[¶](#breaking-changes "Permanent link")

See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/llm/BREAKING.md).

## License[¶](#license "Permanent link")

See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/llm/LICENSE).

August 15, 2026 

Back to top

```json
{"@context": "https://schema.org", "@graph": [{"@type": "TechArticle", "@id": "https://capawesome.io/docs/sdks/capacitor/llm/#article", "headline": "Capacitor LLM Plugin for Android & iOS", "name": "Capacitor LLM Plugin for Android & iOS", "description": "Capacitor LLM plugin to run on-device AI models on Android and iOS, powered by Gemini Nano and Apple Intelligence, with chat and streaming APIs.", "inLanguage": "en", "url": "https://capawesome.io/docs/sdks/capacitor/llm/", "mainEntityOfPage": "https://capawesome.io/docs/sdks/capacitor/llm/", "author": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}, "publisher": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}, "about": {"@id": "https://capawesome.io/docs/sdks/capacitor/llm/#software"}}, {"@type": "SoftwareSourceCode", "@id": "https://capawesome.io/docs/sdks/capacitor/llm/#software", "name": "Capacitor LLM Plugin for Android & iOS", "description": "Capacitor LLM plugin to run on-device AI models on Android and iOS, powered by Gemini Nano and Apple Intelligence, with chat and streaming APIs.", "url": "https://capawesome.io/docs/sdks/capacitor/llm/", "programmingLanguage": "TypeScript", "runtimePlatform": "Capacitor", "codeRepository": "https://github.com/capawesome-team", "author": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}, "publisher": {"@type": "Organization", "name": "Capawesome", "url": "https://capawesome.io", "logo": {"@type": "ImageObject", "url": "https://capawesome.io/assets/images/logo.svg"}}}]}
{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "How is this plugin different from other similar plugins?", "acceptedAnswer": {"@type": "Answer", "text": "It runs entirely on the platform's own on-device models — Apple Intelligence on iOS and Gemini Nano on Android — so prompts and responses never leave the device and there are no model files to bundle, API keys or cloud calls. You get chat sessions that keep context, token streaming, cancellation and typed availability checks through one fully typed API that is actively maintained against the latest OS and Capacitor versions, and it's backed by dedicated support. If you just need occasional cloud completions, a plain HTTP call may be enough; if you want private, offline text generation with a complete on-device workflow, this plugin is built for exactly that."}}, {"@type": "Question", "name": "Is text generation available on the web?", "acceptedAnswer": {"@type": "Answer", "text": "No. Browsers do not provide a system language model, so all methods except getAvailability() reject with an unimplemented error on the web. getAvailability() resolves with the unavailable status."}}, {"@type": "Question", "name": "Which devices support on-device text generation?", "acceptedAnswer": {"@type": "Answer", "text": "On iOS, all Apple Intelligence-enabled devices (iPhone 15 Pro or later) running iOS 26 or later. On Android, only Gemini Nano-capable devices with AICore, for example the Google Pixel 9 series or Samsung Galaxy S25 series. Always check getAvailability() at runtime."}}, {"@type": "Question", "name": "Can I use my own model files?", "acceptedAnswer": {"@type": "Answer", "text": "No. The plugin deliberately supports only the system-provided models. This keeps your app small and inference fast. Support for custom model runtimes may be added in the future."}}, {"@type": "Question", "name": "Why does a generation reject with GENERATION_FAILED?", "acceptedAnswer": {"@type": "Answer", "text": "The most common reasons are an exceeded context window (create a new chat in that case), content that is blocked by the platform's safety guardrails, or an exceeded AICore quota on Android. The error message contains the platform-specific reason."}}, {"@type": "Question", "name": "Can I use this plugin with Ionic, React, Vue or Angular?", "acceptedAnswer": {"@type": "Answer", "text": "Yes, the plugin is framework-agnostic. It works in any Capacitor app regardless of the web framework, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects."}}], "url": "https://capawesome.io/docs/sdks/capacitor/llm/"}
```
