![🛠 How to add your own shortcode in WordPress: add_shortcode() breakdown [+2 examples]](/api/media/file/ai-cover-1785346567950.avif)
🛠 How to add your own shortcode in WordPress: add_shortcode() breakdown [+2 examples]
Created a snippet, added it to functions.php, and WordPress says "cheatin' uh?" Sound familiar? Shortcodes, WordPress's core extension mechanism, and add_shortcode() unlocks it in 15 lines of code. But between "Hello World" and a real-world scenario lie a few pitfalls: return vs echo, callback parameter order, and tag conflicts.
Shortcodes appeared back in WordPress 2.5 and have since become the foundation of the ecosystem: contact forms, galleries, sliders, custom field output, all of it lives inside square brackets. add_shortcode() hasn't changed in years, and you can master it in half an hour.
Below, a complete breakdown of the function with two ready-to-use examples: from an auto-updating year in the footer to a site user list.
💡 Quick overview:
add_shortcode()syntax and parameters, tag and callback- The rule: always
return, neverecho - The
inithook, why your shortcode may not work without it - Two ways to pass data: content between tags and attributes
- Ready-to-use code: a shortcode for auto-year and a user list
How add_shortcode() works in WordPress
The add_shortcode() function is the entry point for registering any custom shortcode. It takes two required parameters: a unique tag and the name of the callback function that will return the content.
According to the WordPress documentation, the callback receives three arguments in a fixed order:
$attributes, an array of attributes passed in the shortcode$content, the text between the opening and closing tags$tag, the tag name the shortcode is registered with
A basic example, a shortcode that outputs "Hello World!":
1 // Shortcode callback function 2 function hello_world_shortcode_callback( $attributes, $content, $tag ) { 3 return 'Hello World!'; 4 } 5 // Shortcode registration 6 function hello_world_shortcode() { 7 add_shortcode( 'hello-world', 'hello_world_shortcode_callback' ); 8 } 9 add_action( 'init', 'hello_world_shortcode' );
The code goes into your child theme's functions.php or via a Code Snippets plugin. After that, [hello-world] in the post editor will output "Hello World!".
Why return, not echo
Note the return inside the callback function. If you use echo, the output happens before WordPress builds the rest of the page, "Hello World!" ends up at the very top, and the surrounding content gets displaced.
The rule is simple: **a shortcode callback always returns a value via ****return**. No echo, print, or direct HTML output outside a buffer.
The init hook is required
WordPress.org developers recommend hooking shortcode registration to init, not calling add_shortcode() directly in the body of functions.php. Without init, WordPress may not have finished initializing its internal mechanisms, and the shortcode simply won't fire.
Syntax and parameters
Function signature:
1 add_shortcode( string $tag, callable $callback )
$tag, the shortcode name (without brackets). If two plugins register a shortcode with the same tag, the last one overrides the previous. To avoid conflicts, add a unique prefix: not [gallery], but [myplugin_gallery].
$callback, the name of the function that returns the content. You can pass a string with the function name, an anonymous function, or a class method (by passing an array [ $object, 'method_name' ]).
How to use shortcodes in the editor
In the WordPress editor, Gutenberg or Classic, a shortcode is inserted in square brackets. For our example:
[hello-world]
The full form with a closing tag also works:
[hello-world][/hello-world]
The second option is needed when content is passed between the tags (more on that below).
How to call a shortcode in PHP
In theme templates or plugins, shortcodes are executed via do_shortcode():
1 echo do_shortcode( '[hello-world]' );
The function takes a string containing a shortcode and returns the result of its execution. It's useful when you need to embed shortcode output into PHP logic, for example, in header.php or inside a loop.
Passing data to a shortcode: two ways
Method 1: content between tags
Enclose the data between the opening and closing tags:
[hello-world]from John to Mary[/hello-world]
The callback receives the string from John to Mary via the second parameter $content:
1 // Callback with content substitution between tags 2 function hello_world_shortcode_callback( $attributes, $content, $tag ) { 3 return 'Hello ' . $content . ' World!'; 4 } 5 // Registration 6 function hello_world_shortcode() { 7 add_shortcode( 'hello-world', 'hello_world_shortcode_callback' ); 8 } 9 add_action( 'init', 'hello_world_shortcode' );
Result: "Hello from John to Mary World!".
Method 2: attributes
Pass parameters directly in the opening tag:
[hello-world from="John" to="Mary"][/hello-world]
In the callback function, attributes are available via the first parameter $attributes, an associative array where keys match the attribute names:
1 // Callback with reading shortcode attributes 2 function hello_world_shortcode_callback( $attributes, $content, $tag ) { 3 return 'Hello from ' . $attributes['from'] . ' to ' . $attributes['to'] . '!'; 4 } 5 // Registration 6 function hello_world_shortcode() { 7 add_shortcode( 'hello-world', 'hello_world_shortcode_callback' ); 8 } 9 add_action( 'init', 'hello_world_shortcode' );
Output: "Hello from John to Mary!". Attributes are better for structured data (price, URL, ID), while content between tags is better for arbitrary text.
In practice, you should immediately add shortcode_atts() to attributes, a built-in WordPress function that merges passed attributes with an array of default values. Without it, accessing $attributes['from'] will throw a PHP Notice if the user didn't specify the attribute. With shortcode_atts(), you always get a populated array: defined keys with fallback values, and unknown attributes are filtered out automatically.
Two ready-to-use shortcodes for WordPress
1. Automatic current year
The [current-year] shortcode inserts the current year on its own, no need to update the date in the footer every January:
1 // Callback: returns current year via date('Y') 2 function current_year_shortcode_callback( $attributes, $content, $tag ) { 3 return date( 'Y' ); 4 } 5 // Registration of shortcode [current-year] 6 function current_year_shortcode() { 7 add_shortcode( 'current-year', 'current_year_shortcode_callback' ); 8 } 9 add_action( 'init', 'current_year_shortcode' );
Place [current-year] in the footer or post text, and the year will update automatically.
2. Registered user list
The [users-list] shortcode outputs the names of all site users as a bulleted list. Useful on a "Team" page or in admin reports:
1 // Callback: gets all users and generates HTML list 2 function my_custom_users_list() { 3 $users = get_users(); 4 $list_html = '<ul>'; 5 foreach ( $users as $user ) { 6 $list_html .= '<li>' . $user->data->display_name . '</li>'; 7 } 8 $list_html .= '</ul>'; 9 return $list_html; 10 } 11 // Registration of shortcode [users-list] 12 function register_my_users_list_shortcode() { 13 add_shortcode( 'users-list', 'my_custom_users_list' ); 14 } 15 add_action( 'init', 'register_my_users_list_shortcode' );
get_users() with no arguments returns all users. To limit the query by role, pass an array: get_users( [ 'role' => 'editor' ] ). The code goes into the active theme's functions.php or a custom plugin.
Video: creating shortcodes in WordPress
A short video on the topic, registering a shortcode from scratch, from writing the function to testing in the editor:
⁉️🤔 Frequent questions
What happens if two plugins register a shortcode with the same tag?
WordPress executes the one that loaded last. The first shortcode is overridden silently, with no errors. That's why you should always use a prefix in your shortcodes:
[acme_gallery]instead of[gallery]. This eliminates conflicts with any other plugin or theme.
Can shortcodes be called inside other shortcodes?
Yes. If the first shortcode's callback returns a string containing the second shortcode's tag, WordPress processes the nested shortcode automatically. But don't forget about
do_shortcode(): if you're building content manually and it contains shortcodes, run the string throughdo_shortcode()explicitly. The standardthe_contentbehavior does this on its own, but a direct callback call does not.
Where should shortcode code live, in the theme or a plugin?
Custom shortcodes carry functionality, not appearance, so it's better to extract them into a separate plugin. Switching themes won't break the site: shortcodes will keep working. At minimum, use a child theme if you're certain you won't change the parent theme.
Are there restrictions on the tag name?
The name must consist of Latin letters, digits, and hyphens. Spaces, underscores, and special characters are not allowed. Recommended length is up to 20 characters, lowercase. Example:
[my-plugin-shortcode].
How do you debug a shortcode if it outputs nothing?
First step, enable
WP_DEBUGinwp-config.php. Second, check that the callback returns a value viareturn, notecho. Third, make sure theinithook is present. If there are no errors but the output is empty, adderror_log()inside the callback and check the server logs.
What to remember about WordPress shortcodes
Shortcodes are a mature and stable mechanism. add_shortcode() hasn't changed in years: tag, callback, init hook, return instead of echo. Remember these four things, and any custom shortcode will work on the first try.
- If you're outputting the year in the footer, grab the ready-made
[current-year]. - If you need a user list,
[users-list]with a role filter. - If you're writing your own shortcode from scratch, always use a prefix and always use
init.
For the next step, look into packaging a shortcode as a standalone plugin and submitting it to the WordPress.org directory, it's the best way to separate functionality from the theme and level up your developer profile.


