An Introduction to WordPress Shortcodes for Developers

By , Updated: March 28, 2017

wordpress shortcodes

According to the WordPress.com definition, a shortcode is a WordPress-specific code that lets you do nifty things with very little effort. Shortcodes can embed files or create objects that would normally require lots of complicated, ugly code in just one line. In other words, thanks to WordPress shortcodes there’s no need for users to manually add HTML code into post content and, in addition, it’s possible to dynamically change the output under specific conditions (i.e. the logged-in user, the current date, the user location, etc.).

If you’ve ever used the [gallery] shortcode, you already know how WordPress shortcodes work:

[gallery ids="129,130,131,132" order="DESC" orderby="title" columns="4"]

In this example, gallery is the shortcode tag, while ids, order, orderby and colums are the shortcode attributes. These attributes determine the returned HTML code.

Shortcode Typologies and Structure

WordPress provides two shortcode typologies:

  • Self-closing shortcodes look like the [gallery] shortcode, and do not require a closing tag.
  • Enclosing shortcodes require a closing tag, and allow the manipulation of the enclosed content.

Here is an example of enclosing shortcode:

[gist]2314628[/gist]

This shortcode is provided by Jetpack plugin in order to embed into posts the code of a public Gist specified by its ID. The text string between the opening and closing tag is the shortcode content.

What You’ll Learn in This Post

Out of the box, WordPress provides six shortcodes, and plugins can add a lot more thanks to the Shortcode API.

The Shortcode API is a simple set of functions for creating WordPress shortcodes for use in posts and pages.

In this post I will introduce you to this useful WordPress feature from a developer’s perspective. From now on, we’ll dive into the Shortcode API following these steps:

The code shown in the following examples has been assembled in a plugin available for download on Github.

The image shows the structure of the kinsta-shortcodes plugin
The image shows the structure of the kinsta-shortcodes plugin

WordPress Shortcodes for Developers

When WordPress processes the post content, it looks for registered shortcodes. Once a shortcode is found, the Shortcode API parses tag, attributes and enclosed content, if available, and pass them to a corresponding handler function. This function performs a task and returns a string to be included into the post content. So, our first step is to register shortcode tag and handler thanks to the add_shortcode function:

add_shortcode( 'shortcode_name', 'shortcode_handler' );
  • The shortcode name is a tag WordPress will search for in post content. It should be lowercase, and requires only letters, numbers and underscores
  • the shortcode handler is a callback function to be executed when WordPress finds the shortcode

The handler function is defined as follows:

function shortcode_handler( $atts, $content, $tag ){}

The function keeps three arguments:

  • $atts (array): an array of attributes or an empty string;
  • $content (string): the enclosed content (available for enclosing shortcodes only);
  • $tag (string): the name of the shortcode, useful for shared callback functions.

Shortcodes can be added to a theme’s functions.php file or to a plugin. In themes we can run the add_shortcode function as is, but in plugins we should wait until WordPress has been initialized:

function shortcodes_init(){
	add_shortcode( 'shortcode_name', 'shortcode_handler' );
}
add_action('init', 'shortcodes_init');

My Frst Self-closing Shortcode

Our first shortcode includes the following a element into the post content:

<a href="" id="" class="" target="">Button</a>

Attributes and text will be set by users as follows:

[kinsta_btn href="" id="" class="" target="" label=""]

Now that we have set our goal, let’s open the main file of a plugin and add the following code:

function kinsta_shortcodes_init(){
	add_shortcode( 'kinsta_btn', 'kinsta_button' );
}
add_action('init', 'kinsta_shortcodes_init');

Then define the shortcode handler function:

function kinsta_button( $atts ){

	// normalize attribute keys, lowercase
	$atts = array_change_key_case( (array)$atts, CASE_LOWER );
	
	extract( shortcode_atts(
			array(
				'href'		=> '',
				'id'		=> '',
				'class'		=> 'green',
				'target'	=> '',
				'label'		=> 'Button'
			),
			$atts,
			'kinsta_btn'
		) );

	if( in_array( $target, array( '_blank', '_self', '_parent', '_top' ) ) ){
		$link_target = ' target="' . esc_attr( $target ) . '"';
	}else{
		$link_target = '';
	}

	$output = '<p><a href="' . esc_url( $href ) . '" id="' . esc_attr( $id ) . '" class="button ' . esc_attr( $class ) . '"' . $link_target . '>' . esc_attr( $label ) . '</a></p>';
	return $output;
}

Here is what happens in this function:

  • array_change_key_case is a PHP function which returns the keys of an array in uppercase or lowercase.
  • shortcode_atts is a WordPress function that combines the user shortcode attributes with existing attributes and sets default values when needed.
  • The resulting array of attributes is passed to the PHP extract function, which imports variables from an array into the current symbol table.
  • The following condition checks the value of the target attribute, and sets an empty string if no valid value is found.
  • The $output variable stores the HTML code of the anchor element, which is finally returned by the function.

In order to convert the a element into an appealing CSS3 button, we have to register and enqueue a stylesheet to the list of available resources:

function kinsta_enqueue_scripts() {
	global $post;
	if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'kinsta_btn') ) {
		wp_register_style( 'kinsta-stylesheet',  plugin_dir_url( __FILE__ ) . 'css/style.css' );
    		wp_enqueue_style( 'kinsta-stylesheet' );
	}
}
add_action( 'wp_enqueue_scripts', 'kinsta_enqueue_scripts');

The function defines the $post global variable, then checks two conditions:

  • whether $post is an instance of the WP_Post object
  • whether the post content contains the kinsta_btn shortcode

If both conditions are true, the function registers and enqueues the stylesheet. I won’t show you the CSS code here, but you can find it on Github. Finally we can add the [kinsta_btn] shortcode into the post content as follows:

[kinsta_btn href="http://www.google.com" class="blue rounded" target="_blank"]
CSS3 button
With the proper CSS styling the anchor will be presented as an amazing CSS3 button

Building an Enclosing Shortcode

If we wanted the same HTML mark-up from an enclosing shortcode, we would make only small changes to the preceding handler function. First, we’ll register the shortcode [kinsta_btn_adv]:

function kinsta_shortcodes_init(){
	add_shortcode( 'kinsta_btn_adv', 'kinsta_button_adv' );
}
add_action('init', 'kinsta_shortcodes_init');

Then, we’ll define the new handler:

function kinsta_button_adv( $atts, $content = null, $tag = '' ){

	// normalize attribute keys, lowercase
	$atts = array_change_key_case( (array)$atts, CASE_LOWER );

	extract( shortcode_atts(
			array(
				'href'		=> '',
				'id'		=> '',
				'class'		=> 'green',
				'target'	=> ''
			),
			$atts,
			'kinsta_btn'
		) );

	if( in_array( $target, array( '_blank', '_self', '_parent', '_top' ) ) ){
		$link_target = ' target="' . esc_attr( $target ) . '"';
	}else{
		$link_target = '';
	}

	$output = '<p><a href="' . esc_url( $href ) . '" id="' . esc_attr( $id ) . '" class="button ' . esc_attr( $class ) . '"' . $link_target . '>' . esc_attr( $content ) . '</a></p>';
	return $output;
}

We don’t need the label attribute for the button, as users will pass the string through the shortcode content. Having two shortcodes we have to load the stylesheet under two conditions. So, let’s change the kinsta_enqueue_scripts function as follows:

function kinsta_enqueue_scripts() {
	global $post;

	// see https://codex.wordpress.org/Function_Reference/has_shortcode
	$has_shortcode = has_shortcode( $post->post_content, 'kinsta_btn') || has_shortcode( $post->post_content, 'kinsta_btn_adv');
	
	if( is_a( $post, 'WP_Post' ) && $has_shortcode ) {
		// see https://codex.wordpress.org/Function_Reference/plugin_dir_url
		wp_register_style( 'kinsta-stylesheet',  plugin_dir_url( __FILE__ ) . 'css/style.css' );
		// see https://developer.wordpress.org/reference/functions/wp_enqueue_style/
		wp_enqueue_style( 'kinsta-stylesheet' );
	}
}
add_action( 'wp_enqueue_scripts', 'kinsta_enqueue_scripts');

Now we can use the following enclosing shortcode:

[kinsta_btn_adv href="http://kinsta.com/blog/" class="red rounded" target="_blank"]It's Amazing! View more[/kinsta_btn_adv]

CSS3 button

Adding the Shortcode From the Visual Editor

Typing WordPress shortcodes can be quite annoying and unfriendly. Luckily, we can make this task quick and fun thanks to the TinyMCE API.

In this last example, we will build an interface that will allow users to insert complex shortcodes into the post content with ease. To accomplish this final task, we need a TinyMCE plugin and a MCE button. Let’s get back to our WordPress plugin, and add the following code:

// register TinyMCE buttons
function kinsta_register_mce_buttons( $buttons ) {
	$buttons[] = 'kinsta';
	return $buttons;
}
// add new buttons
add_filter( 'mce_buttons', 'kinsta_register_mce_buttons' );

We’ve just added a new button named kinsta. Now, let’s register a TinyMCE plugin:

function kinsta_register_mce_plugin( $plugin_array ) {
   $plugin_array['kinsta'] = plugins_url( '/mce/kinsta/plugin.js', __FILE__ );
   return $plugin_array;
}
// Load the TinyMCE plugin
add_filter( 'mce_external_plugins', 'kinsta_register_mce_plugin' );

This code registers the kinsta TinyMCE plugin, which is located in /mce/kinsta/ folder. Open the plugin.js file and add the following JavaScript (you can view the file on Github):

(function() {
	tinymce.PluginManager.add( 'kinsta', function( editor ){});
})();

The PluginManager.add method registers the kinsta plugin. This method allows us to add a custom button to the editor UI:

editor.addButton( 'kinsta', {
	title: 'Kinsta buttons',
	text: 'Kinsta',
	icon: 'code',
	onclick: function(){}
});

The onclick property defines a new anonymous function, which sets the properties of a dialog box:

onclick: function(){
	editor.windowManager.open({
		title: 'Kinsta buttons',
		body: 
		[
			{type: 'textbox', name: 'btn_url', label: 'URL'},
			{type: 'textbox',name: 'btn_label', label: 'Button label'},
			{type: 'textbox', name: 'btn_id', label: 'Button ID'},
			{type: 'listbox', name: 'btn_color', label: 'Button color', values: [
				{text: 'Green', value: 'green'}, 
				{text: 'Blue', value: 'blue'},
				{text: 'Red', value: 'red'},
				{text: 'Grey', value: 'grey'},
				{text: 'Black', value: 'black'}
			]},
			{type: 'checkbox', name: 'btn_corners', label: 'Button corners'}
		],
		onsubmit: function(e){
			var tag = 'kinsta_btn';
			var href = ' href= "' + e.data.btn_url + '"';
			var label = ' label="' + e.data.btn_label + '"';
			var id = ' id="' + e.data.btn_id + '"';
			var corners = e.data.btn_corners == true ? ' rounded' : '';
			var color = e.data.btn_color;
			var css_class = ' class="' + color + corners + '"';
			editor.insertContent('[' + tag + href + id + css_class + label + ']')
		}
	})
}

The dialog box contains three textboxes, a listbox and a checkbox. When the form is submitted, the insertContent method includes the shortcode into the post content.

TinyMCE dialog box
A dialog box allows users to insert complex shortcodes into post content with ease

Actually, this is just a simple example. We could give users more control over the WordPress shortcodes, thanks to a good number of input fields and controls which are listed in TinyMCE documentation.

Nonconventional uses of shortcodes

Out of the box, WordPress admits shortcodes only in post content. Anyway, as plugin developers, we can overcome this limitation by filtering any text content anywhere into the site. We can filter text widgets:

add_filter( 'widget_text', 'shortcode_unautop');
add_filter( 'widget_text', 'do_shortcode');

The first line applies the shortcode_unautop function to the content of text widgets in order to prevent WordPress from automatically wrapping shortcodes within paragraphs. The second line applies the do_shortcode function to text widgets.

We can filter post excerpts:

add_filter( 'the_excerpt', 'do_shortcode');

And we can filter term descriptions:

add_filter( 'term_description', 'do_shortcode' );

In the same way we can dynamically generate the titles of navigation menu items. Suppose you want a menu item showing the username of the logged-in user. First, you need a shortcode that displays the username:

function kinsta_shortcodes_init(){
	add_shortcode( 'kinsta_usr', 'kinsta_username' );
}
add_action('init', 'kinsta_shortcodes_init');

function kinsta_username( $atts = array() ){

	$id = get_current_user_id();
	
	if ( 0 == $id ) {
		// Not logged in
		return __( 'Guest' );
	} else {
		// Logged in
		$user = get_userdata( $id );
		return $user->user_login;
	}
}

Now you can type in the post content ‘[kinsta_usr]’ to get the current user login. In order to use this shortcode in menu items, we have to filter the list of menu items through the wp_nav_menu_objects filter:

function kinsta_dynamic_menu_items( $menu_items ) {

	global $shortcode_tags;

	foreach ( $menu_items as $menu_item ) {

		if ( has_shortcode( $menu_item->title, 'kinsta_usr' ) && isset( $shortcode_tags['kinsta_usr'] ) ){

			$menu_item->title = do_shortcode( $menu_item->title, '[kinsta_usr]' );

			if ( 0 == get_current_user_id() ){

				$menu_item->url = wp_login_url();
			
			}
		}
	}
	return $menu_items;
}
add_filter( 'wp_nav_menu_objects', 'kinsta_dynamic_menu_items' );

First, we define the $shortcode_tags global variable. Following, we checks two conditions:

  • whether any menu item contains the [kinsta_usr] shortcode
  • whether the [kinsta_usr] shortcode exists

If both conditions are true, do_shortcode searches any menu item title for ‘[kinsta_usr]’ and runs the shortcode handler. For not logged-in users we change the menu item URL to wp_login_url(). Now jump to Appearance Menus Screen and add a new menu item to the navigation menu as shown in the image below.

Menu item

Save the menu and view the site.

Dynamic menu

Wrapping up

Now we know how to build WordPress shortcodes, how to allow users to add them anywhere into the site, and how to build TinyMCE dialogs to configure complex shortcodes in few moments. Have you ever created custom shortcodes? Share your ideas with us in the comments below.