WordPress Shortcodes API Overview
The Shortcodes API provides a bbcode-like tag system for WordPress. It allows developers to create custom macros that users can insert into content using bracket notation. The parser is based on the Textpattern tag parser.
Since: WordPress 2.5.0
Source: wp-includes/shortcodes.php
Shortcode Syntax
WordPress supports three shortcode formats:
Self-Closing Shortcodes
[shortcode]
[shortcode /]
Both forms are equivalent. The trailing slash is optional.
Shortcodes with Attributes
[shortcode foo="bar" baz="bing"]
[shortcode foo="bar" baz="bing" /]
Attributes support three quote styles:
- Double quotes:
foo="bar" - Single quotes:
foo='bar' - No quotes:
foo=bar(value cannot contain spaces)
Attribute names are case-insensitive and converted to lowercase.
Enclosing Shortcodes
[shortcode]content here[/shortcode]
[shortcode foo="bar"]content here[/shortcode]
The content between opening and closing tags is passed to the callback as $content.
Attribute Parsing
The attribute parser handles several formats:
| Format | Example | Result |
|---|---|---|
| Double-quoted | name="value" |
['name' => 'value'] |
| Single-quoted | name='value' |
['name' => 'value'] |
| Unquoted | name=value |
['name' => 'value'] |
| Positional (quoted) | "value" |
[0 => 'value'] |
| Positional (unquoted) | value |
[0 => 'value'] |
Important behaviors:
- Attribute names are lowercased automatically
- Non-breaking spaces and zero-width spaces are normalized to regular spaces
- Unclosed HTML elements in attribute values are rejected (value becomes empty string)
- C-style escape sequences are processed via
stripcslashes()
Shortcode Nesting
WordPress shortcodes have limited nesting support. The core parser does not automatically process nested shortcodes—the outer shortcode’s callback must explicitly call do_shortcode() on its content.
How to Support Nesting
function my_outer_shortcode( $atts, $content = null ) {
// Process any nested shortcodes in $content
$content = do_shortcode( $content );
return '<div class="outer">' . $content . '</div>';
}
add_shortcode( 'outer', 'my_outer_shortcode' );
Nesting Limitations
-
Same-tag nesting is NOT supported:
[/gallery] ❌ Will not work correctly -
Different-tag nesting works (when callbacks process content):
[outer][inner][/inner][/outer] ✓ Works if outer calls do_shortcode() -
Detection functions recurse:
has_shortcode()andget_shortcode_tags_in_content()do check nested content (in$m[5]).
Escaping Shortcodes
To display a shortcode literally without processing, wrap it in double brackets:
[[shortcode]] → [shortcode]
[[shortcode /]] → [shortcode /]
[[shortcode]]...[/shortcode]] → [shortcode]...[/shortcode]
The parser strips the outer brackets and returns the inner content as-is.
Shortcode Names
Valid Names
Shortcode names can contain:
- Letters (a-z, A-Z)
- Numbers (0-9)
- Hyphens (-)
- Underscores (_)
Invalid Characters
The following characters are forbidden in shortcode names:
<>(angle brackets)&(ampersand)/(forward slash)[](square brackets)=(equals sign)- Whitespace (spaces, tabs, newlines)
- Control characters (0x00-0x20)
Attempting to register a shortcode with invalid characters triggers _doing_it_wrong().
Processing Pipeline
When do_shortcode() is called:
- Quick exit check: If no
[in content, return immediately - Tag discovery: Find all potential shortcode names in content
- Intersection: Only process registered shortcodes found in content
- HTML context filter: Add filter for
wp_get_attachment_image_context - HTML tag processing: Handle shortcodes inside HTML attributes
- Main replacement:
preg_replace_callback()with shortcode regex - Escape restoration: Convert
[and]back to brackets - Cleanup: Remove context filter
Regex Match Groups
The shortcode regex (get_shortcode_regex()) captures 6 groups:
| Group | Description | Example Match |
|---|---|---|
$m[0] |
Entire matched shortcode | |
$m[1] |
Extra [ for escaping |
[ (from [gallery]) |
$m[2] |
Shortcode name | gallery |
$m[3] |
Attribute string | ids="1,2,3" |
$m[4] |
Self-closing / |
/ (from ) |
$m[5] |
Enclosed content | ... (from [tag]...[/tag]) |
$m[6] |
Extra ] for escaping |
] (from [gallery]) |
Shortcodes in HTML
The function do_shortcodes_in_html_tags() handles shortcodes inside HTML elements:
With $ignore_html = false (default)
- Shortcodes in unquoted attributes are processed
- Shortcodes in quoted attributes are processed with KSES sanitization
- Comments (
<!-- -->) and CDATA sections are protected
With $ignore_html = true
- All square brackets inside HTML tags are encoded
- Prevents shortcode processing within HTML elements
Bracket Encoding
During processing, brackets are temporarily encoded:
[→[]→]
After processing, unescape_invalid_shortcodes() restores them to preserve things like <!--[if IE]>.
Global Variable
global $shortcode_tags;
The $shortcode_tags array stores all registered shortcodes:
$shortcode_tags = [
'gallery' => 'gallery_shortcode',
'caption' => 'img_caption_shortcode',
'embed' => '__return_false',
// ... plugin shortcodes
];
Keys: Shortcode tag names
Values: Callable functions/methods
Best Practices
-
Prefix shortcode names to avoid conflicts:
[myplugin_gallery]not -
Always escape output in shortcode callbacks:
return '<div>' . esc_html( $content ) . '</div>'; -
Use
shortcode_atts()to merge defaults with user attributes -
Call
do_shortcode()on content if you want to support nesting -
Don’t process shortcodes too early—wait until content is ready
-
Check before adding: Use
shortcode_exists()to avoid overwriting -
Clean up on deactivation: Call
remove_shortcode()when disabling functionality