In one of the WordPress plugins I developed recently I had to add a list a buttons (which is a custom post type) in the TinyMCE pop up. It was basically a select field where I needed to populate button ID and title as option fields.

Since I see a lot of people asking how to add dynamic content inside TinyMCE pop up so thought of sharing how I dealt with it.

Dynamic Content in TinyMCE

In this tutorial I will show you how to dynamically add post data as options inside TinyMCE pop up select field. So without any ado let’s jump right into it.

Step 1. Create TinyMCE Button

First of all create a TinyMCE button. Add the below code to your plugin file.

// Hooks your functions into the correct filters
function twd_mce_button() {
	// check user permissions
	if ( !current_user_can( 'edit_posts' ) && !current_user_can( 'edit_pages' ) ) {
	// check if WYSIWYG is enabled
	if ( 'true' == get_user_option( 'rich_editing' ) ) {
		add_filter( 'mce_external_plugins', 'twd_add_mce_plugin' );
		add_filter( 'mce_buttons', 'twd_register_mce_button' );
add_action( 'admin_head', 'twd_mce_button' );

// Script for our mce button
function twd_add_mce_plugin( $plugin_array ) {
	$plugin_array['twd_mce_button'] = plugin_dir_url( __FILE__ ) . 'mce.js';
	return $plugin_array;

// Register our button in the editor
function twd_register_mce_button( $buttons ) {
	array_push( $buttons, 'twd_mce_button' );
	return $buttons;

Here we are using basic WordPress TinyMCE filters to hook our MCE plugin into TinyMCE. You can read more about it on TinyMCE Custom Buttons page on

Step 2. Fetch Post Titles

Now I will make use of wpdb class to fetch the post titles and then return the data that I require (post id and title) as a JSON string in the format that TinyMCE select field expects.

 * Function to fetch cpt posts list
 * @return json
public function twd_posts( $post_type ) {

	global $wpdb;
   	$cpt_type = $post_type;
	$cpt_post_status = 'publish';
        $cpt = $wpdb->get_results( $wpdb->prepare(
        "SELECT ID, post_title
            FROM $wpdb->posts 
            WHERE $wpdb->posts.post_type = %s
            AND $wpdb->posts.post_status = %s
            ORDER BY ID DESC",
    ) );

    $list = array();

    foreach ( $cpt as $post ) {
		$selected = '';
		$post_id = $post->ID;
		$post_name = $post->post_title;
		$list[] = array(
			'text' =>	$post_name,
			'value'	=>	$post_id

	wp_send_json( $list );


Step 3. Store JSON Data with TinyMCE Editor Instance

You might ask why set data inside TinyMCE editor instance? This is because this way we will be able to easily access our data inside the TinyMCE pop up. Here we just need to create a new property under TinyMCE Editor instance to store our data. I will use AJAX here to store the data from previous step.

 * Function to fetch buttons
 * @since  1.6
 * @return string
public function twd_list_ajax() {
	// check for nonce
	check_ajax_referer( 'twd-nonce', 'security' );
	$posts = twd_posts( 'post' );
	return $posts;
add_action( 'wp_ajax_twd_cpt_list', 'twd_list_ajax' );

 * Function to output button list ajax script
 * @since  1.6
 * @return string
public function twd_cpt_list() {
	// create nonce
	global $pagenow;
	if( $pagenow != 'admin.php' ){
		$nonce = wp_create_nonce( 'twd-nonce' );
		?><script type="text/javascript">
			jQuery( document ).ready( function( $ ) {
				var data = {
					'action'	: 'twd_cpt_list', // wp ajax action
					'security'	: '<?php echo $nonce; ?>' // nonce value created earlier
				// fire ajax ajaxurl, data, function( response ) {
			  		// if nonce fails then not authorized else settings saved
			  		if( response === '-1' ){
				  		// do nothing
			  		} else {
			  			if (typeof(tinyMCE) != 'undefined') {
			  				if (tinyMCE.activeEditor != null) {
								tinyMCE.activeEditor.settings.cptPostsList = response;
add_action( 'admin_footer', 'twd_cpt_list' );


Step 4. Create TinyMCE Plugin

This is the last step where we need to create a TinyMCE plugin. The code below is basic JavaScript code to create a plugin. The only change here is that we are using TinyMCE editor instance property that we created earlier to add values to our select field. Submitting the form prints out a short code with id parameter containing the dynamic content.

(function() {
	tinymce.PluginManager.add('twd_mce_button', function( editor, url ) {
		editor.addButton('twd_mce_button', {
			text: '{ TWD CPT Posts }',
			icon: false,
			tooltip: 'CPT List',
			onclick: function() { {
					title: 'Insert List',
					width: 400,
					height: 100,
					body: [
							type: 'listbox',
							name: 'listboxName',
							label: 'TWD CPT Posts',
							'values': editor.settings.cptPostsList
					onsubmit: function( e ) {
						editor.insertContent( '[twd_post_title id="' + + '"]');

I hope through this tutorial you will be able to add dynamic content inside TinyMCE pop up in WordPress. If something is unclear or if you have any suggestion to improve this code then please use the comment form below.

About the author

Blogger, Developer and Web Video Producer.
17 Responses
  1. Will Hershfeld

    Hi Gautam – This tutorial is exactly what I’m looking for – I’ve just got to access a different post type.

    Could you elaborate a little on the where each of these code segments should be placed?

      1. Kassandra

        Hi Guatam. This is great! Thank you. However, I was wondering, how can I get this functionality to work in the TinyMCE for regular posts and not just books.

  2. Paul

    Thank you very much for posting this. It’s very helpful and I’ll be utilising something like this in a personal project I’m in the middle of.

  3. Thanks for the work, i would be very interested in learning doing this, but unfortunately, i’ve just installed your plugin, make the change for fetching posts, and it does not work… Can you please help ?

    1. Gautam Thapar

      Hi Max,

      May I know what changes you made? You just need to change “book” to “post” on line 136 in twd-mce-button.php to get list of posts.

  4. derin

    Hello, Thanks for all the work. Unfortunately, the ajax request method seems to have been nixed somewhere in the last year and so the code doesn’t work anymore. I left an issue at your github

  5. Hannah

    This is a great example!
    Does anyone know of a way to do this type of dropdown with a type ahead feature? So when you start typing, it will scroll down the list box for matches.

  6. Serg

    Hi Gautam!

    I used your plugin, but it does not work, as far as I understand in line 167 (tinyMCE.activeEditor.settings.cptPostsList = response), does not add an array to the settings.

    If you output the value in the console “console.log(editor.settings.cptPostsList)” is not definite.

    any help me?

    1. Gautam Thapar

      Hi, this happens you are in text mode. Anyways, I have updated the code so it should work fine now in both text and visual mode. You can check the new code over here.

  7. Svetlin

    Hi, great work. Very helpful tutorial and it works fine. One issue for me ( I hope its my code), I have multiple editors on the entry edit page and the ajax populates only the first editor. I tried to set all editors like this
    for (edId in tinyMCE.editors){
    tinyMCE.editors[edId].settings.cptPostsList = response;
    with the response, but still only first one has the dropdown populated.
    Can you help me with this issue, please?

Leave a Reply

May be Subscribe?