Plugin Shortcuts

Learn how to configure keyboard shortcuts.

Keyboard shortcuts are essential for a fast and productive editing workflow. Plate allows you to easily define and customize shortcuts for your editor plugins.

Defining Shortcuts

You can add or modify shortcuts for any plugin when you create or configure it (e.g., using createPlatePlugin().extend({...}) or ExistingPlugin.configure({...})). Shortcuts are defined within the shortcuts field of your plugin configuration.

There are two primary ways to define what a shortcut does:

The most straightforward way to create a shortcut is by linking it to an existing method within your plugin. This can be either a transform method or an API method. Transforms are functions that modify the editor's state (e.g., toggling a mark, inserting an element), while API methods provide other functionality.

To do this:

  1. Ensure the name of your shortcut in the shortcuts configuration object matches the name of the method (e.g., a shortcut named toggle will look for a transform named toggle, or if no transform exists, an API method named toggle).
  2. Provide the keys (the key combination) for the shortcut.

Plate will automatically find and call the corresponding method when the specified keys are pressed.

plugins/my-document-plugin.ts
import { createPlatePlugin, Key } from 'platejs/react';
 
// Example: A simplified plugin with both transforms and API
export const MyDocumentPlugin = createPlatePlugin({
  key: 'doc',
})
// Define editor.tf.doc.format()
.extendTransforms(({ editor, type }) => ({
  format: () => {
    editor.tf.normalize({ force: true });
  },
}))
// Define editor.api.doc.format()
.extendApi(({ editor, type }) => ({
  save: async () => {
    // Save the document
    // await fetch(...);
  },
}))
.extend({ // Or .configure() if extending an existing plugin
  shortcuts: {
    // This will call editor.tf.doc.format()
    format: {
      keys: [[Key.Mod, Key.Shift, 'f']], // e.g., Cmd/Ctrl + Shift + F
    },
    // This will call editor.api.doc.save()
    save: {
      keys: [[Key.Mod, 's']], // e.g., Cmd/Ctrl + S
    },
  },
});

The name of the shortcut (e.g., toggle in the example) is crucial as Plate uses it to locate the matching method on the plugin. It first looks for a transform method, then falls back to an API method if no transform exists with that name.

2. Using a Custom Handler

For actions that require more complex logic, depends on the keyboard event, or if there isn't a direct one-to-one mapping with an existing transform name, you can provide a custom handler function. This function will be executed when the shortcut is activated.

plugins/custom-logger-plugin.ts
import { createPlatePlugin, Key } from 'platejs/react';
 
export const CustomLoggerPlugin = createPlatePlugin({
  key: 'customLogger',
}).extend({
  shortcuts: {
    logEditorState: {
      keys: [[Key.Mod, Key.Alt, 's']], // e.g., Cmd/Ctrl + Alt + S
      handler: ({ editor, event, eventDetails }) => {
        // 'editor' is the PlateEditor instance
        // 'event' is the raw KeyboardEvent
        // 'eventDetails' provides more context from the hotkey library
        console.info('Current editor value:', editor.children);
        console.info('Pressed keys:', eventDetails.keys);
        // You might want to prevent other actions or browser defaults
        // event.preventDefault();
      },
    },
  },
});

Shortcut Configuration Properties

When defining or configuring a shortcut, you can use the following properties in its configuration object:

  • keys: Required. The key combination(s) that trigger the shortcut.
    • This can be a string like 'mod+b' or an array using the Key enum for more explicit control (e.g., [[Key.Mod, Key.Shift, 'x']]).
    • Key.Mod is a convenient way to specify Cmd on macOS and Ctrl on other operating systems.
  • handler: (Optional) A function that is called when the shortcut is activated. Its signature is: ({ editor: PlateEditor; event: KeyboardEvent; eventDetails: HotkeysEvent; }) => void; If you omit the handler, Plate will attempt to call a matching transform based on the shortcut's name. Note: If your transform or handler returns false (e.g. not handled), preventDefault will NOT be called, allowing other handlers or browser defaults to take over. Any other return value will use the default preventDefault behavior.
  • preventDefault: (Optional) A boolean. If set to true, it prevents the browser's default action for that key combination (e.g., Mod+B typically bolds text in the browser itself). Defaults to true. This is suitable for most editor-specific shortcuts. Set to false if you need to allow the browser's default action or enable other handlers to process the event, especially if your handler might not always perform an action (e.g., an indent command that doesn't apply in the current context).
  • priority: (Optional) A number. If multiple plugins define shortcuts for the exact same keys, the shortcut with the higher priority number will take precedence. This is useful for resolving conflicts.
  • (Other options): You can also include other options compatible with the underlying useHotkeys hook from the @udecode/react-hotkeys library, such as enabled, enableOnContentEditable, etc., to fine-tune behavior.

Default Shortcuts in Plate Plugins

Many official Plate plugins come with pre-configured shortcuts for their common actions. These defaults typically link to the plugin's internal transform methods. Currently, the following basic mark plugins include default shortcuts:

  • BoldPlugin: Mod+B
  • ItalicPlugin: Mod+I
  • UnderlinePlugin: Mod+U

Other plugins, like CodePlugin, StrikethroughPlugin, etc., provide transforms that can be easily linked to shortcuts (e.g., a toggle shortcut will link to editor.tf.<pluginKey>.toggle()), but you need to define the shortcut keys for them explicitly.

The specific default key combinations for Bold, Italic, and Underline are defined within each plugin's default configuration. You can always override these defaults or define shortcuts for other plugins if they don't fit your needs (see "Overriding and Disabling Shortcuts" below).

Managing Multiple Shortcuts

A single plugin isn't limited to one shortcut; you can define as many as needed:

plugins/my-formatting-tools.ts
import { createPlatePlugin, Key } from 'platejs/react';
 
export const MyFormattingTools = createPlatePlugin({
  key: 'myFormatting',
  // Assuming transforms like editor.tf.myFormatting.applyHeader
  // and editor.tf.myFormatting.applyCodeStyle exist.
})
.extend({
  shortcuts: {
    applyHeader: {
      keys: [[Key.Mod, Key.Alt, '1']],
    },
    applyCodeStyle: {
      keys: [[Key.Mod, Key.Alt, 'c']],
    },
    // A shortcut with a custom handler
    logSomething: {
      keys: [[Key.Mod, 'l']],
      handler: () => console.info('Logging from MyFormattingTools!'),
    },
  },
});

Shortcut Priority

If multiple shortcuts (potentially from different plugins) are configured to use the exact same key combination (e.g., Mod+Shift+P), the priority property on the shortcut configuration object determines which shortcut's action is executed.

A higher number indicates higher priority. If priority is not explicitly set on a shortcut, the priority of its parent plugin is used as a fallback. This allows fine-grained control over which action takes precedence when key combinations overlap.

const PluginA = createPlatePlugin({ key: 'pluginA', priority: 10 }).extend({
  shortcuts: {
    doSomethingImportant: {
      keys: 'mod+shift+p',
      handler: () => console.info('Plugin A: Important action on Mod+Shift+P!'),
      priority: 100, // Explicit, high priority for this specific shortcut
    }
  }
});
 
const PluginB = createPlatePlugin({ key: 'pluginB', priority: 20 }).extend({
  shortcuts: {
    doSomethingLessImportant: {
      keys: 'mod+shift+p', // Same key combination as PluginA's shortcut
      handler: () => console.info('Plugin B: Less important action on Mod+Shift+P.'),
      // No explicit shortcut priority, will use PluginB's priority (20)
    }
  }
});
 
// If both plugins are active, pressing Mod+Shift+P will execute PluginA's handler
// for 'doSomethingImportant' because its shortcut has a higher priority (100 vs 20).

Overriding and Disabling Shortcuts

You can change or disable shortcuts for a specific plugin when you configure it.

To change a plugin's shortcut: When you configure a plugin (e.g., BoldPlugin.configure({ ... })), you can define a shortcut by its name (like toggle). If the plugin already has a shortcut with that name (perhaps a default one), your new configuration for toggle will be used for that plugin. You can change its keys, provide a new handler, or adjust other properties.

import { BoldPlugin, Key } from '@platejs/basic-nodes/react';
 
// BoldPlugin has a default shortcut named 'toggle' (typically Mod+B).
// Let's change its key combination to Mod+Shift+B for BoldPlugin.
const MyCustomBoldPlugin = BoldPlugin.configure({
  shortcuts: {
    toggle: { // This re-configures BoldPlugin's 'toggle' shortcut
      keys: [[Key.Mod, Key.Shift, 'b']], // New key combination
      // The original handler (linking to the 'toggle' transform) is often preserved
      // unless a new 'handler' is specified here.
    },
  },
});

To disable a plugin's shortcut: Set the shortcut's configuration to null in that plugin's shortcuts object. This will remove that specific shortcut (e.g., toggle for ItalicPlugin).

import { ItalicPlugin } from '@platejs/basic-nodes/react';
 
// Example: Disable the 'toggle' shortcut for the ItalicPlugin
const MyCustomItalicPlugin = ItalicPlugin.configure({
  shortcuts: {
    toggle: null, // This will remove/disable the ItalicPlugin's 'toggle' shortcut.
  },
});

Global Shortcuts (Editor Level)

In addition to plugin-specific shortcuts, you can define global shortcuts directly on the editor instance when you create it using createPlateEditor. These shortcuts behave similarly to plugin shortcuts.

editor-config.ts
import { createPlateEditor, Key } from 'platejs/react';
 
const editor = createPlateEditor({
  plugins: [/* ...your array of plugins... */],
  shortcuts: {
    // A global shortcut, perhaps for saving the document
    saveDocument: {
      keys: [[Key.Mod, 's']],
      handler: ({ editor, event }) => {
        console.info('Attempting to save document content:', editor.children);
        // Since preventDefault is set to false for this shortcut,
        // the browser's save dialog will appear by default.
        // If you want to conditionally prevent the default browser behavior
        // (for example, only prevent saving if certain conditions are met),
        // you can call event.preventDefault() inside your handler as needed:
        // if (shouldPrevent) event.preventDefault();
      },
      preventDefault: false,
    },
    anotherGlobalAction: {
      keys: [[Key.Ctrl, Key.Alt, 'g']],
      handler: () => alert('Global action triggered!'),
    }
  },
});

Editor-level shortcuts generally have a high default priority but can still be influenced by the priority settings of individual plugin shortcuts if there are conflicts.

Best Practices

  • Link to Transforms: For clarity and to keep your code DRY, link shortcuts to existing transform methods by matching the shortcut name to the transform name.
  • preventDefault: Most editor shortcuts should prevent the browser's default action for the key combination. Plate handles this by defaulting preventDefault to true. You generally don't need to set it explicitly. However, if your shortcut handler conditionally performs an action (e.g., an indent command that only applies if certain conditions are met), and you want other handlers or the browser's default behavior to take over if your action doesn't run, set preventDefault: false for that shortcut.
  • Maintain Consistency: Strive for intuitive and consistent key combinations. Consider standard shortcuts found in popular text editors or those that make logical sense within your application's context.
  • Manage Priorities for Conflicts: If you anticipate or encounter situations where multiple plugins might try to handle the same key combination, use the priority property to explicitly define which shortcut should take precedence.
  • Provide User Feedback: For actions triggered by shortcuts that aren't immediately visible (like a "Save" action), consider providing some form of user feedback, such as a brief toast notification.