Ultimate Guide to Develop Your First WordPress Plugin




The best thing about WordPress is it can be customized and extended with plug-ins. Developing WordPress plug-in seems like a difficult task; however it is actually simpler than you may think. In this post, we are going to teach you how to develop WordPress plug-in with few simple steps.

ultimate guide to develop beginners' first WP plugin

Before beginning with development task, I'm providing information about what actually WordPress plug-in is. Let me tell you something:

Actually WordPress' first plugin supported version was WordPress 1.2 Mingus. And the first plugin was developed on Monday 19 April 2004 by Dean Allen. The name of that plugin was This is Textile, A Humane Web Text Generator.

Ahead, WP plugin is a PHP script that customize and extend the native functionality of WordPress. Installing flexible Plug-in API in your WordPress, you will enjoy many advantages like:

● It makes your modularity of code for a particular project increases and make updates and maintenance easier than before.

● Develop separate Plug-in functionality from themes

● Extended code base

● Allows using same plug-ins with various themes and has some design-independent functionality

● Implementing modern programming techniques, OOP is easier with a PHP script; at same time it has ability to use native WordPress functions, classes and APIs.

The main task for beginner is to first build a solid plugin foundation that meets WordPress requirements and make plug-in more recognizable. Your plug-in should follow common practices and conventions that accepted by community.

Plugin Name and Files

Firstly, you have to create a unique plug-in name. Create the brand prefix to increase the chance of a name being unique. Ensure to prefix with short reference to the plugin’s name in names of files, classes, functions, variables, etc. as it avoids conflicts with other plug-in’s name and the core as well.

Now, we are going to adopt the name Hello World Plugin and to increase the chance of uniqueness, we are going to use My Plugin converted into abbreviation MP. Now, the exact name would be MP Hello World Plugin. In next step, you have to create the plugin’s files and store them in separate folder inside a dedicated plugin folder. Now, name this folder in accordance with plugin, i.e. mp-helloworld. The main plug-in file is also included in this folder with the same name: mp-helloworld.php.

It is also recommended to use a readme.txt file by WordPress Codex as this file has detailed information about your plugin in a standardized format. It is must to have readme.txt, if you are going to submit your plugin to the WordPress repository. Don’t think that it is burden for you; it provides you many advantages as well. Moreover, if you’re plug-in have many files or load some assets like images, css and js files then it should be managed in sub-folders. You can organize file like this way:

File Structure

Plugin Header

Every plug-in must have header. Having header helps you to organize the script as a valid plugin and provide output proper information on the plugins’ management screen. One can find header, which is a PHP comment block, at the top of the main plugin’s file:

Plugin Name: MP Hello World
Description: Create hello world message
Version: 1.0
Author: Author's name
Author URI: http://authorsite.com/
Plugin URI: http://authorsite.com/mp-helloworld

One can see the header’s information in the corresponding plugin’s row on the management screen.

Plugin Screen

No matter you lines are in order to not, ensure to have in UTF-8 encoding. It is essential to be consistent with the version numbering pattern that you have selected for the WordPress upgrade mechanism to perceive it accurately.

Paths to Files

In next step, we have to decide the correct paths (or URLs) inside the plug-in code. It is clear that paths to plugin files should not be hardcoded as the wp-content folder but could be moved from its default location and it should be detected. plugin_dir_path and plugin_dir_url are the two WordPress funcations that address the issue. However, we can move further by using various tricks:

define('MP_HELLOWORLD_DIR', plugin_dir_path(__FILE__));
define('MP_HELLOWORLD_URL', plugin_dir_url(__FILE__));

One can easily detect the path and URL of the plugin’s folder with this little snippet inside the WordPress installation and can also assign them to suitable constants. Then, we are going to use these constants with known relative paths to sub-folders, for example MP_HELLOWORLD_DIR.'assets/img/image.jpg'. One can easily add plugin files from sub-folders inside the main file by using these constants:

function mp_helloworld_load(){
    if(is_admin()) //load admin files only in admin

Plugin States

In this state, plug-in could be in an active or inactive state after installation. Active states mean that it was activated by the user and its code will be implemented by WordPress every time a page is requested.

In case, the plug-in could be deactivated by the users that means the files are kept in their place, but the codes isn’t implemented. In these states, changes can be caught by WP and can also implement some code planned for such changes. If any code is scheduled for activation or deactivation then it will be executed instantly without loading page.

One can register a so-called activation hook and deactivation hook for activation and deactivation actions. It is just a code that allows WP to implement one particular function on activation and another on deactivation. Below given is an example of such code:

register_activation_hook(__FILE__, 'mp_helloworld_activation');
register_deactivation_hook(__FILE__, 'mp_helloworld_deactivation');

function mp_helloworld_activation() {
 //actions to perform once on plugin activation go here   

function mp_helloworld_deactivation() {    
 // actions to perform once on plugin deactivation go here     

If you want to uninstall actions then you can with two alternatives. Firstly, you have to create a uninstall.php file in the plugin’s folder and add all required code. Once you uninstall.php exists, WP automatically will perform it when the plugin is deleted by the user.

Secondly, you can register an uninstall hook like we did with the activation and deactivation hooks. The difficult part is to call it only once on activation like:

register_activation_hook(__FILE__, 'mp_helloworld_activation');

function mp_helloworld_activation() {
    //actions to perform once on plugin activation go here    
    //register uninstaller
    register_uninstall_hook(__FILE__, 'mp_helloworld_uninstall');

function mp_helloworld_uninstall(){
    //actions to perform once on plugin uninstall go here     

Keep in mind that only one alternative will work. If uninstall.php exists, it will be performed and any uninstall hook will not be fired. Before moving further, you have to keep some important points in your mind.

It is advisable to develop plug-in with debugging. One can find huge information about the WP debugging mode and different plug-ins as well to get additional notifications. It becomes your relievable assistants to make error-free and up-to-date code.

Following WordPress Coding Standards helps to make maintain the consistency of the core code within your plugin. For common task, you can use APIs, core functions, and classes. Different types of tools for commonly required operations are provided by WordPress to developers so they can deliberate on the truly unique functionality of your plug-in.

Now, we are going to know the concept of hooks, actions, and filters. Let’s know about hooks in details, it is specially marked place in the code, where one can deliberately registered hooked into functions to define registration. There are two different types of hooks in WP, i.e. Action hook that makes the place to execute an action and Filter hook that marks the place to apply a modification to a value so the code will utilize the adjusted value.

Let’s move to the details and see the common logic of the WordPress actions. You have to mark the place, where the custom code should go with an action hook and its parameters. Now, you have to create the action function that performs the new code using parameters offered by the hook.

Register the action (#2) to be executed when the hook (#1) is fired with some main concern. At the time when the requested page is loaded by WordPress and finds the hooks, then it will search for all functions hooked into it and carry out them one by one appropriate to their priority. One can have to perform do_action function to perform task #1:

do_action($tag, $arg_1, $arg_2, ... , $arg_n);

Following parameters like tag – the hook name, $arg_1, $arg_2, … , $arg_n, etc. are accepted. WordPress itself using a lot of predefined hooks like:

do_action( 'init' );

Being a very straightforward case, one shouldn’t have to have to add extra parameters. When the most of WordPress is set up, this hook is fired and at that time, custom objects, like custom post type, etc. should be registered.

do_action('save_post', $post_id, $post);

When post is saved, the hook is fired and provides two additional parameters to operate with post_id and post object containing all the data from the saved post. However developing hooks is not a privilege of the core team. Every developers have to create a custom hook for the plug-in or theme.

All thanks to this as it provides more power like theme frameworks allow child themes to change the style and also the markup of parents without overwriting whole files.

do_action( 'my_truly_custom_hook' );

One can have to register the latest for the execution with add_action when he/she create a proper hook and created a custom function for it.

add_action($tag, $function_to_add, $priority, $accepted_args_number);

The two obligatory parameters are accepted by the add_action method, i.e. $tag: the name of the proper hook and $function_to_add: the name of the function that should be performed. In this case, the two parameters are optional: $priority: an integer to specify for registering the functions (by default, 10), $accepted_args_number: number of arguments that the registered function is going to accept (by default, 1). Look at the example for better understanding. We have use the wp_footer hook as it is a part of obligatory footer code that every theme should include.

function mp_helloworld_footer_notice(){        
      echo "<div id='mp-helloworld-notice'>Hello, I'm your custom notice</div>";
add_action('wp_footer', 'mp_helloworld_footer_notice');

With this example, you will understand the prefixed function that that simply outputs the notice’s markup and after that, hooked it into the wp_footer. Once you will add this code in your plug-in file, you will see the result on the site.

custom notice

Working with Filters

In this step, you have to filters operated with same logic as actions. However, you just don’t have to perform some piece of coding in a certain place. Execution of code is done just TO MODIFY some value given to them by the hook. That simply means you will find every filter hook connected with the associated value.

This value should be taken by the function that executes this value and after that, it returns it for further usage. We have seen that the syntax of functions responsible for filters and hooks are bit different.

apply_filters($tag, $value_to_filter, $arg_1, $arg_2, ... , $arg_n);

A filter hook with $tag name and the obligatory parameter $value_to_filter is created by the function apply_filter. Other arguments are discretionary and work the same ways as for actions.

filter_function($value_to_filter, $arg_1, $arg_2, ... , $arg_n){
       //filtering code goes here
       return $value_to_filter; //value has to be returned back

According to the skeleton of filter function, it should be accept at least one argument, the value for modification; and return the value at the end.

add_filter($tag, $function_to_add, $priority, $accepted_args);

A function with a name given as the $function_to_add argument for the $tag filter hook is registered by add_filter. We have seen the optional arguments like $accepted_args and $priority work in the same ways as for action hooks.

Let’s show the whole procedure in action: a common plug-in is to add some content at the end of a post. One can find below given filter hook, if we look closer at the the_content template tag that used to output a post’s content in a theme

$content = apply_filters('the_content', $content);
With this hook, one can add something at the end of the post in the following way: 
function mp_helloworld_post_footer($content) {         
        $content .= "<div class='mp-helloworld-post-footer'><p>Hello, I'm your custom post footer</p></div>";
        return $content;
add_filter('the_content', 'mp_helloworld_post_footer', 100);

We are using quite large number for priority that makes sure us to all default filters have been applied before our mp_helloworld_post_footer. One should see below given result on the site after including the code in the plugin’s file.

Post Footer

How to Find Hooks

One should need to know that what hooks are available as it is obvious to implement action and filter functionality. An Action Reference is provided by the WordPress Codex with most of action hooks fired on typical page load and a Filter Reference with a list of commonly used filters. With the help of these references, one can simply understand the order of actions and logic of filters so one can able to select where and when functionality can and should be injected.

Then, you are ready for the trip into the source code and just execute a search through the WordPress files for the do_action and apply_filters keywords in order to search the needed hook. Having information about WordPress query logic helps you work out where some hooks can be searched for. At last, one can have to refer the WordPress Hooks Database that has full information about the hooks in the core files.

Advanced Operations with Hooks

Actions and filters can also be removed with a similar syntax no doubt it has being added to your plug-in. If you want to remove action, you have to follow below given manner.

remove_action($tag, $function_to_remove, $priority, $accepted_args);
remove_all_actions($tag, $priority);

A particular action registered for a particular hook is removed by remove_action and with remove_all_actions, one can remove all actions registered with a certain hook with a given precedence. One can have heard about a popular security recommendation that helps to hide the WordPress version from the head section of the site. It is a job for remove_action. Firstly, you have to search out the hooks the wp_generator function to print the version information by browsing default filters. Below given thing can be done by code like this:

add_action('wp_head', 'wp_generator');

You have to add the opposite function in your plug-in to eliminate the effect of this code.

remove_action('wp_head', 'wp_generator');

In same way, Filters can be removed.

remove_filter($tag, $function_to_remove, $priority, $accepted_args);
remove_all_filters($tag, $priority);

Developers will get the Plugin API with a way to detect whether the particular hook has registered functions to perform:

has_action($tag, $function_to_check);
has_filter($tag, $function_to_check);

With both of the function, one can check whether a particular action or filter is registered for a hook and returns: false on failure and true on success. One can have to check ability inside the hooked function in the following way:

if('hook_to_check_name' === current_filter()){
    //do stuff related to 'hook_to_check_name' hook

The current_filter works with filters and actions as well. It is advisable to refer Codex for the full set of Plugin API functions.

Real-world Case

Now, let’s open plug-in skeleton that you have created. Now, we are going to add the core.php file with the code that helps to solve a task with actions and filters. In this step, you have to create a custom taxonomy that handles the guest author’s name and short author’s bio. So, any author can provide you a guest article after registering at your site. Moreover, this also ensures you provide credit to the actual author.

For this, you have to assign author’s names as any other taxonomy’s terms to the post. Now, it becomes possible to get an result from an box right after the post’s text. See the code below.

/** Hook plugin's action and filters **/
function mp_helloworld_init(){
    add_action('init', 'mp_helloworld_taxonomies');
    add_filter('the_content', 'mp_helloworld_author_block_filter');
    add_filter('post_class', 'mp_helloworld_post_class');
add_action('plugins_loaded', 'mp_helloworld_init');

/** Register custom taxonomy **/
function mp_helloworld_taxonomies(){
    $args = array(
        'labels' => array(
            'name'          => 'Guest authors',
            'singular_name' => 'Guest author'
        'show_in_nav_menus' => false        
    register_taxonomy('gauthor', array('post'), $args);

/** Create author's box markup **/
function mp_helloworld_author_block(){
    global $post;
    $author_terms = wp_get_object_terms($post->ID, 'gauthor');
    $name = stripslashes($author_terms[0]->name);
    $url = esc_url(get_term_link($author_terms[0]));
    $desc = wp_filter_post_kses($author_terms[0]->description);
    $out = "<div class='gauthor-info'>";
    $out .= "<h5>This is a guest post by <a href='{$url}'>{$name}</a></h5>";
    $out .= "<div class='gauthor-desc'>{$desc}</div></div>";

    return $out;

/** Add author's box to the end of the post **/
function mp_helloworld_author_block_filter($content){
        $content .= mp_helloworld_author_block();
    return $content;

/** Add custom CSS class to the post's container **/
function mp_helloworld_post_class($post_class){
    global $post;
    $author_terms = wp_get_object_terms($post->ID, 'gauthor');
        $post_class[] = 'gauthor';
    return $post_class;

It is recommended to apply an action to register a custom taxonomy to the init hook. So in next step, you can create the template tag, which is responsible for the author box’s markup using native WordPress functions such as wp_get_object_terms.

Now, you have to attach this box at the end of post content by using the_content filter hook. Lastly, we have to add the custom CSS class for the guest posts’ container that makes flexibility in the style of the theme. After applying some style, you will see outcome like:

Guest Author

Options API

The Options API is one of the consistent ways of storing data in the database. Under a given custom name, all the data is saved in the wp_options table and can be accessed through from somewhere in the code. Below given is the most essential function of the API.

<?php get_option('opting_name'); ?>
<?php update_option('option_name', $new_value); ?>

One can find the get_option function from the database and you can have any information stored under a given name and returns it. An option name is taken by the function update_option and its value and updates the matching entry in the database. It you don’t find any entry then it will make automatically. One can operate both the function with arrays as well as single values. That simply means, you can store array of data under a single name in the database and serialization and mineralization actions will be handled by the API on behalf of you. It is recommended to store plug-ins as an array under a single name.

Plugin Options’ Page

Now, you have to create a settings page or group of pages in the admin menu for your plugin. For creating a group of pages, you have to add a top level page first:

<?php add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $output_function, $icon_url, $position ); ?>

We find the parameter values are self-explanatory, but it can be referred to the source for details. Now, one can have to add internal pages one by one in the following manner:

<?php add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function ); ?>

One can have to use the ID of the top level page as a $parent_slug parameter. Moreover, in the case of custom top level, you have provided the value as $menu_slug upon registration. One can create a single page under one of existing top-level sections, if he/she didn’t need several pages. For that you have to use Settings options-general.php should be used as the $parent_slug. On other side, you can add shortcut functions for adding subpages under certain admin menu items it’s add_options_page in Setting option.

Settings API

An interface is created in the Settings API that allows managing the plugin’s settings. You have to mark a page as a settings page in order to process input automatically and also output sections on the page and fields inside each section for accepting user input. Now, you have to develop section-fields structure to attain that the first goal is to register settings with the system.

<?php register_setting($option_group, $option_name, $sanitise_callback); ?>
<?php add_settings_section( $id, $title, $callback, $page ); ?>
<?php add_settings_field( $id, $title, $callback, $page, $section, $args ); ?>

It is advisable to refer the Codex for a detailed description of the parameters; however the logic is bit simpler. Firstly, you have to register options name and then, have to register section(s) with set of fields for each section and an internal ID. Through API, you can have ability to specify custom callbacks for input validation and showing each field and section.

Once, you have to complete registration of options and corresponding fields then you have to display them on the settings page with below given function that called inside the <form> tag:

<?php settings_fields($option_group); ?>
<?php do_settings_sections($page); ?>

With the settings_fields function, you can obligatory hidden fields for the native WordPress alternatives mechanism to work. Actually, the do_settings_sections outputs previously registered sections and fields.

Security Considerations

One can only have to clean the input, escape the output and take care of capabilities when dealing with fundamental security rule. One can also have to escape to output special characters, particularly HTML when showing data extracted from the database. Native functions are provided by both the tasks by WordPress that can be used in different contexts. For more information, you can visit here.

The users’ permissions are another point of concern. WordPress has integral mechanism controlling users’ roles and capabilities that blocks access to particular admin areas for users with limited permissions. Only admin can access anything he/she wants to. At the time of developing options pages, one can have to allocate correct capabilities, generally, it is manage_options. Additionally, it doesn’t allow users with low privileges to access the page. If you want to know more information about WordPress roles and capabilities, refer Codex.

Put it All into Work

Now, we are going to see the whole scenario in action. We are going to create Hello World example that shows information about guest author under a post through custom taxonomy. Previously, we found hard code into the plug-in by author’s block markup. But now, we are providing an ability to users to specify a template for that markup using placeholders for author-specific data (name, url and description). We have two includable PHP files, i.e. core.php (containing the main code) and admin.php (containing admin-related code). What changes we need to create?

Create a plugin options page (in admin.php)
/* register menu item */
function mp_helloworld_admin_menu_setup(){
 'Helloworld Settings',
add_action('admin_menu', 'mp_helloworld_admin_menu_setup'); //menu setup

/* display page content */
function mp_helloworld_admin_page_screen() {
 global $submenu;
// access page settings 
 $page_data = array();
 foreach($submenu['options-general.php'] as $i => $menu_item) {
 if($submenu['options-general.php'][$i][2] == 'mp_helloworld')
 $page_data = $submenu['options-general.php'][$i];

// output 
<div class="wrap">
<?php screen_icon();?>
<h2><?php echo $page_data[3];?></h2>
<form id="mp_helloworld_options" action="options.php" method="post">
submit_button('Save options', 'primary', 'mp_helloworld_options_submit');
/* settings link in plugin management screen */
function mp_helloworld_settings_link($actions, $file) {
if(false !== strpos($file, 'mp-helloworld'))
 $actions['settings'] = '<a href="options-general.php?page=mp_helloworld">Settings</a>';
return $actions;
add_filter('plugin_action_links', 'mp_helloworld_settings_link', 2, 2);

In the next step, you have to create a subpage under the Settings menu in the snippet mp_helloworld_admin_menu_setup. After that, one can have to output the settings form with mp_helloworld_admin_page_screen. The Settings API functions are used by it for fields and for other elements, pre-built WordPress functions are to be used.

It is noted that the action attribute of the <form > tag should point to options.php to process options suitably. Lastly, a shortcut link to the options page on the plugin management screen is created by mp_helloworld_settings_link filter.

page link highlighted

Register plugin options with the system and create fields and rules for them
/* register settings */
function mp_helloworld_settings_init(){


 'Author\'s box', 


add_action('admin_init', 'mp_helloworld_settings_init');

/* validate input */
function mp_helloworld_options_validate($input){
 global $allowedposttags, $allowedrichhtml;
 $input['authorbox_template'] = wp_kses_post($input['authorbox_template']);
return $input;

/* description text */
function mp_helloworld_authorbox_desc(){
echo "<p>Enter the template markup for author box using placeholders: [gauthor_name], [gauthor_url], [gauthor_desc] for name, URL and description of author correspondingly.</p>";

/* filed output */
function mp_helloworld_authorbox_field() {
 $options = get_option('mp_helloworld_options');
 $authorbox = (isset($options['authorbox_template'])) ? $options['authorbox_template'] : '';
 $authorbox = esc_textarea($authorbox); //sanitise output
 <textarea id="authorbox_template" name="mp_helloworld_options[authorbox_template]" cols="50" rows="5" class="large-text code">
 <?php echo $authorbox;?>

It should be pointed out the all plug-in should be stored as an array. Regardless of you only have one option (authorbox_template) that we have to add in an array and the corresponding field into the section for display purposes. The ‘admin_init’ hook should execute the registration function mp_helloworld_settings_init. Moreover, mp_helloworld_options_validate function pay attention of user input by cleaning it with the native wp_kses_post filter that relies on the KSES library.

A description for the form’s section and mp_helloworld_authorbox_field outputs a textarea is created by mp_helloworld_authorbox_desc function to handle inputted markup. It is noted that the CSS classes large-text code is assigned to built-in admin styling is applied. All such create below given screen in the WordPress admin panel.

option form

Modify the function that outputs author’s box (in core.php)

We are doing so as to get the template from the database and replaces placeholder data ([gauthor_name], [gauthor_desc]), [gauthor_url], with the corresponding values.

/* Create author's box markup */
function mp_helloworld_author_block(){
 global $post;

$author_terms = wp_get_object_terms($post->ID, 'gauthor');

$name = stripslashes($author_terms[0]->name);
 $url = esc_url(get_term_link($author_terms[0]));
 $desc = wp_filter_post_kses($author_terms[0]->description);

//get template from option
 $options = get_option('mp_helloworld_options');
 $out = (isset($options['authorbox_template'])) ? $options['authorbox_template'] : '';

$out = str_replace(
 array('[gauthor_url]', '[gauthor_name]', '[gauthor_desc]'),
 array($url, $name, $desc),

return $out;

Lastly, you will see plugin that creates a nice guest author’s box under post content.

Guest Author Box

Wrapping Text

One can have to implement loads of plug-ins to store and access option data. However, with this option, you can deliver the ability to tune the plug-in according to your user’s needs. Hope this guide will be useful for you. If you have any questions through development task, let us know through comments.

Picture: Bjarne Winkler/Flickr


  1. excellent post, everything is collected in one place, many thanks

  2. In a parallel world, you are posting tutorials about blogspot in a wordpress blog, hehe.
    Btw, nice post :D

    1. Blogger provides features free for which wordpress want to be get paid ;)

      And Thanks to read my blog post.

  3. Hello I'm Richard and I have created a Wordpress plugin for SEO. This plugin helps SEO's in all points. They can handle all SEO properties from this one plugin. You can track your leads, visits, goals and conversion etc. I wanted to know through your blog "How can I promote or sell my plugins in offshore development filed"?

  4. Great post and seriously, you shared a great collection of codes and related information that will help out several coders to create their first plug-in just like me. I am completely influenced by the details of your post. Thanks and hearty congrats for such a successful blog post.


Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted.

If you want to link your website with your comment than choose "Comment as: Name/URL" option with respect of above notice.

Let's have a personal and meaningful conversation instead. Thanks for dropping by!