Create a JavaScript plugin
This guide covers creating JavaScript (JS) plugins that extend the Deephaven web UI with custom React components. For plugins that extend the Python client API with custom RPC methods, see Create your own plugin.
JS plugins serve static JavaScript, CSS, and other assets from the Deephaven server. The web UI automatically loads registered plugins on startup.
Prerequisites
Before creating a JS plugin, you should be familiar with:
When to create a JS plugin
Create a JS plugin when you need to:
- Add custom visualization components to the Deephaven web UI.
- Integrate third-party charting or UI libraries (D3, Chart.js, etc.).
- Create reusable UI components that can be shared across projects.
- Build components that require complex client-side interactivity.
If you only need custom UI for a single project without sharing, consider using deephaven.ui components directly.
Quick start with cookiecutter
The easiest way to create a JS plugin is with the cookiecutter templates from the deephaven-plugins repository:
This creates a complete project with Python registration, React scaffolding, and build configuration. The element template creates an element plugin that extends deephaven.ui, which is the right choice for most plugins. If you need full control over the messages sent between the server and the client, use --directory="templates/widget" instead to create a widget plugin.
Plugin architecture
A JS plugin typically consists of two parts:
- Python package: Registers the plugin with the Deephaven server and specifies where the JS assets are located.
- JavaScript bundle: Contains the React components and any other client-side code.
A plugin that only contains JavaScript can skip the Python package. Instead, package it with the pack-plugins.sh script from the web-plugin-packager image and copy the js-plugins directory it generates to <configDir>/js-plugins/. The script extracts each npm package and writes the manifest.json file that lists them. The server only loads plugins listed in that manifest, so copying an npm package into the directory by hand doesn't register it. See Configure JS plugins.
Python registration
The Python side uses deephaven.plugin.js.JsPlugin to register the plugin. This class tells the server where to find the JS assets:
The pyproject.toml must register the plugin as an entry point:
The path method must point to a directory inside the installed Python package that contains the built JS bundle. Installing the Python package doesn't copy the JS on its own, so add a setup.py that copies it in with package_js from deephaven-plugin-packaging. The following example assumes the JS project lives in src/js/ and the Python package in src/my_plugin/:
package_js runs npm pack on the JS project, so the copied directory contains the files listed in the files field of package.json. The cookiecutter templates include this step already.
Plugin types
A JS plugin's entry point should have a default export that the Deephaven web UI can load. The default export is a plugin object that tells the web UI what kind of plugin it is and which React components to use.
Every plugin object has a name and a type. The name identifies the plugin and must be unique. The type is one of the values in PluginType from the @deephaven/plugin package, and it determines which other properties the web UI expects. For the full set of properties each type accepts, see PluginTypes.ts in the web-client-ui repository.
| Type | Purpose |
|---|---|
PluginType.ELEMENT_PLUGIN | Maps custom element names to React components for deephaven.ui. |
PluginType.WIDGET_PLUGIN | Renders a server-side object in a panel. The supportedTypes property lists the server object types the plugin handles, and component renders them. |
PluginType.DASHBOARD_PLUGIN | Mounts a component once per dashboard. Use it to register custom panel types or respond to dashboard events. |
PluginType.TABLE_PLUGIN | Adds a custom component to table panels. |
PluginType.THEME_PLUGIN | Provides one or more custom themes. See Custom themes. |
PluginType.AUTH_PLUGIN | Adds a login method to the web UI. |
PluginType.MIDDLEWARE_PLUGIN | Wraps the component of a widget plugin to add behavior without replacing it. Requires Community Core 41.7 or later (web UI 1.19.0 or later). |
PluginType.MULTI_PLUGIN | Bundles several of the plugins above into one package. See Register multiple plugins from one package. |
The following src/js/src/index.tsx exports an element plugin, which is what the element cookiecutter template generates. Each key in mapping is an element name that a deephaven.ui component on the server refers to, and each value is the React component that renders it:
A widget plugin instead renders a server-side object directly. Its supportedTypes value must match the name of an object type registered on the server. The object type can come from any server plugin, in Python or Java, not just from the same package as the JS plugin. If no plugin on the server registers a matching object type, the widget plugin never renders anything. See Create your own plugin for how to register an object type. The following src/js/src/MyWidgetPlugin.tsx defines a widget plugin:
To use it as the package's only plugin, make it the default export of src/js/src/index.tsx:
Build configuration
Key requirements for the JS bundle:
- Use a scoped package name like
@your-org/your-plugin(official Deephaven plugins use@deephaven/js-plugin-<name>). - Export as a CommonJS (CJS) bundle.
- Externalize the shared dependencies that the web UI provides at runtime, so your plugin uses the same copies as the rest of the UI. The web UI provides
react,react-dom,redux,react-redux,@adobe/react-spectrum, and these Deephaven packages:@deephaven/auth-plugins,@deephaven/chart,@deephaven/components,@deephaven/console,@deephaven/dashboard,@deephaven/dashboard-core-plugins,@deephaven/icons,@deephaven/iris-grid,@deephaven/jsapi-bootstrap,@deephaven/jsapi-components,@deephaven/jsapi-utils,@deephaven/log,@deephaven/plugin, and@deephaven/react-hooks. The current list is inremote-component.config.ts. Don't externalize any other package, including other@deephaven/*packages — the web UI can't supply them, so the plugin fails to load. Bundle those into your plugin instead.
Example package.json:
Example vite.config.ts:
Register multiple plugins from one package
A package's default export normally registers a single plugin. To register several plugins from one package, export a MultiPlugin instead. A MultiPlugin is a plugin object with type: PluginType.MULTI_PLUGIN and a plugins array. When the web UI loads the package, it registers each plugin in the array individually, under that plugin's own name.
A MultiPlugin is useful when a package needs to:
- Provide more than one kind of plugin, such as a widget plugin and a dashboard plugin.
- Keep a legacy plugin registered for backward compatibility while adding a newer one. For example, a dashboard plugin can continue to open panels saved in existing dashboards while a widget plugin handles new ones.
- Register several widget plugins, each with its own
supportedTypes,title, oricon.
Note
MultiPlugin requires Deephaven Community Core 41.5 or later (web UI 1.17.0 or later). Earlier versions don't recognize the MultiPlugin type: the web UI logs a "missing an exported value" error to the browser console and loads none of the plugins in the array.
The following src/js/src/index.tsx registers a widget plugin and a dashboard plugin from the same package. It imports the widget plugin from the MyWidgetPlugin.tsx file shown in Plugin types, and makes the MultiPlugin, rather than the widget plugin, the default export. This is the pattern the official plotly-express plugin uses:
Keep the following rules in mind:
- Give every plugin in the
pluginsarray a unique, non-emptyname. A common convention is to append a suffix to the package name, such as@my-org/my-plugin.DashboardPlugin. In Community Core 41.7 and later (web UI 1.19.0 and later), the web UI skips any inner plugin whosetypeisn't a recognized plugin type or whosenameis empty, and logs a warning to the browser console. It doesn't check type-specific properties such ascomponent, and every entry must be an object. Earlier versions register every entry without checking it, so an invalid entry isn't reported. - Don't nest a
MultiPlugininside anotherMultiPlugin. Nesting isn't supported. - The Python registration doesn't change. The
JsPluginclass still points to a singlemainfile. TheMultiPluginis only the default export of that file.
Development workflow
- Build the JS:
npm install && npm run buildinsrc/js/. - Install the Python package:
pip install -e ./path/to/my-plugin. This runssetup.py, which copies the built bundle into the Python package. - Start Deephaven — the plugin loads automatically.
- Iterate: edit JS code, rebuild, reinstall the Python package so it picks up the new bundle, and refresh the web UI.
For faster iteration with hot module replacement, see the deephaven-plugins development documentation.
Complete examples
The best way to learn JS plugin development is to study existing plugins. The deephaven-plugins repository contains production-ready examples:
plotly-express: Plotly visualization integration. Uses aMultiPluginto register a widget plugin and a legacy dashboard plugin.matplotlib: Matplotlib figure support.ui: Thedeephaven.uiframework itself. Also uses aMultiPlugin.
Each plugin demonstrates:
- Python registration with
JsPlugin. - React component structure.
- Data flow between Python and JavaScript.
- Build configuration with Vite.
For a guided setup, use the cookiecutter templates which generate a complete working project structure.