Easily integrate Panora's swap functionality into your application with our lightweight, plug-and-play SDK — enabling seamless token swaps and real-time data directly within your platform.
Key Features
Quick Setup — Add swaps to your app in under 5 minutes
Three Display Modes — Integrated (inline), Widget (floating button), or Modal (popup)
Fully Customizable — Theme colors, default tokens, locale, slippage, and more
Multi-Language — Supports 11 locales out of the box
Wallet Support — Built-in wallet connection UI, or bring your own
Display Modes
Mode
Description
INTEGRATED
Renders inline inside a target <div> you provide
WIDGET
Renders a floating button (configurable corner). Click to open the swap UI
MODAL
Opens a centered popup dialog with a backdrop overlay
Supported Frameworks
Plain HTML / Vanilla JS
React (Vite, CRA)
Next.js (App Router & Pages Router)
Any framework that can run JavaScript
Getting an API Key
A default public API key is available for development and typical usage:
For production or high-traffic use cases, open a ticket on Discord to request a dedicated key.
Installation
npm
yarn
pnpm
CDN (no bundler needed)
Quick Start
Option A: NPM (React / Next.js)
Option B: CDN (plain HTML)
React App Example
A complete guide to integrating Panora Widget SDK into a React + Vite application.
1. Create a New Project
2. Install the SDK
3. Add the Widget
Replace src/App.tsx with:
4. Run
Using the React Component
If you prefer a declarative approach instead of the imperative init() / close() API, the SDK also exports a <PanoraWidget /> React component:
Note:<PanoraWidget /> always renders in INTEGRATED mode. For WIDGET or MODAL modes, use the imperative init() / close() API.
Key Points
Always call close() before re-initializing to avoid duplicate instances
The 100ms timeout gives the DOM time to settle before the widget mounts
Store your API key in .env as VITE_PANORA_API_KEY
Next.js Example (App Router)
A complete guide to integrating Panora Widget SDK into a Next.js App Router application.
1. Create a New Project
2. Install the SDK
3. Add the Widget
Create or replace src/app/page.tsx:
Important: The "use client" directive is required. The widget uses browser APIs (document, window) and cannot run during server-side rendering.
Widget Mode Example
Modal Mode Example
Next.js Pages Router
If you use the Pages Router instead of App Router, the code is nearly identical — just drop the "use client" directive and place your file in src/pages/index.tsx. No other changes needed.
Key Points
Store your API key in .env.local as NEXT_PUBLIC_PANORA_API_KEY
Always call close() in the useEffect cleanup to prevent memory leaks
The widget loads its UI from the CDN at runtime, so your Next.js bundle size is unaffected
HTML App Example
The simplest integration — no bundler, no framework, just a single <script> tag.
1. Create an HTML File
2. Serve It
Use any static file server:
Widget Mode (Floating Button)
Modal Mode (Popup)
API Reference (CDN)
When using the CDN script, the global PanoraWidget object is available on window:
Method
Description
PanoraWidget.render(container, config?)
Render the widget into a DOM element or CSS selector. Returns { unmount }
Key Points
The defer attribute ensures the script loads after HTML parsing
PanoraWidget.render() accepts either a DOM element or a CSS selector (#id, .class)
The CDN bundle is fully self-contained (includes React) — no other dependencies needed
Configuration
Complete reference for all WidgetConfig options.
API & RPC
Property
Type
Default
Description
panoraApiKey
string
Public key
Your Panora API key
rpcUrl
string
Panora default
Custom Aptos RPC URL. Must use https:// (except localhost)
Display
Property
Type
Default
Description
displayMode
"INTEGRATED" | "WIDGET" | "MODAL"
"WIDGET"
How the widget is presented
integratedTargetId
string
"panora-widget"
DOM element ID for INTEGRATED mode (used with init() only)
Default slippage tolerance (0–50). Must be a numeric string
showPercentageSlider
boolean
—
Show the percentage slider on the input field
Wallet
Property
Type
Default
Description
account
Account
undefined
Pre-configured Aptos account for direct transaction signing without wallet popup. Accepts any Account subclass from @aptos-labs/ts-sdk (Ed25519Account, SingleKeyAccount, KeylessAccount, etc.). When set, the widget signs transactions directly — no wallet connection needed.
independentWalletConnection
boolean
true
When false, hides the widget's built-in wallet connect button. Use when the host app manages wallet connection externally.
useExternalWallet
boolean
false
When true, the widget skips creating its own AptosWalletAdapterProvider and uses useWallet() from the nearest parent provider. The integrator must wrap the widget in their own <AptosWalletAdapterProvider>. Ideal for wallet extensions/apps embedding the widget. Only works with npm package (not CDN).
disableWalletButton
boolean
false
Hide the "Connect Wallet" button
enabledWallets
string[]
All wallets
Restrict which wallets appear in the connection dialog
Integrator Fees
Property
Type
Default
Description
integratorWalletAddress
`0x${string}`
—
Wallet address that receives integrator fees
integratorFeePercentage
number
—
Fee percentage per transaction (0–5)
Transaction Options
Property
Type
Default
Description
transactionOptions.maxGasAmount
number
—
Maximum gas units. Must be a positive integer
transactionOptions.gasUnitPrice
number
—
Price per gas unit (octas). Must be a positive integer
Localization
Property
Type
Default
Description
locale
string
"en"
UI language
Supported locales:
Code
Language
en
English
es
Spanish
hi
Hindi
id
Indonesian
ja
Japanese
ko
Korean
ru
Russian
tr
Turkish
vi
Vietnamese
zh
Chinese (Simplified)
zh-HK
Chinese (Traditional)
Notifications
Property
Type
Default
Description
showNotifications
boolean
false
Show toast notifications for swap success/failure
Validation Rules
The widget validates your config at runtime. Invalid values are silently ignored with a console warning:
Property
Rule
defaultSlippagePercentage
Must be a numeric string between 0 and 50
integratorFeePercentage
Must be a number between 0 and 5
rpcUrl
Must start with https:// (except localhost / 127.0.0.1)
transactionOptions.maxGasAmount
Must be a positive integer
transactionOptions.gasUnitPrice
Must be a positive integer
Theming
Customize the widget's appearance to match your app's design system.
Pass a partial theme object in your config — any property you omit will use the default Panora theme.
Usage
Theme Properties
Property
Default
Description
basicBgColor
rgba(255, 255, 255, .05)
Primary background
secondaryBgColor
#010D09
Secondary background (outer areas)
tertiaryBgColor
#000704
Tertiary background (cards, dropdowns)
primaryColor
#5fdfac
Main accent color (buttons, highlights)
subtleColor
rgba(255, 255, 255, .05)
Subtle UI elements
primaryButtonTextColor
#000000
Text color on primary buttons
borderColor
rgba(255, 255, 255, .05)
Border color
inputColor
#0d110d
Input field background
inputBorderColor
#2a3a2a
Input field border
primaryTextColor
#FFFFFF
Main text color
secondaryTextColor
#6B7280
Muted/secondary text
placeholderTextColor
#3a4a3a
Input placeholder text
textUpColor
#5fdfac
Positive values (gains)
textDownColor
#F6465D
Negative values (losses)
successColor
#5fdfac
Success states
errorColor
#F6465D
Error states
warningColor
#EF8E19
Warning states
Default Theme
Tip: You can import the default theme object in code:
API Reference
NPM Package Exports
init(config?)
Initialize and render the widget.
Parameter
Type
Description
config
WidgetConfig
Widget configuration (see Configuration)
config.integratedTargetId
string
DOM element ID for INTEGRATED mode. Defaults to "panora-widget"
Returns:Promise<{ unmount: () => void }> — call unmount() to remove the widget.
Behavior by display mode:
Mode
What happens
INTEGRATED
Renders into the element matching integratedTargetId. Throws if element not found.
WIDGET
Creates a floating button. A panora-widget-root container is auto-created in document.body if needed.
MODAL
Opens a centered dialog with backdrop. Container auto-created like WIDGET.
render(container, config?)
Lower-level alternative to init() for full control over where the widget mounts.
Parameter
Type
Description
container
HTMLElement | string
A DOM element or CSS selector ("#my-div", ".my-class")
config
WidgetConfig
Widget configuration
Note:render() is primarily used with the CDN bundle (PanoraWidget.render()). When using the npm package, prefer init().
close()
Unmount and clean up the widget.
Always call close() before re-initializing, or in your component's cleanup function to prevent duplicate instances.
resume()
Re-show the widget with the last configuration passed to init().
If a widget instance is already mounted, this is a no-op.
<PanoraWidget />
React component for declarative rendering. Always uses INTEGRATED mode.
Automatically creates a container <div> and calls init() on mount
Automatically calls close() on unmount
Re-initializes when widgetConfig changes
CDN Global: PanoraWidget
When using the CDN <script> tag, a global PanoraWidget object is available on window.
Parameter
Type
Description
container
HTMLElement | string
DOM element or CSS selector
config
WidgetConfig
Widget configuration
The CDN bundle is fully self-contained — it includes its own copy of React and all dependencies.
Attribution
Include "Powered by Panora" when using this SDK in your application. The widget displays this by default.
import { init, close } from "@panoraexch/widget-sdk";
// Open the widget
await init({
displayMode: "INTEGRATED",
integratedTargetId: "my-swap-container",
panoraApiKey: "YOUR_API_KEY",
});
// Later, tear it down
close();
<button id="open-btn">Open Swap</button>
<script>
var instance = null;
document.getElementById("open-btn").addEventListener("click", function () {
var root = document.getElementById("panora-widget-root");
if (!root) {
root = document.createElement("div");
root.id = "panora-widget-root";
document.body.appendChild(root);
}
instance = PanoraWidget.render(root, {
panoraApiKey: "YOUR_API_KEY",
displayMode: "MODAL",
locale: "en",
});
});
</script>
// container can be a DOM element or a CSS selector string
var instance = PanoraWidget.render("#my-div", { displayMode: "INTEGRATED" });
// To remove the widget:
instance.unmount();