Building Hybrid Plugins
A step-by-step guide to building, configuring, and shipping a UXP Hybrid plugin.
Building a uxpaddon
1. Create a Native Library Project
- Create a new Xcode (macOS) or Visual Studio (Windows) project targeting a dynamic library.
- Add the UXP Hybrid Plugin SDK
src/apipath to your project's include paths.
2. Write Your C++ Logic
Include the main header and register your initialization and termination routines:
#include "UxpAddon.h"
addon_value init(addon_env env, addon_value exports) {
// Register functions and properties on 'exports'
return exports;
}
void terminate() {
// Cleanup logic
}
UXP_ADDON_INIT(init);
UXP_ADDON_TERMINATE(terminate);
Within your init function, use the addon APIs (defined in UxpAddonShared.h via addon_apis) to register native functions that will be callable from JavaScript. These APIs are invoked as UxpAddonApis.uxp_addon_***().
3. Compile and Rename
Build the dynamic library (.dylib on macOS, .dll on Windows) and rename the file extension to .uxpaddon.
Using a uxpaddon in JavaScript
Once compiled, the addon is loaded with require() and its exported functions are available immediately:
const addon = require("sample-uxp-addon.uxpaddon");
let result = addon.first_function();
The addon behaves like any other JavaScript moduleβyou can call functions, read properties, and pass data back and forth between JavaScript and C++.
Manifest Configuration
Hybrid plugins require a few specific settings in the manifest.json:
- Manifest version 6 or above.
- The
addonfield with the name of your uxpaddon. - The
enableAddonpermission.
{
"manifestVersion": 6,
"host": {
"app": "premierepro",
"minVersion": "25.6.0",
},
"addon": {
"name": "sample-uxp-addon.uxpaddon"
},
"requiredPermissions": {
"enableAddon": true
}
}
Plugin Structure
The uxpaddon binaries must be placed in a specific directory layout within your plugin bundle, organized by platform and architecture:
π root directory
βββ π manifest.json
βββ π main.js
βββ π mac
β βββ π x64
β β βββ βοΈ sample-uxp-addon.uxpaddon (dylib)
β βββ π arm64
β βββ βοΈ sample-uxp-addon.uxpaddon (dylib)
βββ π win
βββ π x64
βββ βοΈ sample-uxp-addon.uxpaddon (dll)
If the directory structure is incorrect, the plugin will fail to load with a "Plugin Manifest Validation Failed" error in the UDT logs.
data-variant=info
data-slots=text
template-plugin included in the SDK to quickly verify your setup. Load it via UDT and find it under the Window > UXP Plugins menu.Asynchronous Operations
It is important to understand the threading model when working with uxpaddons:
- Initialization and termination routines run on the main thread of the host application.
- JavaScript calls to addon functions run on a dedicated scripting thread (never the main thread).
When your addon needs to perform work on the main thread (e.g., interacting with host-specific APIs) and return results to JavaScript, you must use the asynchronous scheduling APIs. These operations are exposed to JavaScript as Promises.
The pattern involves three steps:
- From the scripting thread: create a deferred promise and schedule work on the main thread.
- On the main thread: perform the task, then schedule the result back to the scripting thread.
- Back on the scripting thread: resolve or reject the promise.
addon_value MyAsyncOperation(addon_env env, addon_callback_info info) {
// Extract and convert arguments to standard C++ types
// (addon_value is transient and cannot be used after this function returns)
std::string myArg = ExtractString(env, argValue);
// Create a promise for the JavaScript caller
addon_deferred promiseValue = nullptr;
addon_value deferred = nullptr;
apis.uxp_addon_create_promise(env, &deferred, &promiseValue);
// Schedule work on the main thread
apis.uxp_addon_schedule_on_main_queue(env, MainThreadTask, ...);
return deferred;
}
void MainThreadTask(...) {
// Perform the task on the main thread...
// When done, schedule the result back to the scripting thread
apis.uxp_addon_schedule_on_javascript_queue(env, ScriptThreadTask, ...);
}
void ScriptThreadTask(...) {
apis.uxp_addon_open_handle_scope(env, &scope);
// Resolve (or reject) the JavaScript promise
addon_value resultValue = nullptr;
apis.uxp_addon_resolve_deferred(env, deferred, resultValue);
apis.uxp_addon_close_handle_scope(env, scope);
}
The template-dev project in the SDK includes a working implementation of this pattern (see MyAsyncEcho in module.cpp).
File System Access
Hybrid plugins benefit from relaxed UXP sandbox restrictions. You can access the file system using direct paths:
let entry = '/path/to/file.txt';
This is unlike standard UXP plugins, which must use require('uxp').storage.localFileSystem to access files outside the sandbox. See the Filesystem Operations recipe for more details on standard file system access.
Debugging
Hybrid plugins have both a JavaScript and a C++ layer, each requiring its own debugging approach:
- JavaScript: use the UDT Debug tool to set breakpoints and inspect variables.
- C++: attach your IDE's debugger to the running Premiere process. In most IDEs this is found under Debug > Attach to Process.
Packaging and Distribution
Hybrid plugins follow the same packaging workflow as standard UXP plugins, but with additional steps for native binaries. Work through the checklist below before distributing your plugin.
1. Verify the plugin structure
Make sure your .uxpaddon files are placed in the correct directory layout (see Plugin Structure above). The folder hierarchy is strictβif it doesn't match, the plugin will fail to load with a "Plugin Manifest Validation Failed" error in UDT.
2. Build for all required architectures
Compile your uxpaddon for each supported platform:
- macOS arm64 (Apple Silicon)
- macOS x64 (Intel)
- Windows x64
If you don't have access to all platforms natively, consider using a virtual machine (e.g., VMware Fusion or Parallels). Keep in mind that Apple Silicon Macs cannot virtualize Windows x64βonly Intel Macs can, so dedicated hardware may be required. For creating macOS universal binaries, see Apple's guide.
data-variant=warning
data-slots=text
.ccx package if any is missing. Partial architecture support is only viable for independent or enterprise distribution.3. Code sign and notarize (macOS)
The macOS .uxpaddon executables must be signed and notarized with a valid Apple Developer ID certificate. Requirements:
- Self-signed or test certificates are not accepted.
- The certificate must be valid for at least one year.
- Only the
.uxpaddonbinaries need signingβthe rest of the plugin bundle (JavaScript, HTML, CSS, manifest) does not.
See the FAQ for more details on Apple Developer ID requirements.
4. Set your plugin ID
Before packaging, ensure the id in your manifest.json is correct. If you plan to publish to the Creative Cloud Marketplace, obtain the ID from the Developer Distribution portal and make sure it matches. See Mind your Plugin's ID for details.
5. Package with UDT
Open the UXP Developer Tool, locate your plugin in the workspace, click the β’β’β’ menu and select Package. This creates a .ccx installer file. See Package with the UXP Developer Tool for a walkthrough.
6. Test the installation
Install the .ccx file on a clean system (or at least one without your development environment) to verify:
- The plugin installs without errors.
- The uxpaddon loads correctly on each supported platform.
- No security warnings appear (if binaries are properly signed).
data-variant=warning
data-slots=text
For the full distribution workflow (Marketplace submission, independent distribution, enterprise deployment), see the Share & Distribute section.