Skip to content

PHP Tutorial PHP for WordPress Developers

Shortcodes

Lesson 334 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

A shortcode is a small tag in post content, written in square brackets, that WordPress replaces with the output of a PHP function.

Text
PLAIN
[greeting name="Isha"]

Register one

index.php
PHP
<?php
add_shortcode("greeting", function (array|string $atts = []): string {
    $atts = shortcode_atts(["name" => "friend"], (array) $atts, "greeting");

    return "Hello, " . esc_html($atts["name"]) . "!";
});

echo do_shortcode('[greeting name="Isha"]') . "\n";
echo do_shortcode("[greeting]");
Output
OUTPUT
Hello, Isha!
Hello, friend!
  • add_shortcode($tag, $callback) registers the tag.
  • The callback receives the attributes as an array (it can be an empty string when there are none, so cast to array).
  • shortcode_atts() merges the given attributes with defaults, and drops unknown ones.
  • The callback returns a string. WordPress puts it in the content.

Return, never echo

A shortcode must return its output. If it echoes, the text appears at the top of the page, before the content.

If your code prints a lot of HTML, capture it:

index.php
PHP
<?php
add_shortcode("card", function (): string {
    ob_start();
    ?>
    <div class="card">Captured with output buffering</div>
    <?php
    return trim(ob_get_clean());
});

echo do_shortcode("[card]");
Output
OUTPUT
<div class="card">Captured with output buffering</div>

Enclosing shortcodes

A shortcode can wrap content.

Text
PLAIN
[note type="warning"]Be careful here.[/note]
index.php
PHP
<?php
add_shortcode("note", function (array|string $atts = [], ?string $content = null): string {
    $atts = shortcode_atts(["type" => "info"], (array) $atts, "note");
    $type = in_array($atts["type"], ["info", "warning"], true) ? $atts["type"] : "info";

    return '<div class="note note-' . esc_attr($type) . '">' . do_shortcode((string) $content) . "</div>";
});

echo do_shortcode('[note type="warning"]Be careful here.[/note]');
Output
OUTPUT
<div class="note note-warning">Be careful here.</div>

The second argument is the content between the tags. Calling do_shortcode() on it allows shortcodes inside shortcodes.

Validate attributes

Attributes come from whoever edits the post. Treat them as input: allow-list values, cast numbers, escape on output.

index.php
PHP
<?php
add_shortcode("stars", function (array|string $atts = []): string {
    $atts = shortcode_atts(["count" => 5], (array) $atts, "stars");
    $count = max(0, min(10, (int) $atts["count"]));

    return str_repeat("*", $count);
});

echo do_shortcode('[stars count="3"]') . " " . do_shortcode('[stars count="999"]');
Output
OUTPUT
*** **********

Use it in a template

index.php
PHP
<?php echo do_shortcode('[greeting name="Isha"]'); ?>

A shortcode that queries posts

index.php
PHP
<?php
add_shortcode("latest_posts", function (array|string $atts = []): string {
    $atts = shortcode_atts(["count" => 3], (array) $atts, "latest_posts");

    $posts = get_posts(["numberposts" => max(1, min(10, (int) $atts["count"]))]);

    if (!$posts) {
        return "";
    }

    $items = array_map(
        fn(WP_Post $p) => '<li><a href="' . esc_url(get_permalink($p)) . '">' . esc_html(get_the_title($p)) . "</a></li>",
        $posts
    );

    return "<ul>" . implode("", $items) . "</ul>";
});

Shortcodes and blocks

The block editor (Gutenberg) uses blocks for the same purpose. Blocks have a visual editor. Shortcodes are still common for older themes and plugins, and the Shortcode block lets you use them inside the block editor.

Performance

A shortcode runs every time the content is rendered. If it does heavy work (queries, API calls), cache the result with a transient.

lightbulb
Remove a shortcode
remove_shortcode("greeting") unregisters it, and shortcode_exists("greeting") checks. strip_shortcodes($content) removes shortcode tags from text.