Shortcode Generator

Generate an add_shortcode() handler with typed attributes, shortcode_atts defaults, escaped output and an optional enclosing form.

Live output

Enable JavaScript to customise; default output below.

What an author types in brackets. Prefix it: a bare tag like [button] collides with other plugins.

Live preview shortcode.php
<?php
/**
 * Handles the [my_plugin_button] shortcode.
 *
 * @param array       $atts    Shortcode attributes.
 * @return string HTML, already escaped.
 */
function my_plugin_button_shortcode( $atts ) {
	$atts = shortcode_atts(
		array(
			'label' => 'Read more',
			'url' => '#',
		),
		$atts,
		'my_plugin_button'
	);

	// Each value is escaped for the context its kind implies.
	$safe_label = esc_html( $atts['label'] );
	$safe_url = esc_url( $atts['url'] );

	// The visible text: the wrapped content when this shortcode encloses,
	// otherwise the first attribute above. Swap it for whichever value you want.
	$text = $safe_label;
	$class_attr = ' class="' . esc_attr( 'my-plugin-button' ) . '"';

	// href falls back to # when the shortcode has no url attribute.
	$href = isset( $safe_url ) ? $safe_url : '#';

	return '<a' . $class_attr . ' href="' . $href . '">' . $text . '</a>';
}
add_shortcode( 'my_plugin_button', 'my_plugin_button_shortcode' );

Output is valid and updates as you type.

Name the shortcode, list the attributes it takes and pick what it outputs. The generator writes the handler with shortcode_atts() defaults and the escaping each attribute’s kind needs.

How to use

  1. Pick the tag an author will type. Prefix it: a bare [button] collides with every other plugin that had the same idea.
  2. Add the attributes. Each one gets a default, so a shortcode written without it still renders.
  3. Set each attribute’s kind. Text is escaped with esc_html(), url with esc_url(), number is cast, and html goes through wp_kses_post().
  4. Turn on enclosing if the shortcode wraps content, as in [tag]…[/tag]. The handler then receives that content and runs nested shortcodes on it.
  5. Paste the result into your plugin. It hooks itself with add_shortcode() at the bottom.

Example

A callout that wraps content in a div, with a tone attribute and a column count:

$atts = shortcode_atts(
	array(
		'tone' => 'warning',
		'columns' => '2',
	),
	$atts,
	'acme_callout'
);

$safe_tone = esc_html( $atts['tone'] );
$safe_columns = (float) $atts['columns'];

$text = $content ? wp_kses_post( do_shortcode( $content ) ) : '';

Written in a post as [acme_callout tone="danger"]Back up first.[/acme_callout].

The escaping happens on the way out, at the point the value is printed, which is where WordPress expects it rather than on the way in.

Pitfalls

  • An unprefixed tag is a collision waiting to happen. Shortcodes share one global namespace across every plugin and theme on the site.
  • A shortcode handler must return its output, never echo it. Echoing prints the content at the top of the page, above everything else.
  • shortcode_atts() lowercases attribute names, so [tag Label="x"] arrives as label. Name attributes in lowercase and expect them that way.
  • Attribute values with spaces need quotes in the post: [tag label="Read more"]. Without quotes only the first word arrives.
  • An enclosing shortcode gets $content only when the closing tag is present. [tag] on its own passes null, which is why the generated code guards it.
  • Running do_shortcode() on wrapped content is what makes nesting work, and it also means anything an author nests is executed. Keep wp_kses_post() around it.
  • Numbers arrive as strings. The generated cast is the difference between 2 and "2 onclick=...".
  • Shortcodes do not run in widgets or excerpts by default, and block themes render them only inside a shortcode block or post content.

Compatibility

add_shortcode() and shortcode_atts() have been stable since WordPress 2.5, and wp_kses_post() since 2.9, so the generated code runs on any WordPress still receiving updates. It targets PHP 7.0 and later. The tool itself runs in your browser with no network request.

Frequently asked questions

Should I write a shortcode or a block?
A block for anything an author places visually in the editor. A shortcode when the thing has to work in older content, in text widgets, or when someone will paste it into a template call.
Why does my shortcode output appear at the top of the page?
The handler is echoing instead of returning. Collect the markup into a string and return it.
How do I use this in a template file?
echo do_shortcode( '[my_plugin_button label="Buy"]' );. Passing user input into that string is the one way to reintroduce the escaping problem the handler solves.
Do attribute names have to be lowercase?
Yes in practice. shortcode_atts() lowercases the keys it receives, so an uppercase name never matches your defaults array.
Can a shortcode take no attributes at all?
Yes. Remove every attribute row and the handler still works; shortcode_atts() simply has nothing to merge.

From the people who built this tool

WP Adminify

The WordPress admin, rebuilt: a dashboard worth looking at, menu and column control, a real file manager and the login page your client sees.

See WP Adminify Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.