{"record":{"id":"b0493857c62574cb","repo":"codex-team/editor.js","slug":"shortcut-shortcut-name-is-already-registered-fo","errorCode":null,"errorMessage":"Shortcut ${shortcut.name} is already registered for ${shortcut.on}. Please remove it before add a new handler.","messagePattern":"Shortcut (.+?) is already registered for (.+?)\\. Please remove it before add a new handler\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/components/utils/shortcuts.ts","lineNumber":57,"sourceCode":" */\nclass Shortcuts {\n  /**\n   * All registered shortcuts\n   *\n   * @type {Map<Element, Shortcut[]>}\n   */\n  private registeredShortcuts: Map<Element, Shortcut[]> = new Map();\n\n  /**\n   * Register shortcut\n   *\n   * @param shortcut - shortcut options\n   */\n  public add(shortcut: ShortcutData): void {\n    const foundShortcut = this.findShortcut(shortcut.on, shortcut.name);\n\n    if (foundShortcut) {\n      throw Error(\n        `Shortcut ${shortcut.name} is already registered for ${shortcut.on}. Please remove it before add a new handler.`\n      );\n    }\n\n    const newShortcut = new Shortcut({\n      name: shortcut.name,\n      on: shortcut.on,\n      callback: shortcut.handler,\n    });\n    const shortcuts = this.registeredShortcuts.get(shortcut.on) || [];\n\n    this.registeredShortcuts.set(shortcut.on, [...shortcuts, newShortcut]);\n  }\n\n  /**\n   * Remove shortcut\n   *\n   * @param element - Element shortcut is set for","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/codex-team/editor.js/blob/5f45dabbe5690b7033b2f9047d3985add5045fdd/src/components/utils/shortcuts.ts#L39-L75","documentation":"Editor.js's Shortcuts registry (used for keyboard shortcut handling on editor blocks) throws this when you attempt to register a shortcut with a name that is already registered for the same event target (the on field, e.g. 'keydown'). The API is add/remove based, so re-registering requires an explicit removal first.","triggerScenarios":"Calling shortcuts.add({ name: 'CMD+B', on: 'keydown', handler }) twice with the same name and on value, e.g. when a plugin's prepare() runs again, when a custom tool is re-initialized, or when module code subscribes in a lifecycle hook that fires multiple times (mount/unmount cycles in React).","commonSituations":"React/Vue components that create Editor.js instances or register shortcuts in useEffect/componentDidMount without cleanup, hot-module reloading re-running registration code, or plugin code that adds shortcuts on every block focus/change event. Typically the handler registration code is idempotent-assuming but the library is not.","solutions":["Guard the registration: call findShortcut(on, name) (or track registered names yourself) and only add when not already present.","Call shortcuts.remove(name, on) before shortcuts.add(...) when re-registering intentionally.","In framework components, register shortcuts once (empty deps in React useEffect) and clean up with shortcuts.remove on unmount to avoid duplicate registrations on remount.","If the error comes from a third-party tool's lifecycle, ensure the editor/tool is destroyed properly (editor.destroy()) before re-initializing instead of stacking instances."],"exampleFix":"// before\nshortcuts.add({ name: 'CMD+B', on: 'keydown', handler: this.handleB });\n\n// after\nif (!shortcuts.findShortcut('keydown', 'CMD+B')) {\n  shortcuts.add({ name: 'CMD+B', on: 'keydown', handler: this.handleB });\n} else {\n  shortcuts.remove('CMD+B', 'keydown');\n  shortcuts.add({ name: 'CMD+B', on: 'keydown', handler: this.handleB });\n}","handlingStrategy":"validation","validationCode":"function registerShortcut(shortcuts, data) {\n  if (shortcuts.findShortcut(data.on, data.name)) {\n    shortcuts.remove(data.name, data.on);\n  }\n  shortcuts.add(data);\n}\n\n// usage: registerShortcut(this.shortcuts, { name: 'CMD+B', on: 'keydown', handler });","typeGuard":"function isShortcutRegistered(shortcuts, data) {\n  return Boolean(shortcuts.findShortcut(data.on, data.name));\n}","tryCatchPattern":"try {\n  shortcuts.add(shortcutData);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('is already registered')) {\n    shortcuts.remove(shortcutData.name, shortcutData.on);\n    shortcuts.add(shortcutData);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Register shortcuts only once per editor instance; avoid doing it in per-block or per-keystroke handlers.","In React, use useEffect with [] deps (or proper cleanup calling shortcuts.remove) to survive remounts.","Wrap registration in a findShortcut/remove guard, especially in HMR or dev hot-reload environments."],"tags":["editorjs","shortcuts","keyboard","duplicate-registration","lifecycle"],"backgroundTag":"duplicate-event-handler-registration","analyzedSha":"5f45dabbe5690b7033b2f9047d3985add5045fdd","analyzedAt":"2026-08-27T22:50:23.124Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}