Creating custom Basic toolbar buttons

A basic button triggers its onAction function when clicked.


Name Value Requirement Description




Text to display if no icon is found.




Name of the icon to be displayed. Must correspond to an icon: in the icon pack, in a custom icon pack, or added using the addIcon API.




Text for button tooltip.




default: true - Represents the button’s state. When false, the button is unclickable. Toggled by the button’s API.


(api) => (api) => void


default: () => () => {} - Function invoked when the button is rendered. For details, see: Using onSetup.


(api) => void


Function invoked when the button is clicked.

NOTE: This feature is only available for TinyMCE 7.0 and later.




Shortcut to display in the tooltip. To register a shortcut, see: Add custom shortcuts to TinyMCE.


Name Value Description


() => boolean

Checks if the button is enabled.


(state: boolean) => void

Sets the button’s enabled state.

NOTE: This feature is only available for TinyMCE 6.4 and later.


(text: string) => void

Sets the text label to display.


(icon: string) => void

Sets the icon of the button.

Basic button example and explanation

The following example adds two buttons to the toolbar:

  • TinyMCE

  • HTML

  • JS

  • Edit on CodePen

<textarea id="custom-toolbar-button">
  <p><img style="display: block; margin-left: auto; margin-right: auto;" title="Tiny Logo" src="" alt="TinyMCE Logo" width="128" height="128"></p>
  <h2 style="text-align: center;">Welcome to the TinyMCE editor demo!</h2>
  <p>Select a menu item from the listbox above and it will insert contents into the editor at the caret position.</p>

  <h2>Got questions or need help?</h2>
    <li>Our <a href="">documentation</a> is a great resource for learning how to configure TinyMCE.</li>
    <li>Have a specific question? Try the <a href="" target="_blank" rel="noopener"><code>tinymce</code> tag at Stack Overflow</a>.</li>
    <li>We also offer enterprise grade support as part of <a href="">TinyMCE premium plans</a>.</li>

  <h2>Found a bug?</h2>
  <p>If you think you have found a bug please create an issue on the <a href="">GitHub repo</a> to report it to the developers.</p>

  <h2>Finally ...</h2>
  <p>Don't forget to check out our other product <a href="" target="_blank">Plupload</a>, your ultimate upload solution featuring HTML5 upload support.</p>
  <p>Thanks for supporting TinyMCE! We hope it helps you and your users create great content.
    <br>All the best from the TinyMCE team.</p>
  selector: 'textarea#custom-toolbar-button',
  toolbar: 'customInsertButton customDateButton',
  setup: (editor) => {

    editor.ui.registry.addButton('customInsertButton', {
      text: 'My Button',
      onAction: (_) => editor.insertContent(`&nbsp;<strong>It's my button!</strong>&nbsp;`)

    const toTimeHtml = (date) => `<time datetime="${date.toString()}">${date.toDateString()}</time>`;

    editor.ui.registry.addButton('customDateButton', {
      icon: 'insert-time',
      tooltip: 'Insert Current Date',
      enabled: false,
      onAction: (_) => editor.insertContent(toTimeHtml(new Date())),
      onSetup: (buttonApi) => {
        const editorEventCallback = (eventApi) => {
          buttonApi.setEnabled(eventApi.element.nodeName.toLowerCase() !== 'time');
        editor.on('NodeChange', editorEventCallback);

        /* onSetup should always return the unbind handlers */
        return () =>'NodeChange', editorEventCallback);
  content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'

The first button inserts "It’s my button!" into the editor when clicked. The second button is an example of how onSetup works. This button inserts a time element containing the current date into the editor using a toTimeHtml() helper function - a simplified version of TinyMCE’s insertdatetime plugin.

In this example an icon from the insertdatetime plugin is used to demonstrate how to use a registered icon. disabled is set to true so that the button is disabled when it is first rendered.

onSetup is used to listen to the editor’s NodeChange event to disable the button when the cursor is inside a time element (or "node"). This ensures it is not possible to insert a time element into another time element.

Using onSetup

onSetup is a complex property. It takes a function that is passed the component’s API and should return a callback that is passed the component’s API and returns nothing. This occurs because onSetup runs whenever the component is rendered, and the returned callback is executed when the component is destroyed. This is essentially an onTeardown handler, and can be used to unbind events and callbacks.

To clarify, in code onSetup may look like this:

onSetup: (api) => {
  // Do something here on component render, like set component properties or bind an event listener

  return (api) => {
    // Do something here on teardown, like unbind an event listener

To bind a callback function to an editor event use editor.on(eventName, callback). To unbind an event listener use, callback). Any event listeners should be unbound in the teardown callback. The only editor event which does not need to be unbound is init e.g. editor.on('init', callback).

  • The callback function for should be the same function passed to editor.on(). For example, if a editorEventCallback function is bound to the NodeChange event when the button is created, onSetup should return (api) =>'NodeChange', editorEventCallback).

  • If onSetup does not have any event listeners or only listens to the init event, onSetup can return an empty function e.g. return () => {};.