> 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/pdf-viewer.md).

# PDF Viewer

## What is a PDF Viewer?

The PDF Viewer lets your app open and display PDF files using a native viewer - no need to redirect users to an external browser or ask them to download the file first. You can load a PDF from a public URL or pass Base64-encoded content directly. A single toggle controls both the download and share buttons, letting users save the file to their device or share it with other apps when enabled.

## 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" %}

#### \[Action] Natively - Open PDF

* **URL** - the public URL of the PDF file to display. Either URL or Base64 must be provided.
* **Base64** - the Base64 encoded content of the PDF file. Either URL or Base64 must be provided.
* **File name** - optional. The name to use when saving or sharing the file (e.g. `invoice_Q4.pdf`). If not provided, a default name with a timestamp will be generated (e.g. `1234567890.pdf`)
* **Download** - Yes/No. Controls whether the download and share buttons appear in the PDF viewer.

<figure><img src="/files/5NepICHMXzaom4IJ2Xyz" alt="" width="341"><figcaption></figcaption></figure>
{% endtab %}

{% tab title="JavaScript SDK" %}

```javascript
// ============================================================================
// NATIVELY PDF VIEWER - DOCUMENTATION & EXAMPLES
// ============================================================================

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

// ============================================================================
// ALL AVAILABLE METHODS
// ============================================================================
// window.natively.openPDF(options, callback)
//   - Opens a PDF file using the device's native PDF viewer.
//   - options.url: string — public URL of the PDF file. Use url OR base64, not both.
//   - options.base64: string — Base64 encoded PDF content. Use url OR base64, not both.
//   - options.fileName: string — optional file name for saving/sharing. Defaults to a timestamp (e.g. 1234567890.pdf) if not provided.
//   - options.download: boolean — shows or hides BOTH the download and share buttons together.

// ============================================================================
// CALLBACK RESPONSE FIELDS
// ============================================================================
// resp.status  - "SUCCESS" or "FAILED"
// resp.message - description of the result or error

// --- PDF Viewer. Open from URL Example. Start ---

window.natively.openPDF({
    url: 'https://example.com/documents/guide.pdf',
    fileName: 'UserGuide.pdf',
    download: true
}, (response) => {
    console.log('PDF Viewer action:', response);
});

// --- PDF Viewer. Open from URL Example. End ---


// --- PDF Viewer. Open from Base64 Example. Start ---

window.natively.openPDF({
    base64: 'JVBERi0xLjMKJcTl8uXrp...', // Truncated for brevity
    fileName: 'GeneratedReport.pdf',
    download: false
}, (response) => {
    if (response.status === 'FAILED') {
        console.error('PDF Viewer action:', response.message);
    }
});

// --- PDF Viewer. Open from Base64 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 line below and paste it into your AI agent.&#x20;

<pre><code>[<a data-footnote-ref href="#user-content-fn-1">Your feature description</a>] using the Natively PDF Viewer SDK: // window.natively.openPDF(options, callback) — opens a PDF file using the device's native PDF viewer. options.url: string — public URL of the PDF file. options.base64: string — Base64 encoded PDF content. Use either url or base64, not both. options.fileName: string — optional file name for saving/sharing, if not provided a default name with timestamp is generated (e.g. 1234567890.pdf). options.download: boolean — shows or hides BOTH the download and share buttons together (true = both shown, false = both hidden). resp.status: "SUCCESS" or "FAILED". resp.message: description of the result or error. For reference: https://docs.buildnatively.com/guides/integration/pdf-viewer
</code></pre>

{% hint style="warning" %}
Replace the placeholder at the beginning with a description of what you want to build - for example: "Open a PDF invoice from a URL when the user taps the View Invoice button, with a download button enabled".
{% endhint %}
{% endtab %}
{% endtabs %}

### How to use

#### Load from URL vs Base64

Use a URL when the PDF is hosted on a publicly accessible server - this is the simplest and most common approach. Use Base64 when you need to display a dynamically generated PDF (such as an invoice or report) that isn't hosted anywhere, by encoding the file content directly and passing it to the viewer.

#### Always provide a file name

While optional, providing a meaningful file name (e.g. `invoice_2024_Q4.pdf`) makes a much better experience when the user downloads or shares the file. Without it, the file will be saved with a timestamp as the name, which is confusing for users.

`download` controls both the download and share buttons

The `download` parameter shows or hides the download and share buttons - there's no way to show one without the other. Set `download: true` for documents users are likely to want to save or send on - invoices, tickets, reports, contracts. Set `download: false` for documents that should be viewed in-app only, like onboarding guides or terms of service.

#### Download behavior

When the user taps the download button, the PDF is saved directly to the device's default Downloads folder. A snackbar notification appears at the bottom of the screen confirming the download's success or failure, and the download button is replaced with a green checkmark icon indicating the file was saved.

#### Only provide a URL or Base64, not both

If you provide both `url` and `base64`, the behavior is undefined. Always use one or the other.

## Troubleshooting

<details>

<summary>PDF not loading from URL</summary>

Make sure the URL is publicly accessible - private, authenticated, or local URLs will not work. Test the URL directly in a browser before passing it to `openPDF`. Also ensure the URL points directly to a `.pdf` file, not to a page that embeds a PDF viewer.

</details>

<details>

<summary>PDF not loading from Base64</summary>

Verify the Base64 string is a valid encoding of a PDF file. The string should start with `JVBERi0` (the Base64 encoding of `%PDF-`). An incomplete or malformed Base64 string will cause the viewer to fail.

</details>

<details>

<summary><code>resp.status</code> returns <code>FAILED</code></summary>

Check `resp.message` for the specific error description. Common causes include an inaccessible URL, malformed Base64 content, or a file that is not a valid PDF.

</details>

<details>

<summary>File saved with a timestamp name instead of a meaningful name</summary>

You didn't provide a `fileName` in the options. Always pass a descriptive file name to avoid files being saved as `1234567890.pdf`.

</details>

<details>

<summary>Download or share button not appearing</summary>

Make sure `download: true` is set in the options - this single flag controls both buttons. If using the Bubble plugin, verify the Download field is set to Yes.

</details>

<details>

<summary>PDF viewer not working in a web browser</summary>

This is a native feature and will not work in a standard web browser. Test on a real device using a Natively build or the Preview app.

</details>

[^1]: Replace this placeholder
