> For the complete documentation index, see [llms.txt](https://docs.buildnatively.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.buildnatively.com/guides/integration/device-info.md).

# Device Info

## What is Device Info?

Device Info gives your app access to details about the user's device and your app's runtime environment - device model, OS version, app build info, dark mode status, and screen orientation. It also lets you listen for app state changes (foreground/background) and keyboard visibility, and includes an error handler for managing HTTP request failures without showing the default error screen.

{% hint style="info" %}
For platform detection (iOS, Android, or web), see [Browser Info](/guides/integration/browser-info.md) instead.
{% endhint %}

## Prerequisites

{% hint style="success" %}
This feature is available in all plans. [See all plans](/getting-started/subscription-plans.md)
{% endhint %}

## Implementation

Choose your integration method below: **Bubble.io Plugin** (No-Code), **JavaScript SDK** (Code), or **AI Agents** (for AI-powered editors like Lovable, Base44, and Replit).

### Initialization

{% tabs %}
{% tab title="Bubble.io Plugin" %}
**Check Plugin**

Before starting, verify if the Natively plugin is already installed in your Bubble project.

1. Open your Bubble editor and navigate to the Plugins tab in the left sidebar.
2. **Check Installed Plugins:** Look through your list of installed plugins for "Natively iOS & Android app builder".
   * If it IS installed: Check the version number. If an update is available (e.g., you see a button saying "Update"), click it to ensure you have the latest features and bug fixes.

<figure><img src="https://docs.buildnatively.com/~gitbook/image?url=https%3A%2F%2F3352617162-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F90tV7pYflEQdiAr2VfWu%252Fuploads%252FmfnSUug82IdnxOAoBrak%252Fnatively_app_builder_bubble_plugin_update.png%3Falt%3Dmedia%26token%3Dc193f69f-b03b-4be4-b80b-f34ba37ac212&#x26;width=768&#x26;dpr=3&#x26;quality=100&#x26;sign=a89e4510&#x26;sv=2" alt=""><figcaption></figcaption></figure>

* If it is NOT installed: Click the + Add plugins button, search for "Natively", and click Install.

<figure><img src="https://docs.buildnatively.com/~gitbook/image?url=https%3A%2F%2F3352617162-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F90tV7pYflEQdiAr2VfWu%252Fuploads%252FC5rA42yQHcN1uGKFbzmF%252Fnatively_app_builder_bubble_plugin.png%3Falt%3Dmedia%26token%3Dd9706d9b-dbe8-459b-b9b3-5667648aa4b7&#x26;width=768&#x26;dpr=3&#x26;quality=100&#x26;sign=9aae2297&#x26;sv=2" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="JavaScript SDK" %}
**Check SDK**

Before writing any logic, ensure the Natively SDK is correctly installed and up-to-date in your codebase.

1. Open your project's main HTML file (or header settings) and look for the Natively script tag inside the `<head>` section.
2. Install/Update: If missing or outdated, add the following code. You can specify the SDK version in the URL (e.g., `@2.26.0`).

```javascript
<head>
  <script async onload="nativelyOnLoad()" src="https://cdn.jsdelivr.net/npm/natively@2.26.0/natively-frontend.min.js"></script>
</head>
```

{% hint style="info" %}
To ensure you are using the most up-to-date version, check the [Natively GitHub releases page](https://github.com/buildnatively/js-sdk/releases) for the latest version number.
{% endhint %}
{% endtab %}

{% tab title="AI Agents" %}
**Initialize the SDK**

AI-powered editors like Lovable, Base44, and Replit use the JavaScript SDK to implement Natively features. Before implementing any feature, the Natively SDK must be initialized in your project.

Copy the line below and paste it into your AI agent to check and set up the Natively SDK in your project.

```
Check if the following Natively SDK script is present in the <head> of index.html. If missing or outdated, add it: <script async onload="nativelyOnLoad()" src="https://cdn.jsdelivr.net/npm/natively@2.26.0/natively-frontend.min.js"></script><script>function nativelyOnLoad() { window.natively.setDebug(true); console.log("✅ Natively SDK loaded successfully."); }</script> For reference: https://docs.buildnatively.com/guides/integration/how-to-get-started https://github.com/buildnatively/js-sdk/releases
```

{% endtab %}
{% endtabs %}

### Setup Logic

{% tabs %}
{% tab title="Bubble.io Plugin" %}

#### \[Element] Natively - Device

{% hint style="info" %}
**Refresh App Info** action is called automatically on element load, so device information is available as soon as the element initializes. Avoid adding multiple **Natively - Device** elements on the same page, as each one sends a request to the device on load.
{% endhint %}

#### **Events:**

* **App Info received** - fires when device and app information is successfully retrieved.
* **App went foreground** - fires when the user opens the app.
* **App went background** - fires when the user minimizes the app without exiting.
* **Keyboard is visible** - fires when the on-screen keyboard appears.
* **Keyboard is hidden** - fires when the on-screen keyboard is dismissed.
* **Error occurred** - fires when an HTTP request fails, and the **Set Error Handler** action has been previously called on the current page. (Available starting from v. 2.26.&#x30;**)**

#### **States:**

* **Device** - device model identifier. (iOS values can be decoded with this [list](https://github.com/pbakondy/ios-device-list/blob/master/devices.json))
* **OS Version** - Android / iOS version.
* **App Version** - your app's build version.
* **Build Number** - your app's build number.
* **Natively SDK Version** - the Natively SDK version in use.
* **OS Name** - "Android" or "iOS".
* **isDarkMode** - Yes / No.
* **Orientation** - "PORTRAIT" or "LANDSCAPE".
* **Error Code** - the HTTP status code of the failed request (e.g. 404, 500).
* **Error Description** - a text description of the error provided by the requester endpoint.
* **Error Response Data** - the raw response body (JSON/Text) returned by the server for the failed HTTP request (if available).

#### Actions:

* **Refresh App Info** - retrieves the latest device and app information.
* **Set Error Handler** - enables custom error handling for the current page session. When active, the app will not automatically display the native error screen when an HTTP request fails. Instead, it fires the **Error occurred** event.

#### How to use the Error Handler

To ensure consistent error handling across your app, place the logic in a Reusable Element (like your Header or a dedicated technical Reusable) that exists on every page.

1. Place the **Natively - Device** element inside your Reusable Element.
2. When Natively - Device's Device state is not empty (this ensures the element is fully initialized) > call the **Set Error Handler** action.\
   ![](/files/8NPvX56w8P2SrMlXNAPo)
3. Create a workflow to handle the error when it happens (the event **Error occurred** is fired).\
   ![](/files/tKx9Uxt6VXsy3Gjd3qmO)
4. The error's details can also be visible in the Debug console.
   {% endtab %}

{% tab title="JavaScript SDK" %}

#### Device Info

```javascript
// ============================================================================
// NATIVELY DEVICE INFO - DOCUMENTATION & EXAMPLES
// ============================================================================

// Initialize
const info = new NativelyInfo();

// ============================================================================
// ALL AVAILABLE METHODS
// ============================================================================
// info.getAppInfo(callback)
//   - Retrieves device, OS, and app build information.
//   - Returns the result via callback.
//
// info.app_state(callback)
//   - Listens for app state changes (foreground/background).
//
// info.keyboard_visibility(callback)
//   - Listens for changes in on-screen keyboard visibility.

// ============================================================================
// CALLBACK RESPONSE FIELDS - getAppInfo
// ============================================================================
// resp.device        - Device model (iOS values can be decoded with this list:
//                       https://github.com/pbakondy/ios-device-list/blob/master/devices.json)
// resp.osVersion     - Android/iOS version.
// resp.osName        - "iOS" or "Android".
// resp.buildVersion  - Your app's build version.
// resp.buildNumber   - Your app's build number.
// resp.sdkVersion    - The Natively SDK version in use.
// resp.isDarkMode    - Boolean. true if the device is in dark mode.
// resp.orientation   - "PORTRAIT" or "LANDSCAPE".

// ============================================================================
// CALLBACK RESPONSE FIELDS - app_state
// ============================================================================
// resp.state   - Boolean. true if the app is in the foreground, false if backgrounded.

// ============================================================================
// CALLBACK RESPONSE FIELDS - keyboard_visibility
// ============================================================================
// resp.visible - Boolean. true if the on-screen keyboard is visible, false if hidden.


// --- Device Info. Get App Info Example. Start ---

const app_info_callback = function(resp) {
    console.log(resp.device);       // iPhone14,2
    console.log(resp.osVersion);    // 15.6
    console.log(resp.osName);       // iOS / Android
    console.log(resp.buildVersion); // 1.0.0
    console.log(resp.buildNumber);  // 1
    console.log(resp.sdkVersion);   // 3
    console.log(resp.isDarkMode);   // true/false
    console.log(resp.orientation);  // PORTRAIT / LANDSCAPE
};

info.getAppInfo(app_info_callback);

// --- Device Info. Get App Info Example. End ---


// --- Device Info. App State Example. Start ---

const app_state_callback = function(resp) {
    if (resp.state) {
        console.log("App went foreground");
    } else {
        console.log("App went background");
    }
};

info.app_state(app_state_callback);

// --- Device Info. App State Example. End ---


// --- Device Info. Keyboard Visibility Example. Start ---

const keyboard_visibility_callback = function(resp) {
    if (resp.visible) {
        console.log("Keyboard is visible");
    } else {
        console.log("Keyboard is hidden");
    }
};

info.keyboard_visibility(keyboard_visibility_callback);

// --- Device Info. Keyboard Visibility Example. End ---

```

#### Error Handler

```javascript
// ============================================================================
// NATIVELY ERROR HANDLER - DOCUMENTATION & EXAMPLES
// ============================================================================

// No initialization required — setErrorHandler is available directly
// on the global window.natively object.

// ============================================================================
// ALL AVAILABLE METHODS
// ============================================================================
// window.natively.setErrorHandler(callback)
//   - Suppresses the default native error screen on failed HTTP requests.
//   - Fires the callback instead, letting you handle the error in your own workflow.

// ============================================================================
// CALLBACK RESPONSE FIELDS
// ============================================================================
// resp.code          - HTTP status code of the failed request (e.g. 404, 500, 0).
// resp.description   - Text description of the error (e.g. "Network Request Failed", "Not Found").
// resp.url           - The URL of the failed request.
// resp.type          - "NETWORK_ERROR" or "HTTP_ERROR".
// resp.response_data - The raw response body (usually an Object — stringify it to read it).

// --- Error Handler. Example. Start ---

const error_handler_callback = function(resp) {
    console.log(resp.code);
    console.log(resp.description);
    console.log(resp.url);
    console.log(resp.type);
    console.log(JSON.stringify(resp.response_data));
};

window.natively.setErrorHandler(error_handler_callback);

// --- Error Handler. Example. End ---
```

{% endtab %}

{% tab title="AI Agents" %}
AI-powered editors like Lovable, Base44, and Replit use the JavaScript SDK to implement Natively features.&#x20;

Copy the relevant line below and paste it into your AI agent.

#### Device Info

<pre><code>[<a data-footnote-ref href="#user-content-fn-1">Your feature description</a>] using the Natively Device Info SDK: const info = new NativelyInfo(); // info.getAppInfo(callback) — retrieves device info. resp.device: device model. resp.osVersion: OS version. resp.osName: "iOS" or "Android". resp.buildVersion: app version. resp.buildNumber: build number. resp.sdkVersion: SDK version. resp.isDarkMode: boolean. resp.orientation: "PORTRAIT" or "LANDSCAPE". // info.app_state(callback) — listens for app state changes. resp.state: boolean (true=foreground, false=background). // info.keyboard_visibility(callback) — listens for on-screen keyboard visibility changes. resp.visible: boolean (true=visible, false=hidden). For reference: https://docs.buildnatively.com/guides/integration/device-info
</code></pre>

{% hint style="warning" %}
Replace the placeholder at the beginning with a description of what you want to build - for example: "When the app loads, get the device info and display the OS name and app version on the settings page".
{% endhint %}

#### Error Handler

<pre><code>[<a data-footnote-ref href="#user-content-fn-1">Your feature description</a>] using the Natively Error Handler: // window.natively.setErrorHandler(callback) — suppresses default error screen on HTTP failures. No initialization required. resp.code: HTTP status code. resp.description: error description. resp.url: failed URL. resp.type: "NETWORK_ERROR" or "HTTP_ERROR". resp.response_data: raw response body (stringify to read). For reference: https://docs.buildnatively.com/guides/integration/device-info
</code></pre>

{% hint style="warning" %}
Replace the placeholder at the beginning with a description of what you want to build - for example: "Handle HTTP errors silently without showing the native error screen, and display a custom error message instead".
{% endhint %}
{% endtab %}
{% endtabs %}

### How to use

#### Reading device info on app load

Call `info.getAppInfo()` early in your app's initialization, ideally inside `nativelyOnLoad(),` so device details are available before any conditional logic runs. Store the result rather than calling it repeatedly.

#### Detect dark mode

Use `resp.isDarkMode` to apply the correct theme when the app loads. Combine with `app_state` to re-check when the user returns to the foreground, since they may have changed their system appearance while the app was backgrounded.

#### Handle orientation changes

Use `resp.orientation` from `getAppInfo()` to adjust your layout on load. Note that this is a snapshot at the time of the call - it won't update automatically as the device rotates. Call `getAppInfo()` again inside `app_state` if you need to track rotation.

#### App state: foreground and background

Use `resp.state` inside `app_state` to pause or resume activity when the user switches apps. Common use cases include pausing media playback, stopping timers, or refreshing data when the user returns.

#### Keyboard visibility

Use `resp.visible` inside `keyboard_visibility` to adjust your layout when the keyboard appears or disappears - for example, scrolling to a focused input or hiding a bottom navigation bar.

#### Error Handler - when to use it

Enable the Error Handler when you want to handle HTTP failures gracefully instead of showing the native error screen. Place it in a Reusable Element that exists on every page so it's always active. Use `resp.type` to distinguish between network failures (`NETWORK_ERROR`) and server errors (`HTTP_ERROR`), and `resp.code` to respond differently to specific status codes like 401 or 500.

## Troubleshooting

<details>

<summary><code>getAppInfo()</code> returns empty or undefined values</summary>

Make sure the call is made after the SDK has fully loaded. Call it inside `nativelyOnLoad()` or trigger it from a user interaction rather than immediately on page load.

</details>

<details>

<summary><code>app_state</code> is not firing</summary>

The `app_state` listener needs to be registered after the SDK is loaded. Register it inside `nativelyOnLoad()` to ensure it's active before any state changes occur.

</details>

<details>

<summary>Orientation is not updating on device rotation</summary>

`getAppInfo()` returns a snapshot of the orientation at the time of the call - it does not update automatically. Call `getAppInfo()` again inside your `app_state` callback when the app returns to the foreground to get the latest orientation value.

</details>

<details>

<summary>Keyboard visibility events are not firing</summary>

Keyboard visibility is reported through the separate `keyboard_visibility` method, not `app_state`. Make sure you've registered a callback with `info.keyboard_visibility(callback)` and that it's called after the SDK has loaded.

</details>

<details>

<summary>Error Handler is not suppressing the native error screen</summary>

Make sure `window.natively.setErrorHandler()` is called before any HTTP requests are made. Place it in a Reusable Element that loads on every page so it's always active. If it's only called on specific pages, the error screen will still appear on pages where it hasn't been set.

</details>

<details>

<summary><code>resp.response_data</code> is not readable</summary>

`resp.response_data` is usually an Object - use `JSON.stringify(resp.response_data)` to convert it to a readable string before logging or displaying it.

</details>

[^1]: Replace this placeholder
