This is a plugin template for Zotero.
[!tip] 👁 Watch this repo so that you can be notified whenever there are fixes & updates.
If you are using this repo, I recommended that you put the following badge on your README:
[](https://github.com/windingwind/zotero-plugin-template)
src/modules/examples.ts, covering most of the commonly used APIs in plugins (using zotero-plugin-toolkit);development / production);This repo provides examples for zotero-plugin-toolkit APIs.
Search @example in src/examples.ts. The examples are called in src/hooks.ts.


See src/modules/preferenceScript.ts

An Obsidian-style prompt(popup command input) module. It accepts text command to run callback, with optional display in the popup.
Activate with Shift+P.

[!note] This guide assumes that you have an initial understanding of the basic structure and workings of the Zotero plugin. If you don't, please refer to the documentation and official plugin examples Make It Red first.
Click Use this template
Git clone your new repo
GitHub CodeSpace enables you getting started without the need to download code/IDE/dependencies locally.
Replace the steps above and build you first plugin in 30 seconds!
Use this template, click Open in codespace. You may need to login to your GitHub account.Enter the repo folder
Modify the settings in ./package.json, including:
{
"version": "0.0.0",
"description": "",
"config": {
"addonName": "", // name to be displayed in the plugin manager
"addonID": "", // ID to avoid conflict. IMPORTANT!
"addonRef": "", // e.g. Element ID prefix
"addonInstance": "", // the plugin's root instance: Zotero.${addonInstance}
"prefsPrefix": "extensions.zotero.${addonRef}", // the prefix of prefs
},
"repository": {
"type": "git",
"url": "git+https://github.com/your-github-name/repo-name.git",
},
"author": "Your Name",
"bugs": {
"url": "https://github.com/your-github-name/repo-name/issues",
},
"homepage": "https://github.com/your-github-name/repo-name#readme",
}
[!warning] Be careful to set the addonID and addonRef to avoid conflict.
If you need to host your XPI packages outside of GitHub, modify updateURL and add xpiDownloadLink in zotero-plugin.config.ts.
Copy the environment variable file. Modify the commands that starts your installation of the beta Zotero.
Create a development profile (Optional)
Start the beta Zotero with/path/to/zotero -p. Create a new profile and use it as your development profile. Do this only once
cp .env.example .env
vim .env
If you are developing more than one plugin, you can store the bin path and profile path in the system environment variables, which can be omitted here.
Install dependencies with npm install
If you are using
pnpmas the package manager for your project, you need to addpublic-hoist-pattern[]=*@types/bluebird*to.npmrc, see https://github.com/windingwind/zotero-types?tab=readme-ov-file#usage.
If you get npm ERR! ERESOLVE unable to resolve dependency tree with npm install, which is an upstream dependency bug of typescript-eslint, use the npm i -f command to install it.
Start development server with npm start, it will:
build/src/** and addon/**, rebuild and reload plugin in Zotero when source code changed.Tired of endless restarting? Forget about it!
npm start.When file changes are detected in src or addon, the plugin will be automatically compiled and reloaded.
Please see zotero-plugin-scaffold.
You can also:
Zotero.debug(). Find the outputs in Help->Debug Output Logging->View Output;XUL Documentation: http://www.devdoc.net/web/developer.mozilla.org/en-US/docs/XUL.html
Run npm run build to build the plugin in production mode. The build output will be located in the .scaffold/build/ directory.
For detailed build steps, refer to the zotero-plugin-scaffold documentation. In short, the process can be divided into the following steps:
build/ directoryaddon/** to .scaffold/build/addon/**package.json**/*.flt to **/${addonRef}-*.fltaddonRef-package.json#prefsPrefix and generate type declaration files for preferences.ts source code to .js, building from src/index.ts to .scaffold/build/addon/content/scripts.scaffold/build/addon directory into .scaffold/build/*.xpiupdate.json or update-beta.json[!note]
What's the difference between dev & prod?
- This environment variable is stored in
Zotero.${addonInstance}.data.env. The outputs to console is disabled in prod mode.- You can decide what users cannot see/use based on this variable.
- In production mode, the build script will pack the plugin and update the
update.json.
To build and release, use
# version increase, git add, commit and push
# then on ci, npm run build, and release to GitHub
npm run release
[!note] This will use Bumpp to prompt for the new version number, locally bump the version, run any (pre/post)version scripts defined in
package.json, commit, build (optional), tag the commit with the version number and push commits and git tags. Bumpp can be configured inzotero-plugin-config.ts; for example, addrelease: { bumpp: { execute: "npm run build" } }to also build before committing.Subsequently GitHub Action will rebuild the plugin and use
zotero-plugin-scaffold'sreleasescript to publish the XPI to GitHub Release. In addition, a separate release (tag:release) will be created or updated that includes update manifestsupdate.jsonandupdate-beta.jsonas assets. These will be available athttps://github.com/{{owner}}/{{repo}}/releases/download/release/update*.json.
The template defines prerelease as the beta version of the plugin, when you select a prerelease version in Bumpp (with - in the version number). The build script will create a new update-beta.json for prerelease use, which ensures that users of the regular version won't be able to update to the beta. Only users who have manually downloaded and installed the beta will be able to update to the next beta automatically.
When the next regular release is updated, both update.json and update-beta.json will be updated (on the special release release, see above) so that both regular and beta users can update to the new regular release.
[!warning] Strictly, distinguishing between Zotero 6 and Zotero 7 compatible plugin versions should be done by configuring
applications.zotero.strict_min_versioninaddons.__addonID__.updates[]ofupdate.jsonrespectively, so that Zotero recognizes it properly, see https://www.zotero.org/support/dev/zotero_7_for_developers#updaterdf_updatesjson.
See also
src/hooks.ts
bootstrap.js > startup is called
index.js (the main entrance of plugin code, built from index.ts)index.js, the plugin object is injected under Zotero and hooks.ts > onStartup is called.
bootstrap.js > shutdown is called.
events.ts > onShutdown is called. Remove UI elements, preference panes, or anything created by the plugin.See also
src/index.ts
The bootstrapped plugin runs in a sandbox, which does not have default global variables like Zotero or window, which we used to have in the overlay plugins' window environment.
This template registers the following variables to the global scope:
Zotero, ZoteroPane, Zotero_Tabs, window, document, rootURI, ztoolkit, addon;
The plugin template provides new APIs for bootstrap plugins. We have two reasons to use these APIs, instead of the createElement/createElementNS:
createElement, the plugin template will maintain these elements. Just unregisterAll at the exit.createXULElement. The React.createElement-like API createElement detects namespace(xul/html/svg) and creates elements automatically, with the return element in the corresponding TS element type.createElement(document, "div"); // returns HTMLDivElement
createElement(document, "hbox"); // returns XUL.Box
createElement(document, "button", { namespace: "xul" }); // manually set namespace. returns XUL.Button
Zotero docs are outdated and incomplete. Clone https://github.com/zotero/zotero and search the keyword globally.
⭐The zotero-types provides most frequently used Zotero APIs. It's included in this template by default. Your IDE would provide hint for most of the APIs.
A trick for finding the API you want:
Search the UI label in .xhtml/.flt files, find the corresponding key in locale file. Then search this keys in .js/.jsx files.
This section shows the directory structure of a template.
.js/.ts code files are in ./src;./addon/manifest.json;./addon/content/*.xhtml../addon/locale/**/*.flt;./addon/prefs.js;.
|-- .github/ # github conf
|-- .vscode/ # vscode conf
|-- addon # static files
| |-- bootstrap.js
| |-- content
| | |-- icons
| | | |-- favicon.png
| | | `-- favicon@0.5x.png
| | |-- preferences.xhtml
| | `-- zoteroPane.css
| |-- locale
| | |-- en-US
| | | |-- addon.ftl
| | | |-- mainWindow.ftl
| | | `-- preferences.ftl
| | `-- zh-CN
| | |-- addon.ftl
| | |-- mainWindow.ftl
| | `-- preferences.ftl
| |-- manifest.json
| `-- prefs.js
|-- build # build dir
|-- node_modules
|-- src # source code of scripts
| |-- addon.ts # base class
| |-- hooks.ts # lifecycle hooks
| |-- index.ts # main entry
| |-- modules # sub modules
| | |-- examples.ts
| | `-- preferenceScript.ts
| `-- utils # utilities
| |-- locale.ts
| |-- prefs.ts
| |-- wait.ts
| |-- window.ts
| `-- ztoolkit.ts
|-- typings # ts typings
| `-- global.d.ts
|-- .env # enviroment config (do not check into repo)
|-- .env.example # template of enviroment config, https://github.com/northword/zotero-plugin-scaffold
|-- .gitignore # git conf
|-- .gitattributes # git conf
|-- .prettierrc # prettier conf, https://prettier.io/
|-- eslint.config.mjs # eslint conf, https://eslint.org/
|-- LICENSE
|-- package-lock.json
|-- package.json
|-- tsconfig.json # typescript conf, https://code.visualstudio.com/docs/languages/jsconfig
|-- README.md
`-- zotero-plugin.config.ts # scaffold conf, https://github.com/northword/zotero-plugin-scaffold
Use this code under AGPL. No warranties are provided. Keep the laws of your locality in mind!
If you want to change the license, please contact me at wyzlshx@foxmail.com
TypeScript
92.5%
Fluent
4.4%
HTML
2.0%
This is a plugin template for Zotero.
[!tip] 👁 Watch this repo so that you can be notified whenever there are fixes & updates.
If you are using this repo, I recommended that you put the following badge on your README:
[](https://github.com/windingwind/zotero-plugin-template)
src/modules/examples.ts, covering most of the commonly used APIs in plugins (using zotero-plugin-toolkit);development / production);This repo provides examples for zotero-plugin-toolkit APIs.
Search @example in src/examples.ts. The examples are called in src/hooks.ts.


See src/modules/preferenceScript.ts

An Obsidian-style prompt(popup command input) module. It accepts text command to run callback, with optional display in the popup.
Activate with Shift+P.

[!note] This guide assumes that you have an initial understanding of the basic structure and workings of the Zotero plugin. If you don't, please refer to the documentation and official plugin examples Make It Red first.
Click Use this template
Git clone your new repo
GitHub CodeSpace enables you getting started without the need to download code/IDE/dependencies locally.
Replace the steps above and build you first plugin in 30 seconds!
Use this template, click Open in codespace. You may need to login to your GitHub account.Enter the repo folder
Modify the settings in ./package.json, including:
{
"version": "0.0.0",
"description": "",
"config": {
"addonName": "", // name to be displayed in the plugin manager
"addonID": "", // ID to avoid conflict. IMPORTANT!
"addonRef": "", // e.g. Element ID prefix
"addonInstance": "", // the plugin's root instance: Zotero.${addonInstance}
"prefsPrefix": "extensions.zotero.${addonRef}", // the prefix of prefs
},
"repository": {
"type": "git",
"url": "git+https://github.com/your-github-name/repo-name.git",
},
"author": "Your Name",
"bugs": {
"url": "https://github.com/your-github-name/repo-name/issues",
},
"homepage": "https://github.com/your-github-name/repo-name#readme",
}
[!warning] Be careful to set the addonID and addonRef to avoid conflict.
If you need to host your XPI packages outside of GitHub, modify updateURL and add xpiDownloadLink in zotero-plugin.config.ts.
Copy the environment variable file. Modify the commands that starts your installation of the beta Zotero.
Create a development profile (Optional)
Start the beta Zotero with/path/to/zotero -p. Create a new profile and use it as your development profile. Do this only once
cp .env.example .env
vim .env
If you are developing more than one plugin, you can store the bin path and profile path in the system environment variables, which can be omitted here.
Install dependencies with npm install
If you are using
pnpmas the package manager for your project, you need to addpublic-hoist-pattern[]=*@types/bluebird*to.npmrc, see https://github.com/windingwind/zotero-types?tab=readme-ov-file#usage.
If you get npm ERR! ERESOLVE unable to resolve dependency tree with npm install, which is an upstream dependency bug of typescript-eslint, use the npm i -f command to install it.
Start development server with npm start, it will:
build/src/** and addon/**, rebuild and reload plugin in Zotero when source code changed.Tired of endless restarting? Forget about it!
npm start.When file changes are detected in src or addon, the plugin will be automatically compiled and reloaded.
Please see zotero-plugin-scaffold.
You can also:
Zotero.debug(). Find the outputs in Help->Debug Output Logging->View Output;XUL Documentation: http://www.devdoc.net/web/developer.mozilla.org/en-US/docs/XUL.html
Run npm run build to build the plugin in production mode. The build output will be located in the .scaffold/build/ directory.
For detailed build steps, refer to the zotero-plugin-scaffold documentation. In short, the process can be divided into the following steps:
build/ directoryaddon/** to .scaffold/build/addon/**package.json**/*.flt to **/${addonRef}-*.fltaddonRef-package.json#prefsPrefix and generate type declaration files for preferences.ts source code to .js, building from src/index.ts to .scaffold/build/addon/content/scripts.scaffold/build/addon directory into .scaffold/build/*.xpiupdate.json or update-beta.json[!note]
What's the difference between dev & prod?
- This environment variable is stored in
Zotero.${addonInstance}.data.env. The outputs to console is disabled in prod mode.- You can decide what users cannot see/use based on this variable.
- In production mode, the build script will pack the plugin and update the
update.json.
To build and release, use
# version increase, git add, commit and push
# then on ci, npm run build, and release to GitHub
npm run release
[!note] This will use Bumpp to prompt for the new version number, locally bump the version, run any (pre/post)version scripts defined in
package.json, commit, build (optional), tag the commit with the version number and push commits and git tags. Bumpp can be configured inzotero-plugin-config.ts; for example, addrelease: { bumpp: { execute: "npm run build" } }to also build before committing.Subsequently GitHub Action will rebuild the plugin and use
zotero-plugin-scaffold'sreleasescript to publish the XPI to GitHub Release. In addition, a separate release (tag:release) will be created or updated that includes update manifestsupdate.jsonandupdate-beta.jsonas assets. These will be available athttps://github.com/{{owner}}/{{repo}}/releases/download/release/update*.json.
The template defines prerelease as the beta version of the plugin, when you select a prerelease version in Bumpp (with - in the version number). The build script will create a new update-beta.json for prerelease use, which ensures that users of the regular version won't be able to update to the beta. Only users who have manually downloaded and installed the beta will be able to update to the next beta automatically.
When the next regular release is updated, both update.json and update-beta.json will be updated (on the special release release, see above) so that both regular and beta users can update to the new regular release.
[!warning] Strictly, distinguishing between Zotero 6 and Zotero 7 compatible plugin versions should be done by configuring
applications.zotero.strict_min_versioninaddons.__addonID__.updates[]ofupdate.jsonrespectively, so that Zotero recognizes it properly, see https://www.zotero.org/support/dev/zotero_7_for_developers#updaterdf_updatesjson.
See also
src/hooks.ts
bootstrap.js > startup is called
index.js (the main entrance of plugin code, built from index.ts)index.js, the plugin object is injected under Zotero and hooks.ts > onStartup is called.
bootstrap.js > shutdown is called.
events.ts > onShutdown is called. Remove UI elements, preference panes, or anything created by the plugin.See also
src/index.ts
The bootstrapped plugin runs in a sandbox, which does not have default global variables like Zotero or window, which we used to have in the overlay plugins' window environment.
This template registers the following variables to the global scope:
Zotero, ZoteroPane, Zotero_Tabs, window, document, rootURI, ztoolkit, addon;
The plugin template provides new APIs for bootstrap plugins. We have two reasons to use these APIs, instead of the createElement/createElementNS:
createElement, the plugin template will maintain these elements. Just unregisterAll at the exit.createXULElement. The React.createElement-like API createElement detects namespace(xul/html/svg) and creates elements automatically, with the return element in the corresponding TS element type.createElement(document, "div"); // returns HTMLDivElement
createElement(document, "hbox"); // returns XUL.Box
createElement(document, "button", { namespace: "xul" }); // manually set namespace. returns XUL.Button
Zotero docs are outdated and incomplete. Clone https://github.com/zotero/zotero and search the keyword globally.
⭐The zotero-types provides most frequently used Zotero APIs. It's included in this template by default. Your IDE would provide hint for most of the APIs.
A trick for finding the API you want:
Search the UI label in .xhtml/.flt files, find the corresponding key in locale file. Then search this keys in .js/.jsx files.
This section shows the directory structure of a template.
.js/.ts code files are in ./src;./addon/manifest.json;./addon/content/*.xhtml../addon/locale/**/*.flt;./addon/prefs.js;.
|-- .github/ # github conf
|-- .vscode/ # vscode conf
|-- addon # static files
| |-- bootstrap.js
| |-- content
| | |-- icons
| | | |-- favicon.png
| | | `-- favicon@0.5x.png
| | |-- preferences.xhtml
| | `-- zoteroPane.css
| |-- locale
| | |-- en-US
| | | |-- addon.ftl
| | | |-- mainWindow.ftl
| | | `-- preferences.ftl
| | `-- zh-CN
| | |-- addon.ftl
| | |-- mainWindow.ftl
| | `-- preferences.ftl
| |-- manifest.json
| `-- prefs.js
|-- build # build dir
|-- node_modules
|-- src # source code of scripts
| |-- addon.ts # base class
| |-- hooks.ts # lifecycle hooks
| |-- index.ts # main entry
| |-- modules # sub modules
| | |-- examples.ts
| | `-- preferenceScript.ts
| `-- utils # utilities
| |-- locale.ts
| |-- prefs.ts
| |-- wait.ts
| |-- window.ts
| `-- ztoolkit.ts
|-- typings # ts typings
| `-- global.d.ts
|-- .env # enviroment config (do not check into repo)
|-- .env.example # template of enviroment config, https://github.com/northword/zotero-plugin-scaffold
|-- .gitignore # git conf
|-- .gitattributes # git conf
|-- .prettierrc # prettier conf, https://prettier.io/
|-- eslint.config.mjs # eslint conf, https://eslint.org/
|-- LICENSE
|-- package-lock.json
|-- package.json
|-- tsconfig.json # typescript conf, https://code.visualstudio.com/docs/languages/jsconfig
|-- README.md
`-- zotero-plugin.config.ts # scaffold conf, https://github.com/northword/zotero-plugin-scaffold
Use this code under AGPL. No warranties are provided. Keep the laws of your locality in mind!
If you want to change the license, please contact me at wyzlshx@foxmail.com
TypeScript
92.5%
Fluent
4.4%
HTML
2.0%