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:
Copied to your clipboard#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:
Copied to your clipboardconst 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.
Copied to your clipboard{"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:
Copied to your clipboard📁 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.
You can try the pre-compiled 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.
Copied to your clipboardaddon_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 calleraddon_deferred promiseValue = nullptr;addon_value deferred = nullptr;apis.uxp_addon_create_promise(env, &deferred, &promiseValue);// Schedule work on the main threadapis.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 threadapis.uxp_addon_schedule_on_javascript_queue(env, ScriptThreadTask, ...);}void ScriptThreadTask(...) {apis.uxp_addon_open_handle_scope(env, &scope);// Resolve (or reject) the JavaScript promiseaddon_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:
Copied to your clipboardlet 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.
You can package and install a Hybrid plugin with only a subset of architectures—the plugin will simply fail to load on unsupported platforms. However, the Creative Cloud Marketplace requires all three architectures; the Developer Distribution portal will reject your .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).
Since Hybrid plugins include native code, users will be prompted for OS administrator credentials during installation and updates. This is expected behavior.
For the full distribution workflow (Marketplace submission, independent distribution, enterprise deployment), see the Share & Distribute section.
