āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/udecode/plate/(guides)/plugin-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.
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:
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).keys (the key combination) for the shortcut.Plate will automatically find and call the corresponding method when the specified keys are pressed.
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
},
},
});
<Callout type="info">
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.
</Callout>
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.
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();
},
},
},
});
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.
'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.useHotkeys hook from the @udecode/react-hotkeys library, such as enabled, enableOnContentEditable, etc., to fine-tune behavior.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:
Mod+BMod+IMod+UOther 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.
A single plugin isn't limited to one shortcut; you can define as many as needed:
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!'),
},
},
});
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).
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.
},
});
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.
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.
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.priority property to explicitly define which shortcut should take precedence.ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā