Creating custom buttons within the TinyMCE text editor is a small “nice-to-have” feature when structuring the admin of a website. It provides the team and clients with the ability to add custom elements to their content.

In this article, I’ll run through how to add your own, along with some examples. In my example code, I work on three custom buttons:

  1. A simple button with text
  2. A custom button
  3. A custom multi-column list

Please note: WordPress uses v4 of TinyMCE and this guide only works with websites before the Gutenberg update of WordPress 5.0+ or websites using the Classic Editor plugin.

Folder Structure

You will need at least one JavaScript file for implementing custom buttons. In my example, I call it zz-btns.js. The hook to trigger these buttons can be added right into the functions.php file. I will also show an additional step utilizing one CSS file and image(s) to add custom button icons. I include these elements in a folder called includes for clarity and organization.

Files/folders you will be modifying or creating:

functions.php
/includes
zz-btns.js
zz-btns.css
/icons
	icon-zz-mce-list.png
icon-zz-mce-button.png

The PHP

In your functions.php file, check if the user has permissions to edit posts/pages and if WYSIWYG is enabled. Then, register the TinyMCE customization(s). You can register as many as you’d like and will be added to the top or bottom row of the TinyMCE bar, depending on what filter you used in the function, (mce_buttons or mce_buttons_2 respectively). In my example, the function names start with zz_tc, which is short for “Zion & Zion TinyMCE Customization.” You can name the functions anything you wish, of course. The needed functions.php file additions are the following:

<?php

add_action('admin_head', 'zz_add_my_tc_button');

function zz_add_my_tc_button() {
  global $typenow;
  // Check user permissions
  if ( !current_user_can('edit_posts') && !current_user_can('edit_pages') ) {
    return;
  }

  // Check if WYSIWYG is enabled
  if ( get_user_option('rich_editing') == 'true') {
    add_filter('mce_external_plugins', 'zz_add_tinymce_plugin');
    add_filter('mce_buttons', 'zz_register_my_tc_button');
  }

  // Register the CSS files if using custom icons via the CSS property background-image
  wp_register_style( 'zz_tc_button_css', get_template_directory_uri() . '/includes/zz-btns.css', false, '1.0.0' );
  wp_enqueue_style( 'zz_tc_button_css' );
}

// Create the custom TinyMCE plugins
function zz_add_tinymce_plugin($plugin_array) {
  $plugin_array['zz_tc_simple'] = get_template_directory_uri() . '/includes/zz-btns.js';
  $plugin_array['zz_tc_button'] = get_template_directory_uri() . '/includes/zz-btns.js';
  $plugin_array['zz_tc_list'] = get_template_directory_uri() . '/includes/zz-btns.js';
  return $plugin_array;
}
// Add the buttons to the TinyMCE array of buttons that display, so they appear in the WYSIWYG editor 
function zz_register_my_tc_button($buttons) {
  array_push($buttons, 'zz_tc_simple');
  array_push($buttons, 'zz_tc_button');
  array_push($buttons, 'zz_tc_list');
  return $buttons;
}

The JavaScript

The JavaScript is the most important portion of these buttons. It is where the buttons are created, options are saved, and content to be inserted into the WYSIWYG editor is declared.

Create the Button

To connect your button with the editor, the name will need to match to what was declared in your functions.php functions. I will use the zz_tc_simple button as an example for now. Since I am technically creating a TinyMCE plugin, the custom button will be created with the following:

tinymce.PluginManager.add('zz_tc_simple', function( editor, url ) {
  editor.addButton( 'zz_tc_simple', {
  });
});

Adding Button Options

Inside the editor.addButton, a number of button options can be added, including:

  • text – the text that will show up on the button
  • icon – CSS class for the icon (from one of the loaded stylesheets)
  • image – URL of the image (16×16 recommended) to use as an icon (overrides icon option if defined)
  • tooltip – tooltip to pop up as a hover state
  • onclick – callback to call when button is clicked
  • onpostrender – callback to call when button is rendered
  • cmd – editor command to invoke when the button is clicked (command should be registered prior to this, either by editor or by you)

Let’s add text to my example code above so it displays the word “Simple” on the button. Let’s also add an onclick function that will insert some text into the TinyMCE editor at the cursor location.

tinymce.PluginManager.add('zz_tc_simple', function( editor, url ) {
  editor.addButton( 'zz_tc_simple', {
    text: 'Simple',
    onclick: function() {
      editor.insertContent('Just some simple text');
    }
  });
});

Adding a Popup Window

The above example is a simplistic way to insert static content every time. Often, it will be beneficial to add options and choices to select and customize the inserted content. The wonderful thing about TinyMCE is that it provides a built-in option for this as editor.windowManager,which triggers a popup window when clicking the custom TinyMCE button. In this example, let’s create one that provides fields that will generate a custom theme button. This looks like the following:

tinymce.PluginManager.add('zz_tc_button', function( editor, url ) {
  editor.addButton( 'zz_tc_button', {
    text: "ZZ Button",
    title: "Insert Button",
    onclick: function() {
      editor.windowManager.open( {

Popup Window Options

There a few options for the popup window, including a window title, window height, window width, and body content types (more on that below). Other than the body types, these options are not required but it helps the look.

Popup Window Body Types

The field types to add to the popup will depend on the needs of your button. There are several to choose from, including text, dropdowns, checkboxes, colors, etc. I created a CodePen to reference all those options in one place (click the “All Widgets” button). In this example, I will add a few textbox fields, a checkbox field, and a dropdown field. This looks like the following:

Sending Popup Data to the Editor

Once the appropriate options are selected, you need to send this data to the editor window. To do this, add the onsubmit function. In the function, pass through the event which references the selected options and then reference each data piece by its name argument. Inside this function you can use pretty much any JavaScript you would normally use, like strings, variables, loops, conditionals, etc. This looks like the following:

onsubmit: function( e ) {
  let $content = '<a href="' + e.data.url + '" class="btn' + (e.data.style !== 'default' ? ' ' + e.data.style : '') + '"' + (!!e.data.newtab ? ' target="_blank"' : '' ) + '>' + e.data.label + '</a>';
  editor.insertContent( $content );
}

Putting it All Together

If you put together all the above code for the completed code, it looks like the following:

tinymce.PluginManager.add('zz_tc_button', function( editor, url ) {
  editor.addButton( 'zz_tc_button', {
    text: "ZZ Button",
    title: "Insert Button",
    onclick: function() {
      editor.windowManager.open( {
        title: 'Insert Button',
        width: 500,
        height: 300,
        body: [
        {
          type: 'textbox',
          name: 'url',
          label: 'URL'
        },
        {
          type: 'textbox',
          name: 'label',
          label: 'Link Text'
        },
        {
          type: 'checkbox',
          name: 'newtab',
          label: ' ',
          text: 'Open link in new tab',
          checked: true
        },
        {
          type: 'listbox',
          name: 'style',
          label: 'Button Style',
          'values': [
            { text: "Primary", value: "default" },
            { text: "Secondary", value: "secondary" },
            { text: "Disabled", value: "disabled" }
          ]
        }],
        onsubmit: function( e ) {
          let $content = '<a href="' + e.data.url + '" class="btn' + (e.data.style !== 'default' ? ' ' + e.data.style : '') + '"' + (!!e.data.newtab ? ' target="_blank"' : '' ) + '>' + e.data.label + '</a>';
          editor.insertContent( $content );
        }
      });
    }
  });
});

Below are images of the popup window and a submission.

Icons

Now that I’ve covered the JavaScript portion, let’s take a moment to discuss adding custom button icons. These will take your buttons from plain text (or nothing) to your own recognizable brand. If you have been following along with the code provided you should have an includes folder with a zz-btns.css file and a folder called icons within the includes folder. You will use these.

Within the zz-btns.js JavaScript file there is a line for icon where you can add any class you’d like. These are used to target between the various buttons created to apply different icons with the CSS style background-image. For example, the custom button zz_tc_button uses a class of zz-mce-button. In the zz-btns.css file, I’ll target that and reference a custom image for the button icon.

tinymce.PluginManager.add('zz_tc_button', function( editor, url ) {
  editor.addButton( 'zz_tc_button', {
    title: 'Insert Button',
    icon: 'icon zz-mce-button',
    onclick: function() {
      editor.windowManager.open( {

In the CSS file, I’ll add the property background image:

i.zz-mce-button {
  background-image: url('icons/icon-zz-mce-button.png');
}

You can utilize any images you wish. I have a bit of design experience, so I went ahead and created my own.

Bonus: There’s an alternative way to add an icon, where the button option for ”image” is added to button options along with the URL path to the icon. The URL of the image (16×16 recommended)  overrides the icon option if defined. This looks like the following:

tinymce.PluginManager.add('zz_tc_button', function( editor, url ) {
    editor.addButton( 'zz_tc_button', {
      title: 'Insert Button',
      image: '/wp-content/themes/my_custom_theme/includes/icons/icon-zz-mce-button.png',
      // icon: 'icon zz-mce-button', This is overridden by image option above
      onclick: function() {
        editor.windowManager.open( {

Just One Way to Make an Impact

There are many ways to customize a TinyMCE editor to your needs. These custom buttons are just one small way to make your WordPress admin a little easier to use. At the end of the day, it’s important to remember that a lot of small things can add up to make a large impact for the end-user.