Shortcodes

This article is part of the WordPress guide. Read the introduction.

As already noted, shortcodes are a way of including complex and/or dynamic content in a post using the Classic Editor. They are a very old feature, added in WordPress 2.5. The basic idea is very simple – you write a PHP function, register it with a name, and then someone uses that name in square brackets inside the editor. Let’s see what it really looks like.

A Hello World Shortcode

PHP
function hello_world_shortcode( $atts ) {
	return '<p>Hello, World!</p>';
}
add_shortcode( 'hello_world', 'hello_world_shortcode' );

That’s it. Now you can write [hello_world] in your editor and it’ll be replaced with “Hello, World!”. Notice that you should return the content (HTML) as a string, not echo it. This is the simplest possible version of a shortcode. Let’s look at one that utilizes attributes.

Shortcode With Attributes

PHP
function hello_world_shortcode( $atts ) {
	$a = shortcode_atts( array(
		'world' => 'World',
	), $atts );

	return '<p>Hello, ' . $a['world'] . '!</p>';
}
add_shortcode( 'hello_world', 'hello_world_shortcode' );

This is a little more interesting, isn’t it? First of all, you’d use it like: [hello_world world=”World”] to render “Hello, World!”. But we’re using an attribute, which means we can do [hello_world world=”WordPress”] to render “Hello, WordPress!”. You can see, attributes provide a way for the user to pass data to the callback function (which can introduce XSS vulnerabilities if the data is rendered like here – sanitize your inputs!).

But what is this mysterious shortcode_atts() function? To understand its significance, we have to understand the behavior of the $atts array.

First of all, any attribute can be passed to the shortcode by the user, and it’ll be present in $atts. This means if the user did [hello_world abcd=”rogue attribute”], this would make the $atts array contain an ‘abcd’ key-value pair. This is weird, as we don’t use this attribute in our callback and we’ve never said we want it.

You can see the second major point in the previous example as well. Notice that there’s no ‘world’ attribute. The user omitted a crucial attribute we’re using in the code. If you were to use the $atts array directly, you’d get a PHP error for trying to access an undefined key ($atts[‘world’]).

The shortcode_atts() function solves both of these problems. The first argument is a new associative array. This array defines the expected attributes along with their default values. If an attribute specified in this array is not present in $atts, it’ll be added. The second argument is the $atts array. In addition to adding attributes with their default values, this function filters out unanticipated attributes as well. Any attribute present in $atts but absent from the first array will not be included in the returned array (the original $atts array is not modified).

Attributes are pretty powerful, but there’s another way. It’s using enclosed content.

Enclosing Shortcode

PHP
function hello_world_shortcode( $atts, $content = null ) {
	return "<p>Hello, $content!</p>";
}
add_shortcode( 'hello_world', 'hello_world_shortcode' );

You would then use this shortcode in the editor like: [hello_world]World[/hello_world].

There’s also a third, rarely used argument passed to the callback function. It’s $tag. It contains the name of the shortcode used to call this function. In our case, it’d be ‘hello_world’. This might be useful if you’re using the same callback function for multiple different shortcodes.

do_shortcode()

The do_shortcode( $content ) function is responsible for parsing the $content and replacing any registered shortcodes with their returned HTML. Keep in mind, a shortcode needs to be registered with add_shortcode() in order to be rendered. This can often be very problematic, where a plugin providing the shortcode is disabled, and the text (e.g., “[hello_world]”) is displayed instead of the shortcode’s output.

This function is hooked to the the_content filter with priority 11 by default. This filter passes the post’s content. Priority 11 ensures it’s run after all filters with default priority (10) have already run, as they may modify/add shortcodes to the contents of the post. After this function runs, all the registered shortcodes that are properly used in the post’s contents are rendered.

While shortcodes are powerful, the user can’t see what they’ll look like until they preview the rendered page. This makes them less than ideal, and it’s exactly what blocks and the Block Editor were created to solve.