|
| 1 | +--- |
| 2 | +title: Create a JavaScript plugin |
| 3 | +--- |
| 4 | + |
| 5 | +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](./create-plugins.md). |
| 6 | + |
| 7 | +JS plugins serve static JavaScript, CSS, and other assets from the Deephaven server. The web UI automatically loads registered plugins on startup. |
| 8 | + |
| 9 | +## Prerequisites |
| 10 | + |
| 11 | +Before creating a JS plugin, you should be familiar with: |
| 12 | + |
| 13 | +- [React](https://react.dev/) and TypeScript/JavaScript |
| 14 | +- [npm](https://www.npmjs.com/) package management |
| 15 | +- Python packaging basics |
| 16 | + |
| 17 | +## When to create a JS plugin |
| 18 | + |
| 19 | +Create a JS plugin when you need to: |
| 20 | + |
| 21 | +- Add custom visualization components to the Deephaven web UI. |
| 22 | +- Integrate third-party charting or UI libraries (D3, Chart.js, etc.). |
| 23 | +- Create reusable UI components that can be shared across projects. |
| 24 | +- Build components that require complex client-side interactivity. |
| 25 | + |
| 26 | +If you only need custom UI for a single project without sharing, consider using [`deephaven.ui`](./deephaven-ui.md) components directly. |
| 27 | + |
| 28 | +## Quick start with cookiecutter |
| 29 | + |
| 30 | +The easiest way to create a JS plugin is with the [cookiecutter](https://cookiecutter.readthedocs.io/) templates from the [deephaven-plugins](https://github.com/deephaven/deephaven-plugins) repository: |
| 31 | + |
| 32 | +```bash |
| 33 | +pip install cookiecutter |
| 34 | +cookiecutter gh:deephaven/deephaven-plugins --directory="templates/element" |
| 35 | +``` |
| 36 | + |
| 37 | +This creates a complete project with Python registration, React scaffolding, and build configuration. |
| 38 | + |
| 39 | +## Plugin architecture |
| 40 | + |
| 41 | +A JS plugin consists of two parts: |
| 42 | + |
| 43 | +1. **Python package**: Registers the plugin with the Deephaven server and specifies where the JS assets are located. |
| 44 | +2. **JavaScript bundle**: Contains the React components and any other client-side code. |
| 45 | + |
| 46 | +### Python registration |
| 47 | + |
| 48 | +The Python side uses [`deephaven.plugin.js.JsPlugin`](https://github.com/deephaven/deephaven-plugin/blob/main/src/deephaven/plugin/js.py) to register the plugin. This class tells the server where to find the JS assets: |
| 49 | + |
| 50 | +```python skip-test |
| 51 | +from deephaven.plugin.js import JsPlugin |
| 52 | +from deephaven.plugin import Registration, Callback |
| 53 | +import pathlib |
| 54 | + |
| 55 | + |
| 56 | +class MyPluginJsPlugin(JsPlugin): |
| 57 | + @property |
| 58 | + def name(self) -> str: |
| 59 | + # Contents served at js-plugins/{name}/ |
| 60 | + return "@my-org/my-plugin" |
| 61 | + |
| 62 | + @property |
| 63 | + def version(self) -> str: |
| 64 | + return "0.1.0" |
| 65 | + |
| 66 | + @property |
| 67 | + def main(self) -> str: |
| 68 | + # Path to main JS file, relative to path() |
| 69 | + return "dist/index.js" |
| 70 | + |
| 71 | + def path(self) -> pathlib.Path: |
| 72 | + # Directory containing built JS assets |
| 73 | + return pathlib.Path(__file__).parent / "js" |
| 74 | + |
| 75 | + |
| 76 | +class MyPluginRegistration(Registration): |
| 77 | + @classmethod |
| 78 | + def register_into(cls, callback: Callback) -> None: |
| 79 | + callback.register(MyPluginJsPlugin()) |
| 80 | +``` |
| 81 | + |
| 82 | +The `pyproject.toml` must register the plugin as an entry point: |
| 83 | + |
| 84 | +```toml |
| 85 | +[build-system] |
| 86 | +requires = ["setuptools"] |
| 87 | +build-backend = "setuptools.build_meta" |
| 88 | + |
| 89 | +[project] |
| 90 | +name = "my-plugin" |
| 91 | +version = "0.1.0" |
| 92 | +dependencies = ["deephaven-plugin>=0.6.0"] |
| 93 | + |
| 94 | +[project.entry-points."deephaven.plugin"] |
| 95 | +registration_cls = "my_plugin:MyPluginRegistration" |
| 96 | +``` |
| 97 | + |
| 98 | +### JavaScript structure |
| 99 | + |
| 100 | +The JS plugin must export modules that the Deephaven web UI can load. For element plugins extending `deephaven.ui`, the main export is typically a React component. |
| 101 | + |
| 102 | +Key requirements for JS plugins: |
| 103 | + |
| 104 | +- Use a scoped package name like `@your-org/your-plugin` (official Deephaven plugins use `@deephaven/js-plugin-<name>`). |
| 105 | +- Export as a CommonJS (CJS) bundle. |
| 106 | +- Externalize shared dependencies: `react`, `react-dom`, `redux`, `react-redux`, and `@deephaven/*` packages. |
| 107 | + |
| 108 | +Example `package.json`: |
| 109 | + |
| 110 | +```json |
| 111 | +{ |
| 112 | + "name": "@my-org/my-plugin", |
| 113 | + "version": "0.1.0", |
| 114 | + "type": "module", |
| 115 | + "scripts": { |
| 116 | + "build": "vite build" |
| 117 | + }, |
| 118 | + "devDependencies": { |
| 119 | + "@vitejs/plugin-react": "^4.0.0", |
| 120 | + "typescript": "^5.0.0", |
| 121 | + "vite": "^5.0.0" |
| 122 | + }, |
| 123 | + "peerDependencies": { |
| 124 | + "react": "^18.2.0", |
| 125 | + "react-dom": "^18.2.0" |
| 126 | + } |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +Example `vite.config.ts`: |
| 131 | + |
| 132 | +```typescript |
| 133 | +import react from "@vitejs/plugin-react"; |
| 134 | +import { defineConfig } from "vite"; |
| 135 | + |
| 136 | +export default defineConfig({ |
| 137 | + plugins: [react()], |
| 138 | + build: { |
| 139 | + lib: { |
| 140 | + entry: "src/index.tsx", |
| 141 | + formats: ["cjs"], |
| 142 | + fileName: () => "index.js", |
| 143 | + }, |
| 144 | + rollupOptions: { |
| 145 | + external: [ |
| 146 | + "react", |
| 147 | + "react-dom", |
| 148 | + "redux", |
| 149 | + "react-redux", |
| 150 | + /@deephaven\/.*/, |
| 151 | + ], |
| 152 | + }, |
| 153 | + outDir: "dist", |
| 154 | + }, |
| 155 | +}); |
| 156 | +``` |
| 157 | + |
| 158 | +## Development workflow |
| 159 | + |
| 160 | +1. Build the JS: `npm install && npm run build`. |
| 161 | +2. Install the Python package: `pip install -e ./path/to/my-plugin`. |
| 162 | +3. Start Deephaven - the plugin loads automatically. |
| 163 | +4. Iterate: edit JS code, rebuild, refresh the web UI. |
| 164 | + |
| 165 | +For faster iteration with hot module replacement, see the [deephaven-plugins development documentation](https://github.com/deephaven/deephaven-plugins#development). |
| 166 | + |
| 167 | +## Complete examples |
| 168 | + |
| 169 | +The best way to learn JS plugin development is to study existing plugins. The [deephaven-plugins](https://github.com/deephaven/deephaven-plugins) repository contains production-ready examples: |
| 170 | + |
| 171 | +- [`plotly-express`](https://github.com/deephaven/deephaven-plugins/tree/main/plugins/plotly-express): Plotly visualization integration. |
| 172 | +- [`matplotlib`](https://github.com/deephaven/deephaven-plugins/tree/main/plugins/matplotlib): Matplotlib figure support. |
| 173 | +- [`json`](https://github.com/deephaven/deephaven-plugins/tree/main/plugins/json): JSON viewer component. |
| 174 | +- [`ui`](https://github.com/deephaven/deephaven-plugins/tree/main/plugins/ui): The deephaven.ui framework itself. |
| 175 | + |
| 176 | +Each plugin demonstrates: |
| 177 | + |
| 178 | +- Python registration with `JsPlugin`. |
| 179 | +- React component structure. |
| 180 | +- Data flow between Python and JavaScript. |
| 181 | +- Build configuration with Vite. |
| 182 | + |
| 183 | +For a guided setup, use the cookiecutter templates which generate a complete working project structure. |
| 184 | + |
| 185 | +## Related documentation |
| 186 | + |
| 187 | +- [Install and use plugins](./install-use-plugins.md) |
| 188 | +- [Create your own bidirectional Python plugins](./create-plugins.md) |
| 189 | +- [deephaven.ui](./deephaven-ui.md) |
| 190 | +- [deephaven-plugins repository](https://github.com/deephaven/deephaven-plugins) |
| 191 | +- [JsPlugin Python class](https://github.com/deephaven/deephaven-plugin/blob/main/src/deephaven/plugin/js.py) |
0 commit comments