Home » How to Display Custom Content with WordPress Shortcodes

How to Display Custom Content with WordPress Shortcodes

wordpress code snippets

How to Display Custom Content with Shortcodes

WordPress shortcodes allow PHP-generated content to be inserted into posts, pages and other shortcode-compatible areas without repeatedly writing the underlying HTML or application logic. A shortcode can display a simple message, retrieve recent posts, generate a table, show information from a custom database query or provide the interface for a small interactive tool.

The shortcode itself is a text-based placeholder. WordPress recognises the placeholder during content processing, executes the PHP function registered to handle it and substitutes the function’s returned output into the page.

For a website publishing technical articles, downloadable resources or reference material, shortcodes provide a practical way to reuse functionality across multiple pages. A single shortcode can display different content by accepting attributes, while the underlying PHP remains in one maintainable location.

The main distinction is between displaying static content and generating content dynamically. A shortcode that returns a fixed copyright notice is straightforward. A shortcode that retrieves posts, accepts visitor input or displays personalised information requires additional consideration of security, query performance, caching and execution context.

How WordPress Processes Shortcodes

The WordPress Shortcode API provides the functions needed to register and execute custom shortcodes.

A basic shortcode looks like this:

[site_message]

A shortcode with attributes can look like this:

[site_message text="Welcome to the resource library"]

An enclosing shortcode places content between opening and closing tags:

[site_notice]
This information appears inside a custom notice box.
[/site_notice]

WordPress processes registered shortcodes when the relevant content passes through shortcode parsing. In ordinary post content, the do_shortcode() function is attached to the the_content filter.

The replacement output is generated by a PHP callback associated with the shortcode name.

Function Purpose
add_shortcode() Registers a shortcode and its callback
remove_shortcode() Removes a registered shortcode
shortcode_exists() Checks whether a shortcode is registered
shortcode_atts() Combines supplied attributes with default values
do_shortcode() Processes shortcodes within a string
has_shortcode() Checks whether content contains a particular shortcode
shortcode_parse_atts() Parses an attribute string
get_shortcode_regex() Returns a regular expression for shortcode parsing

The most commonly used functions are add_shortcode(), shortcode_atts() and do_shortcode().

A shortcode callback should return its generated content rather than echoing it directly. WordPress needs the returned string so it can insert the output at the position where the shortcode appears.

Output produced directly with echo can appear in an unexpected location because shortcode callbacks may execute during content filtering rather than at the point where the final page is sent to the browser.

Where to Put Custom Shortcode Code

Custom shortcodes should normally be registered from a plugin rather than placed directly into article content.

A small functionality plugin is particularly suitable when the shortcode must remain available after changing themes.

Create a directory named site-content-shortcodes inside:

wp-content/plugins/

Inside the new directory, create:

site-content-shortcodes.php

Add the following code:

<?php
/**
 * Plugin Name: Site Content Shortcodes
 * Description: Custom shortcodes for reusable website content.
 * Version: 1.0.0
 */

defined( 'ABSPATH' ) || exit;

The file should now be located at:

wp-content/plugins/site-content-shortcodes/site-content-shortcodes.php

Activate the plugin from Plugins > Installed Plugins in the WordPress administration area.

Additional shortcode functions can be inserted below the plugin header.

A code snippets plugin is another option, provided it supports PHP snippets and executes them in the appropriate context.

A child theme’s functions.php can also register shortcodes. However, functionality associated with content should generally remain independent of the current theme. Otherwise, changing themes may cause published shortcodes to stop working.

The code examples below are designed to be inserted into an existing PHP plugin file or suitable PHP snippets editor. Do not add repeated opening <?php tags.

Each example uses a distinctive function and shortcode name to minimise naming conflicts.

Registering a Basic Shortcode

The simplest shortcode returns a fixed text string.

function site_basic_message_shortcode() {
    return 'Welcome to the WordPress resource library.';
}

add_shortcode(
    'site_basic_message',
    'site_basic_message_shortcode'
);

The add_shortcode() function connects the shortcode name to the callback function.

Its syntax is:

add_shortcode( $tag, $callback );

The first argument identifies the shortcode. The second specifies the function WordPress should execute.

After registering the example, insert the following into a Shortcode block:

[site_basic_message]

The rendered page displays:

Welcome to the WordPress resource library.

The callback runs when WordPress processes the shortcode. It returns the replacement string, which WordPress inserts into the surrounding content.

The shortcode name must be unique. If another plugin registers the same shortcode tag, the registration that takes precedence can replace the original callback.

Using a site-specific prefix reduces this risk.

For a fixed message, a reusable block pattern may be simpler than PHP. Shortcodes become more useful when the returned content changes according to attributes, post information or other data.

Displaying HTML with a Shortcode

A shortcode can return HTML markup as well as plain text.

The following example displays a simple information box.

function site_information_box_shortcode() {
    $output = '<div class="site-information-box">';

    $output .= '<strong>';
    $output .= esc_html( 'Important Information' );
    $output .= '</strong>';

    $output .= '<p>';
    $output .= esc_html(
        'Check the instructions before changing website settings.'
    );
    $output .= '</p>';

    $output .= '</div>';

    return $output;
}

add_shortcode(
    'site_information_box',
    'site_information_box_shortcode'
);

Usage:

[site_information_box]

The PHP callback returns a complete HTML container containing a heading-style label and a paragraph.

The esc_html() function ensures the text is suitable for an HTML text context.

The surrounding HTML is fixed markup created by the developer rather than data supplied by the visitor.

The box can be styled through the active theme or a custom stylesheet:

.site-information-box {
    padding: 20px;
    margin: 24px 0;
    background-color: #f0f6ff;
    border-left: 4px solid #2563eb;
    border-radius: 4px;
}

.site-information-box strong {
    display: block;
    margin-bottom: 8px;
}

.site-information-box p {
    margin: 0;
}

The CSS belongs in a stylesheet or appropriate CSS editor, not inside the PHP shortcode callback.

Separating presentation from application logic makes it easier to change the design without altering the PHP.

Using Shortcode Attributes

Attributes allow the same shortcode to produce different output depending on how it is inserted into the content.

A simple text shortcode could accept a title and message.

[site_custom_message title="Notice" text="Maintenance is scheduled."]

WordPress passes the attributes to the registered callback.

The shortcode_atts() function provides default values and ignores unsupported attributes.

Creating a Shortcode with Attributes

function site_custom_message_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'title' => 'Information',
            'text'  => 'No additional information.',
        ),
        $atts,
        'site_custom_message'
    );

    $title = sanitize_text_field( $atts['title'] );
    $text  = sanitize_text_field( $atts['text'] );

    $output = '<div class="site-custom-message">';

    $output .= '<strong>' .
        esc_html( $title ) .
        '</strong>';

    $output .= '<p>' .
        esc_html( $text ) .
        '</p>';

    $output .= '</div>';

    return $output;
}

add_shortcode(
    'site_custom_message',
    'site_custom_message_shortcode'
);

Usage:

[site_custom_message title="Update" text="New reference sheets are available."]

Both attributes are optional.

If no values are supplied, the defaults are used:

[site_custom_message]

The shortcode_atts() function returns an array containing the supported attribute names and their final values.

Shortcode attribute names are case-insensitive during parsing and are normally converted to lowercase. Their values retain their supplied content.

Use lowercase attribute names in both the callback and the shortcode itself.

Attribute Handling Reference

Requirement Appropriate approach
Default attribute value shortcode_atts()
Integer attribute absint() or suitable integer validation
Plain-text attribute sanitize_text_field()
URL attribute esc_url() when outputting a URL
HTML text output esc_html()
HTML attribute output esc_attr()
Restrict allowed values Compare against a defined allowlist
Optional parameter Supply an appropriate default
Unsupported attribute Ignore through shortcode_atts()

Sanitisation and escaping serve different purposes. Sanitisation prepares data for an intended use; escaping protects the output context.

For example, a URL might be validated or normalised when accepted, then escaped with esc_url() when inserted into an HTML link.

Creating an Enclosing Shortcode

An enclosing shortcode allows the content author to place text between opening and closing tags.

This is useful for notices, callouts and other reusable content containers.

The basic format is:

[site_note]Text to display inside the note.[/site_note]

The callback receives the enclosed content as its second argument.

function site_note_shortcode(
    $atts,
    $content = null
) {
    if ( null === $content ) {
        return '';
    }

    $atts = shortcode_atts(
        array(
            'title' => 'Note',
        ),
        $atts,
        'site_note'
    );

    $title = sanitize_text_field( $atts['title'] );

    $output = '<aside class="site-note">';

    $output .= '<strong>' .
        esc_html( $title ) .
        '</strong>';

    $output .= wpautop(
        esc_html( $content )
    );

    $output .= '</aside>';

    return $output;
}

add_shortcode(
    'site_note',
    'site_note_shortcode'
);

Usage:

[site_note title="Compatibility"]
Check the WordPress and PHP requirements before installing the plugin.
[/site_note]

The callback returns an aside element containing the title and supplied content.

The content is escaped as plain text and then formatted into paragraphs using wpautop().

This means HTML supplied inside the shortcode will be displayed as text rather than interpreted as HTML.

That restriction is intentional for a simple information box.

A more advanced shortcode could allow selected formatting through wp_kses_post(), but the permitted markup and content-processing behaviour should be defined explicitly.

Enclosing shortcodes can be awkward when nested within other shortcodes, particularly where identical shortcode names are nested. A block-based implementation may be preferable for complex editable layouts.

Displaying Dynamic WordPress Content

The principal advantage of shortcodes is their ability to retrieve and display data already stored in WordPress.

The content can change automatically as posts are published, metadata is updated or website settings change.

Display the Latest Posts

A recent-posts shortcode can be used on a homepage, topic index or resource landing page.

The example below accepts a count attribute and displays a list of published posts.

function site_latest_posts_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'count' => 5,
        ),
        $atts,
        'site_latest_posts'
    );

    $count = max(
        1,
        min( 20, absint( $atts['count'] ) )
    );

    $query = new WP_Query(
        array(
            'post_type'           => 'post',
            'post_status'         => 'publish',
            'posts_per_page'      => $count,
            'ignore_sticky_posts' => true,
            'no_found_rows'       => true,
        )
    );

    if ( ! $query->have_posts() ) {
        return '<p>No posts available.</p>';
    }

    $output = '<ul class="site-latest-posts">';

    while ( $query->have_posts() ) {
        $query->the_post();

        $output .= '<li>';

        $output .= '<a href="' .
            esc_url( get_permalink() ) .
            '">';

        $output .= esc_html( get_the_title() );

        $output .= '</a>';

        $output .= '</li>';
    }

    wp_reset_postdata();

    $output .= '</ul>';

    return $output;
}

add_shortcode(
    'site_latest_posts',
    'site_latest_posts_shortcode'
);

Usage:

[site_latest_posts count="8"]

The WP_Query object retrieves the requested posts.

The shortcode limits the count to a maximum of 20, preventing an unexpectedly large query if an excessive value is entered.

The no_found_rows argument avoids calculating the total number of matching posts when pagination is unnecessary.

The wp_reset_postdata() function restores the previous global post context after the custom loop.

Without this reset, subsequent template functions might operate on the final post from the shortcode query rather than the original page.

The generated HTML is a conventional unordered list. It can be styled through the .site-latest-posts class.

For large websites, repeated shortcode queries can introduce additional database work. Use them selectively, particularly on heavily visited pages.

Display Posts from a Selected Category

A category-based shortcode can generate content lists for topic pages.

For a WordPress resource website, separate pages might display articles about development, security, performance or troubleshooting.

function site_category_articles_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'slug'  => '',
            'count' => 5,
        ),
        $atts,
        'site_category_articles'
    );

    $slug = sanitize_title( $atts['slug'] );

    if ( '' === $slug ) {
        return '';
    }

    $count = max(
        1,
        min( 20, absint( $atts['count'] ) )
    );

    $query = new WP_Query(
        array(
            'post_type'           => 'post',
            'post_status'         => 'publish',
            'category_name'       => $slug,
            'posts_per_page'      => $count,
            'ignore_sticky_posts' => true,
            'no_found_rows'       => true,
        )
    );

    if ( ! $query->have_posts() ) {
        return '<p>No matching articles.</p>';
    }

    $output = '<ul class="site-category-articles">';

    while ( $query->have_posts() ) {
        $query->the_post();

        $output .= '<li><a href="' .
            esc_url( get_permalink() ) .
            '">' .
            esc_html( get_the_title() ) .
            '</a></li>';
    }

    wp_reset_postdata();

    $output .= '</ul>';

    return $output;
}

add_shortcode(
    'site_category_articles',
    'site_category_articles_shortcode'
);

Usage:

[site_category_articles slug="wordpress-security" count="6"]

The slug attribute identifies the category. It must match the category’s URL slug rather than its displayed name.

The shortcode returns published standard posts assigned to that category.

The result changes automatically as qualifying posts are published.

This is particularly useful for creating curated reference pages without manually updating every article link.

It does not provide a complete replacement for native WordPress category archives. Those archives already handle pagination, taxonomy metadata and other functionality that a basic shortcode does not implement.

Display a Post’s Last Modified Date

A shortcode can retrieve information about the current post or page.

The following example displays the stored modification date using the website’s configured date format.

function site_modified_date_shortcode() {
    $post = get_post();

    if ( ! $post instanceof WP_Post ) {
        return '';
    }

    $timestamp = get_post_modified_time(
        'U',
        true,
        $post
    );

    if ( ! $timestamp ) {
        return '';
    }

    $date = wp_date(
        get_option( 'date_format' ),
        (int) $timestamp
    );

    return '<span class="site-modified-date">' .
        esc_html( $date ) .
        '</span>';
}

add_shortcode(
    'site_modified_date',
    'site_modified_date_shortcode'
);

Usage:

[site_modified_date]

The callback obtains the current post object using get_post().

It then retrieves the stored modification timestamp and formats it according to the WordPress date settings.

This shortcode works best in a context where the current post is clearly defined, such as the content of a single post or page.

It may produce unexpected results when executed inside a custom query or another shortcode that modifies the current post context.

The modification date is not necessarily the date when an article was technically reviewed. If content verification dates are required, maintain a separate field rather than treating every editorial change as a complete review.

Display Custom Post Metadata

WordPress stores additional information about posts in post metadata.

Custom metadata can contain fields such as document versions, reference codes, revision information or other values associated with a post.

A shortcode can retrieve a specific metadata field.

The following example displays a resource version stored under the key _site_resource_version.

function site_resource_version_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'id' => 0,
        ),
        $atts,
        'site_resource_version'
    );

    $post_id = absint( $atts['id'] );

    if ( 0 === $post_id ) {
        $post_id = get_the_ID();
    }

    if ( ! $post_id ) {
        return '';
    }

    $post = get_post( $post_id );

    if (
        ! $post instanceof WP_Post ||
        'publish' !== $post->post_status ||
        ! is_post_publicly_viewable( $post )
    ) {
        return '';
    }

    $version = get_post_meta(
        $post_id,
        '_site_resource_version',
        true
    );

    if (
        ! is_scalar( $version ) ||
        '' === (string) $version
    ) {
        return '';
    }

    return '<span class="site-resource-version">' .
        esc_html( 'Version: ' . (string) $version ) .
        '</span>';
}

add_shortcode(
    'site_resource_version',
    'site_resource_version_shortcode'
);

Usage for the current post:

[site_resource_version]

Usage for a specific post:

[site_resource_version id="123"]

The numeric post ID identifies the post whose metadata should be retrieved.

The example checks that the post is published and publicly viewable before displaying the value. This prevents the shortcode from exposing metadata belonging to drafts, private posts or non-public post types through an arbitrary ID.

The metadata key begins with an underscore. WordPress commonly uses underscore-prefixed keys to keep metadata out of some standard custom-field interfaces, but that naming convention does not itself provide access control.

The field must already exist and contain a value. This shortcode does not create or update the metadata.

For sensitive metadata, a public shortcode is inappropriate unless the access controls are explicitly designed for that use.

Creating Reusable Resource Cards

A resource website may need to display consistent cards linking to particular articles, documentation pages or other published resources.

Instead of manually constructing the same markup repeatedly, a shortcode can accept a post ID and generate the title, excerpt and link.

The following example creates a simple reusable resource card.

function site_resource_card_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'id' => 0,
        ),
        $atts,
        'site_resource_card'
    );

    $post_id = absint( $atts['id'] );

    if ( ! $post_id ) {
        return '';
    }

    $post = get_post( $post_id );

    if (
        ! $post instanceof WP_Post ||
        'publish' !== $post->post_status ||
        ! is_post_publicly_viewable( $post )
    ) {
        return '';
    }

    $title = get_the_title( $post_id );
    $url   = get_permalink( $post_id );

    if ( ! $url ) {
        return '';
    }

    $excerpt = get_the_excerpt( $post_id );

    $output = '<article class="site-resource-card">';

    $output .= '<h3>';

    $output .= '<a href="' .
        esc_url( $url ) .
        '">';

    $output .= esc_html( $title );

    $output .= '</a></h3>';

    if ( '' !== trim( $excerpt ) ) {
        $output .= '<p>' .
            esc_html( wp_strip_all_tags( $excerpt ) ) .
            '</p>';
    }

    $output .= '</article>';

    return $output;
}

add_shortcode(
    'site_resource_card',
    'site_resource_card_shortcode'
);

Usage:

[site_resource_card id="123"]

The example retrieves the selected published post and generates its title, permalink and excerpt.

It does not require the content author to maintain a separate title and description inside every shortcode.

If the post’s title or excerpt changes, the updated values are used the next time the shortcode is rendered.

One qualification is that get_the_excerpt() may generate an excerpt from the post content when no manual excerpt exists. This can involve content filters and additional processing, depending on the site’s configuration.

For a large collection of resource cards, manually written excerpts may provide more predictable output.

The card can be styled with CSS:

.site-resource-card {
    padding: 24px;
    margin-bottom: 20px;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    background-color: #ffffff;
}

.site-resource-card h3 {
    margin-top: 0;
    margin-bottom: 12px;
}

.site-resource-card h3 a {
    text-decoration: none;
}

.site-resource-card p {
    margin-bottom: 0;
    line-height: 1.6;
}

This approach is useful for individual article recommendations, selected reference documents and resource landing pages.

It is less suitable for large product catalogues with pricing, stock, purchase controls or access restrictions. Those requirements should normally be handled by the relevant ecommerce system.

Using Output Buffering for Complex Shortcodes

Building HTML through repeated string concatenation is manageable for small shortcodes. It becomes less convenient when the output contains tables, forms or multiple nested elements.

PHP output buffering provides an alternative.

The ob_start() function begins capturing generated output. The ob_get_clean() function retrieves the captured output as a string and closes that buffer.

The shortcode can then return the string instead of displaying it immediately.

Example: Display a Resource Information Panel

function site_resource_panel_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'title'  => 'Resource Information',
            'status' => 'Available',
        ),
        $atts,
        'site_resource_panel'
    );

    $title = sanitize_text_field( $atts['title'] );

    $allowed_statuses = array(
        'Available',
        'Coming Soon',
        'Updated',
    );

    $status = in_array(
        $atts['status'],
        $allowed_statuses,
        true
    ) ? $atts['status'] : 'Available';

    ob_start();
    ?>

    <section class="site-resource-panel">
        <h3><?php echo esc_html( $title ); ?></h3>

        <p>
            <strong>Status:</strong>
            <?php echo esc_html( $status ); ?>
        </p>
    </section>

    <?php
    return ob_get_clean();
}

add_shortcode(
    'site_resource_panel',
    'site_resource_panel_shortcode'
);

Usage:

[site_resource_panel title="WordPress Reference Pack" status="Updated"]

The callback uses ordinary HTML markup between PHP sections, rather than constructing the entire output through concatenation.

The status value is checked against an explicit list of permitted values.

If an unsupported status is supplied, the default value is used.

Output buffering can make larger templates easier to read, but it must be handled carefully. A missing ob_get_clean() call or an error that leaves an output buffer open can interfere with later output.

For complex components, consider separating the HTML template from the data-processing function.

Shortcodes that require extensive form handling, database updates or interactive user interfaces may be better implemented as a dedicated plugin or block rather than one very large callback.

Executing Shortcodes Programmatically

Shortcodes are not limited to content entered through the WordPress editor.

The do_shortcode() function processes registered shortcodes within a PHP string.

Its basic syntax is:

do_shortcode( $content );

For example, within an appropriate theme template:

echo do_shortcode(
    '[site_latest_posts count="5"]'
);

This executes the registered site_latest_posts shortcode and outputs its returned HTML.

The example assumes the shortcode has already been registered.

The function can also process strings containing normal text and several shortcodes.

$content = 'Latest articles: [site_latest_posts count="3"]';

$output = do_shortcode( $content );

echo $output;

When using do_shortcode(), distinguish between trusted shortcode definitions constructed by the developer and content supplied by visitors.

Do not allow untrusted visitors to submit arbitrary shortcode expressions for execution unless the supported shortcode set and permissions have been carefully considered. Registered shortcode callbacks may perform operations or retrieve information that was not intended to be exposed through an unrestricted visitor-controlled interface.

Avoid applying do_shortcode() indiscriminately to data that does not require shortcode parsing.

Processing Shortcodes Inside Other Shortcodes

WordPress does not automatically process every nested shortcode within the content passed to a callback.

A shortcode designed to support nested content can explicitly call do_shortcode().

function site_content_wrapper_shortcode(
    $atts,
    $content = null
) {
    if ( null === $content ) {
        return '';
    }

    $processed = do_shortcode( $content );

    return '<div class="site-content-wrapper">' .
        wp_kses_post( $processed ) .
        '</div>';
}

add_shortcode(
    'site_content_wrapper',
    'site_content_wrapper_shortcode'
);

Usage:

[site_content_wrapper]
[site_latest_posts count="3"]
[/site_content_wrapper]

The outer shortcode processes the enclosed content, including the inner shortcode.

The result is then filtered through wp_kses_post() to permit only HTML elements and attributes allowed in ordinary post content.

This makes the wrapper unsuitable for nested components that depend on disallowed markup, such as certain forms or scripts. A more specialised wrapper would need a carefully designed output policy.

Identical nested shortcode names can also cause parsing problems. Avoid relying on complicated nesting structures.

For deeply nested interactive content, WordPress blocks are often a more maintainable solution.

Displaying Shortcodes in the WordPress Editor

The block editor includes a dedicated Shortcode block.

To insert one, open a post or page, add a Shortcode block and enter the registered shortcode expression.

[site_custom_message title="Notice" text="Please review this information."]

The block stores the shortcode expression and allows WordPress to process it during rendering.

The editor preview may not always match the final front-end result, particularly when a shortcode depends on front-end scripts, current-post context or information that is unavailable in the editor.

Some shortcodes are also supported in other content locations, including classic editor content and suitable widget areas.

However, shortcode processing is not guaranteed in every text field supplied by a theme, plugin or page builder.

A field may store its content as plain text without passing it through the Shortcode API.

If the shortcode appears literally on the page, first establish whether the field supports shortcode execution before assuming the PHP callback is incorrect.

Shortcodes and Page Builders

Many page builders provide modules or widgets that support WordPress shortcodes.

For example, a shortcode may be inserted into a suitable text or code-oriented module, depending on the builder.

The crucial requirement is that the module must pass the content through shortcode processing.

Some builders also modify their rendered markup or execute content in a different context, which can affect how custom shortcodes operate.

Shortcodes that return simple HTML are usually easier to integrate than shortcodes requiring JavaScript initialisation or complex nested content.

For interactive tools, loading the required JavaScript and CSS through WordPress’s enqueueing system is generally preferable to inserting script tags directly into the shortcode output.

Shortcode Security and Output Handling

A shortcode may receive attributes from a content author, retrieve information from the database and generate HTML.

Each stage needs appropriate handling.

Data or operation Appropriate protection
Plain-text attribute sanitize_text_field()
Integer attribute absint() or suitable validation
Output text esc_html()
HTML attribute esc_attr()
URL output esc_url()
Restricted HTML content wp_kses_post()
State-changing request Capability check and suitable nonce verification
Database query with variable SQL Prepared query using appropriate APIs
Post metadata access Check whether the requesting user may access the data
User-specific information Enforce authorisation before display

Escaping should match the output context.

A string inserted into ordinary HTML text requires different handling from a string inserted into a URL or HTML attribute.

Do not assume that shortcode_atts() validates supplied values. Its main purpose is to combine known attributes with defaults.

A shortcode attribute used as an integer should be converted and checked against sensible limits.

A shortcode that retrieves a post by ID should consider whether the post is public, private, a draft or password-protected.

A shortcode that displays account details should check the current user’s permissions before accessing or returning the information.

Shortcodes are not a security boundary. The fact that a shortcode is normally inserted by an administrator does not mean its callback can safely omit access checks when processing sensitive operations.

Performance and Caching Considerations

A simple shortcode returning fixed text has little overhead.

A shortcode that executes database queries, processes large datasets or calls external services can affect page generation time.

The cost becomes more significant if the same shortcode appears multiple times on a page.

For example, a resource index containing 30 separate shortcodes may execute 30 additional queries if each callback independently retrieves data.

Where possible, combine related data retrieval into one query or use a caching strategy suited to the content.

WordPress transients can cache data temporarily, but their expiration times are maximum intended lifetimes rather than guarantees that cached data will remain available for that duration.

Persistent object caching, page caching and transient storage have different characteristics.

Shortcode type Caching consideration
Fixed text Usually safe to cache
Recent posts Cache may delay the appearance of newly published posts
Category lists Cache should reflect publishing and category changes
Current date Cached pages may retain an earlier value
User-specific content Must not leak between visitors
Shopping basket information Requires appropriate personalised handling
External API results May benefit from controlled temporary caching
Random content Full-page caching may prevent variation

Do not cache personalised shortcode output in a publicly shared page cache unless the caching system correctly separates responses by user or other relevant context.

For resource directories, consider whether native WordPress Query Loop blocks can provide the same result more efficiently and with less custom code.

Troubleshooting Common Shortcode Problems

Symptom Likely cause Specific check
Shortcode appears as text Not registered or field does not process shortcodes Check plugin activation and content location
Shortcode displays nothing Callback returns an empty string Inspect callback conditions
Output appears above content Callback uses echo Return a string instead
PHP fatal error Duplicate function or invalid PHP Inspect error log and function names
Attributes ignored Incorrect names or defaults Check shortcode_atts()
Unexpected attribute values Missing validation Validate and normalise attributes
HTML displays as text Output escaped deliberately Check whether HTML is intended
HTML layout is broken Invalid or unbalanced markup Inspect returned HTML
Wrong post information Current post context changed Check custom queries and postdata restoration
Private content appears Inadequate access checks Verify post visibility and permissions
Recent posts do not update Full-page or object caching Identify the relevant cache
Nested shortcode is not processed Nested parsing not requested Check controlled use of do_shortcode()
Styles do not apply CSS not loaded or selector mismatch Inspect markup and stylesheet loading
JavaScript functionality fails Required script not loaded or initialised Inspect browser console
Duplicate shortcode behaviour Conflicting registration Check shortcode names across active plugins
Slow page rendering Repeated expensive queries Profile shortcode callbacks

Checking Whether a Shortcode Is Registered

WordPress provides shortcode_exists() for checking whether a specific shortcode name is registered.

if ( shortcode_exists( 'site_latest_posts' ) ) {
    // The shortcode is registered.
}

This verifies registration but does not establish that the shortcode’s callback produces the intended result.

A registered shortcode may still return no content because of its attributes, current post context or internal conditions.

Removing a Registered Shortcode

The remove_shortcode() function removes a shortcode registration.

remove_shortcode( 'site_latest_posts' );

After removal, that shortcode is no longer processed by its previous callback unless another component registers it again.

The removal must occur after the relevant shortcode registration. Otherwise, later registration code may restore the shortcode.

Avoid using remove_all_shortcodes() as a routine troubleshooting technique. It removes all current shortcode registrations, potentially breaking unrelated features.

Diagnosing PHP Errors

A shortcode callback executes PHP as part of the WordPress request.

An invalid callback, duplicate function declaration or unexpected argument type can therefore cause a PHP error that affects the surrounding page.

When investigating a fault, enable logging in a controlled environment and examine the corresponding PHP error details.

A minimal debugging configuration in wp-config.php may include:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

These constants must be placed in the appropriate configuration location and must not duplicate existing definitions.

Debugging should be enabled deliberately, with log access protected.

Do not expose PHP error details publicly on a production website.

A staging environment is preferable for diagnosing failures that require changing executable PHP code.

When to Use Shortcodes Instead of Blocks

Shortcodes remain useful, but WordPress blocks provide another way to create reusable and dynamic content.

The most appropriate choice depends on the complexity of the feature and how the content will be edited.

Requirement Shortcode Block
Insert simple dynamic text Suitable Suitable
Display a PHP-generated resource list Suitable Dynamic block also suitable
Reuse existing legacy functionality Particularly suitable May require adaptation
Visual editing experience Limited Usually better
Complex nested layouts Awkward Usually better
Configure attributes in plain text Straightforward Usually uses block controls
Advanced interactive interface Requires additional implementation Often better integrated
Lightweight plugin development Relatively straightforward May require more development
Insert functionality into shortcode-compatible builders Useful Depends on builder support
Long-term visual content editing Less convenient Generally preferable

For a simple custom calculator, generator or resource display, a shortcode can provide a manageable implementation without requiring a complete custom Gutenberg block.

A shortcode also provides a stable insertion interface: the PHP implementation can be updated while existing shortcode expressions remain unchanged.

However, shortcodes should not be used merely because they are familiar. Standard paragraphs, reusable patterns, Query Loop blocks and other native WordPress features may already provide the required functionality.

For a website distributing lightweight WordPress plugins, shortcodes are especially useful because they allow a plugin to expose a self-contained tool that can be inserted into different themes and page builders.

The developer must still ensure that the tool’s assets, security checks and data processing operate correctly in the intended environment.

Practical Shortcode Development Reference

Requirement Recommended approach
Register a shortcode add_shortcode()
Generate output Return a string
Set default attributes shortcode_atts()
Handle enclosing content Accept the $content parameter
Display HTML text esc_html()
Display an HTML attribute esc_attr()
Display a URL esc_url()
Permit selected HTML wp_kses_post()
Generate larger HTML templates Output buffering where appropriate
Retrieve recent posts WP_Query
Retrieve post metadata get_post_meta()
Restore global post context wp_reset_postdata()
Process shortcodes in PHP do_shortcode()
Check shortcode registration shortcode_exists()
Remove a shortcode remove_shortcode()
Avoid naming collisions Use distinctive shortcode names
Support theme changes Register site functionality in a plugin
Protect private data Perform explicit access checks
Limit expensive operations Validate parameters and optimise queries
Load supporting assets Use WordPress enqueue functions
Debug failures Inspect PHP logs and rendered output

A maintainable shortcode should have a clearly defined purpose, documented attributes, predictable output and minimal dependencies. It should return valid markup, handle missing data without producing warnings and avoid modifying unrelated WordPress state.

For a growing reference website, a small collection of focused shortcodes can provide reusable article lists, resource cards, metadata displays and interactive plugin interfaces without requiring extensive theme development.

You may also like