---
title: "Building a WordPress Plugin: Frontend Display with Shortcodes"
description: "In our previous article, Implementing AJAX for Dynamic Content, we added AJAX note submissions with JavaScript, a PHP handler and server-side validation…"
url: "https://sajdoko.com/blog/building-a-wordpress-plugin-frontend-display-with-shortcodes/"
language: "en"
content_signal: "ai-train=no, search=yes, ai-input=yes"
---
# Building a WordPress Plugin: Frontend Display with Shortcodes

 4 July 2025·3 minutes read·[WordPress](https://sajdoko.com/blog/category/wordpress/)     

In our previous article, [*Implementing AJAX for Dynamic Content*](https://sajdoko.com/blog/building-a-wordpress-plugin-implementing-ajax-for-dynamic-content/), we added AJAX note submissions with JavaScript, a PHP handler and server-side validation.

 

The admin interface can now manage notes. Next, we’ll display them on public pages using a shortcode.

 

This article will cover the implementation of shortcodes. A `[quick_notes]` shortcode will be created, allowing users to display a list of their notes anywhere in their content.

 

## Displaying Notes with a Shortcode

 

The WordPress Shortcode API lets users insert dynamic content into a page. A shortcode like `[quick_notes]` is more intuitive for a non-technical user than embedding PHP or HTML.

   

### Creating a Basic Shortcode

 

Registering a shortcode is done with the [`add_shortcode()`](https://developer.wordpress.org/reference/functions/add_shortcode/) function, which should be hooked into the `init` action. This function maps a shortcode tag (e.g., ‘quick_notes’) to a callback function that generates the output.

 

```
<?php
/**
 * Renders the [quick_notes] shortcode.
 *
 * @param array $atts Shortcode attributes.
 * @return string The shortcode output.
 */
function qnm_render_notes_shortcode($atts) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'quick_notes';

    $notes = $wpdb->get_results("SELECT note, created_at FROM $table_name ORDER BY created_at DESC", ARRAY_A);

    if (empty($notes)) {
        return '';
    }

    // Use output buffering to capture the HTML
    ob_start();
   ?>
    <ul class="qnm-notes-list">
        <?php foreach ($notes as $note) : ?>
            <li><?php echo esc_html($note['note']); ?></li>
        <?php endforeach; ?>
    </ul>
    <?php
    return ob_get_clean();
}

/**
 * Initializes shortcodes.
 */
function qnm_shortcodes_init() {
    add_shortcode('quick_notes', 'qnm_render_notes_shortcode');
}
add_action('init', 'qnm_shortcodes_init');
```

  

In this handler function, several key practices are followed:

 

- The global `$wpdb` object is used to query the `wp_quick_notes` table for all notes.
- Output buffering (`ob_start()`, `ob_get_clean()`) is used to build the HTML output as a string. This is crucial because shortcode handlers must `return` their output, not `echo` it directly.
- The note content is escaped using `esc_html()` before being displayed. This is a critical security measure to prevent Cross-Site Scripting (XSS) attacks by ensuring any HTML within the note is displayed as text rather than being executed by the browser.

 

### Adding Attributes to the Shortcode

 

Shortcodes can be made more flexible by accepting attributes. For instance, a `limit` attribute could control how many notes are displayed: `[quick_notes limit="5"]`.

 

The `[shortcode_atts()](https://developer.wordpress.org/reference/functions/shortcode_atts/)` function is used to parse these attributes, merging them with a set of default values. This provides sensible defaults and ensures the shortcode works even if no attributes are provided.

 

The handler function is modified to accept and process attributes:

 

```
function qnm_render_notes_shortcode($atts) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'quick_notes';

    // 1. Define and parse attributes
    $atts = shortcode_atts(
        array(
            'limit' => -1, // Default: show all notes
        ),
        $atts,
        'quick_notes'
    );

    $limit = intval($atts['limit']);

    // 2. Build the query with the limit
    $query = "SELECT note, created_at FROM $table_name ORDER BY created_at DESC";
    if ($limit > 0) {
        $query .= $wpdb->prepare(" LIMIT %d", $limit);
    }

    $notes = $wpdb->get_results($query, ARRAY_A);
    
    //... (rest of the function remains the same)
    //...
}
```

 

This updated function now uses `shortcode_atts()` to process the `limit` attribute, validates it as an integer, and modifies the SQL query accordingly.

 

Next up is [*Security, Styling, and Distribution Best Practices*](https://sajdoko.com/blog/building-a-wordpress-plugin-security-styling-and-distribution-best-practices/). We’ll add styles, review security, test the plugin and prepare it for distribution.

  

- [Code](https://sajdoko.com/blog/category/wordpress/code/)
- [Development](https://sajdoko.com/blog/category/development/)
- [Plugin](https://sajdoko.com/blog/category/wordpress/plugin/)
- [WordPress](https://sajdoko.com/blog/category/wordpress/)
- [Quick Note Manager](https://sajdoko.com/blog/tag/quick-note-manager/)

   

## Have a project in mind?

 

Write to us and we come back with a concrete estimate within one working day.

  [Contact us →](https://sajdoko.com/contact/)     

## Leave a comment [Cancel](https://sajdoko.com/blog/building-a-wordpress-plugin-frontend-display-with-shortcodes/?format=markdown#respond)

      

## Recent Posts

- [Web and Software Development Costs in Albania: The International Buyer’s Strategic Guide](https://sajdoko.com/blog/website-costs-in-albania-international-perspective/)
- [Scaling WooCommerce Performance: Eliminating High-Traffic Checkout Bottlenecks](https://sajdoko.com/blog/scaling-woocommerce-performance-checkout-bottlenecks/)
- [The Hidden Cost of Cheap or “Free” Websites: Why €100 Web Design Costs €3,000 Later](https://sajdoko.com/blog/the-hidden-cost-of-cheap-or-free-websites/)
- [Building High-Concurrency Tour Booking Engines: Architecture Beyond Generic Plugins](https://sajdoko.com/blog/building-high-concurrency-tour-booking-engines/)
- [WordPress Malware Removal and Security Hardening: The Complete Emergency Recovery Guide](https://sajdoko.com/blog/wordpress-malware-removal-and-security-hardening/)

## Recent Comments

1. Alvaro on [Optimizing and Extending the Livewire Product Filter – Part 4](https://sajdoko.com/blog/optimizing-and-extending-the-livewire-product-filter/#comment-4)
2. [sajdoko](https://sajdoko.com) on [How to Set Up WordPress on localhost Using XAMPP: A Step-by-Step Guide](https://sajdoko.com/blog/how-to-install-wordpress-on-localhost-using-xampp-step-by-step-guide/#comment-3)
3. Roshni on [How to Set Up WordPress on localhost Using XAMPP: A Step-by-Step Guide](https://sajdoko.com/blog/how-to-install-wordpress-on-localhost-using-xampp-step-by-step-guide/#comment-2)
