Zotero 8 for Developers

Zotero 8 includes an internal upgrade of the Mozilla platform on which Zotero is based, incorporating changes from Firefox 115 through Firefox 140.

Most of the guidance from Zotero 7 for Developers is still relevant.

Feedback

If you have questions about anything on this page or encounter other problems while updating your plugin, let us know on the dev list. Please don't post to the Zotero Forums about Zotero 8 at this time.

Dev Builds

The dev channel has been paused. Use Zotero 8 beta builds for development.

Platform Changes

Mozilla Platform

The following list includes nearly all Mozilla changes that affected Zotero code. You may encounter other breaking changes if you use APIs not used in Zotero. Searchfox is the best resource for identifying current correct usage in Mozilla code and changes between Firefox 115 and Firefox 140.

Earlier Zotero 8.0 (formerly 7.1) beta releases were based on Firefox 128, so we've listed changes for Firefox 128 and 140 separately.

Firefox 115 → Firefox 128 ("Zotero 7.1" beta)

  • Manual Services.jsm imports must be removed (example)
  • nsIScriptableUnicodeConverter was removed; replace convertToByteArray() and convertToInputStream() (example)
  • nsIOSFileConstantsService was removed (example)
  • XPCOMUtils.defineLazyGetterChromeUtils.defineLazyGetter (example)
  • nsIDOMChromeWindow was removed (example)
  • Login manager: addLoginaddLoginAsync (example)
  • BrowsingContext should be passed to nsIFilePicker init (example, but we recommend using Zotero's FilePicker module instead)
  • DataTransfer#types: contains()includes() — now a standard array (example)
  • -moz-nativehyperlinktextLinkText (example)

Firefox 128 → Firefox 140 (Zotero 8)

  • All JSMs (.jsm files) in Firefox and Zotero were converted to ESMs (.mjs files, or .sys.mjs files for Firefox code) (example)
    • Zotero now uses standard JavaScript modules and import statements (example)
    • See Mozilla's primer on ESMification — in particular, the “Renamed New API” section
    • Zotero includes a script that can update most JSM-based code automatically. Copy the migrate-fx140 directory into your plugin's Git repo and run migrate-fx140/migrate.py esmify path/to/Module.jsm. To update the imports in a non-JSM file, run migrate-fx140/migrate.py esmify --imports path/to/file.js. Both commands also accept a directory for batch conversion.
    • Global imports are no longer supported. Imported modules need to be assigned to a variable. (example)
    • All ESMs run in strict mode
  • Bluebird was removed — Zotero now uses standard JavaScript promises everywhere (example)
    • Zotero provides a script that can update most Bluebird-based code automatically. Copy the migrate-fx140 directory into your plugin's Git repo and run migrate-fx140/migrate.py asyncify path/to/file.js. This command also accepts a directory for batch conversion.
    • For compatibility, Zotero.Promise.delay() and Zotero.Promise.defer() are still supported. defer() can no longer be called as a constructor (example).
    • Bluebird Promise instance methods like map(), filter(), each(), isResolved(), isPending(), and cancel() are no longer available. Collection methods can usually be replaced with iteration (example, example) or awaits (example). Complicated isPending() logic will need to be rewritten (example).
    • ZoteroProtocolHandler extensions: new AsyncChannel() now takes an async function, not a generator (example)
    • Zotero.spawn() was removed.
  • Services.appShell.hiddenDOMWindow was removed outside macOS — use only as a fallback (example)
  • ZOTERO_CONFIG needs to be imported (example)
  • Preference panes now run in their own global scope, so a var defined in one preference pane's script won't automatically be accessible to other preference panes. Set variables on window explicitly if you need to share them between preference panes.
  • Update button labels using the label property, not attribute (example)
  • The first segment of a zotero: URI is now parsed as its host, not as part of its path (example)

Plugin Changes

Custom Menu Items

A new API allows plugins to create custom menu items in Zotero's menu popups. Plugins should use this official API if possible rather than manually injecting content.

The example below shows registering a custom menu item and a submenu in Zotero's items list context menu.

let registeredID = Zotero.MenuManager.registerMenu({
	menuID: "test",
	pluginID: "example@example.com",
	target: "main/library/item",
	menus: [
		{
			menuType: "menuitem",
			l10nID: "menu-print",
			onShowing: (event, context) => {
				Zotero.debug("onShowing");
				Zotero.debug(Object.keys(context));
			},
			onCommand: (event, context) => {
				Zotero.debug("onCommand");
				Zotero.debug(Object.keys(context));
			},
		},
		{
			menuType: "submenu",
			l10nID: "menu-print",
			menus: [
				{
					menuType: "menuitem",
					l10nID: "menu-print",
					onShowing: (event, context) => {
						Zotero.debug("onShowing submenu");
						Zotero.debug(Object.keys(context));
						// Only for regular items
						context.setVisible(context.items?.every((item) => item.isRegularItem()));
					},
					onCommand: (event, context) => {
						Zotero.debug("onCommand submenu");
						Zotero.debug(Object.keys(context));
					},
				},
			],
		},
	],
});

Available targets:

  • Main window menubar menus
    • main/menubar/file — File menu in the main window menubar
    • main/menubar/edit — Edit menu in the main window menubar
    • main/menubar/view — View menu in the main window menubar
    • main/menubar/go — Go menu in the main window menubar
    • main/menubar/tools — Tools menu in the main window menubar
    • main/menubar/help — Help menu in the main window menubar
  • Main window library context menus
    • main/library/item — Context menu for library items
    • main/library/collection — Context menu for library collections
  • Main window toolbar & file menu submenus
    • main/library/addAttachment — “Add attachment” button/menu
    • main/library/addNote — “New note” button/menu
  • Main window tab context menus
    • main/tab — Context menu for main window tabs
  • Reader window menubar menus
    • reader/menubar/file — File menu in reader window
    • reader/menubar/edit — Edit menu in reader window
    • reader/menubar/view — View menu in reader window
    • reader/menubar/go — Go menu in reader window
    • reader/menubar/window — Window menu in reader window
  • Item pane context menus
    • itemPane/info/row — Context menu for item pane info rows
  • Notes pane add note buttons
    • notesPane/addItemNote — Add item note button
    • notesPane/addStandaloneNote — Add standalone note button
  • Sidenav buttons
    • sidenav/locate — Locate button in side navigation

More advanced options are documented in the source code.

When the plugin is disabled or uninstalled, custom menus with the corresponding pluginID will be automatically removed. If you want to unregister a custom menu manually, you can use unregisterMenu():

Zotero.MenuManager.unregisterMenu(registeredID);

If you encounter any problem with this API, please let us know on the dev list.

dev/zotero_8_for_developers.txt · Last modified: 2025/09/15 04:12 by xiangyu