Home>Blog>How to Build a Custom WordPress Plugin from Scratch
WordPress
7 min read

How to Build a Custom WordPress Plugin from Scratch

September 12, 2026
·By Haris Maqsood

    What Is a Custom WordPress Plugin?

    A WordPress plugin is a package of PHP code that extends the functionality of a WordPress site without touching core files. Instead of modifying wp-includes or your theme directly, plugins hook into WordPress at defined points using the action and filter system.

    When an off-the-shelf plugin does not do exactly what you need, you build a custom one. I build custom plugins regularly for clients who need API integrations, custom post type management tools, admin dashboards, and WooCommerce extensions.

    Setting Up Your Plugin File

    Every WordPress plugin starts with a PHP file containing a specific header comment block. This tells WordPress the plugin exists and what it is called.

    Create a folder in /wp-content/plugins/your-plugin-name/ and inside it create your-plugin-name.php:

    php
    <?php
    /**
     * Plugin Name:       My Custom Plugin
     * Plugin URI:        https://yoursite.com/
     * Description:       A custom plugin that does something specific.
     * Version:           1.0.0
     * Author:            Haris Maqsood
     * Author URI:        https://harismaqsood.com
     * License:           GPL v2 or later
     * Text Domain:       my-custom-plugin
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
        exit; // Prevent direct access
    }

    The ABSPATH check is critical for security. It prevents the file from being executed directly via URL.

    Using WordPress Hooks: Actions and Filters

    The WordPress hook system is how your plugin communicates with core. There are two types: actions and filters.

    Actions let you run code at specific points. Filters let you modify data before it is used.

    php
    // Action: Run code when WordPress initializes
    add_action( 'init', 'mcp_register_custom_post_type' );
    
    function mcp_register_custom_post_type() {
        register_post_type( 'project', [
            'label'    => 'Projects',
            'public'   => true,
            'supports' => [ 'title', 'editor', 'thumbnail' ],
        ] );
    }
    
    // Filter: Modify the page title
    add_filter( 'the_title', 'mcp_modify_title', 10, 2 );
    
    function mcp_modify_title( $title, $post_id ) {
        if ( get_post_type( $post_id ) === 'project' ) {
            return 'Project: ' . $title;
        }
        return $title;
    }

    Always prefix your function names with something unique to your plugin. This prevents collisions with other plugins or themes.

    Adding an Admin Settings Page

    Most plugins need a settings page so non-technical users can configure behavior without touching code.

    php
    add_action( 'admin_menu', 'mcp_add_settings_page' );
    
    function mcp_add_settings_page() {
        add_options_page(
            'My Plugin Settings',
            'My Plugin',
            'manage_options',
            'my-custom-plugin',
            'mcp_render_settings_page'
        );
    }
    
    function mcp_render_settings_page() {
        if ( ! current_user_can( 'manage_options' ) ) return;
        ?>
        <div class="wrap">
            <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
            <form method="post" action="options.php">
                <?php
                settings_fields( 'mcp_settings_group' );
                do_settings_sections( 'my-custom-plugin' );
                submit_button();
                ?>
            </form>
        </div>
        <?php
    }

    Storing and Retrieving Options Safely

    WordPress has a built-in options API. Use get_option and update_option to store plugin settings. Always sanitize input on save and escape output when rendering.

    php
    // Save a value
    update_option( 'mcp_api_key', sanitize_text_field( $input ) );
    
    // Retrieve and output it safely
    $api_key = get_option( 'mcp_api_key', '' );
    echo esc_html( $api_key );

    Security Checklist for Every Plugin

    Every time I write a WordPress plugin, I check these four things before delivery:

    Nonce verification: Any form submission or AJAX request must include a nonce to prevent cross-site request forgery.

    php
    // Generate nonce in your form
    wp_nonce_field( 'mcp_save_action', 'mcp_nonce_field' );
    
    // Verify it on form processing
    if ( ! wp_verify_nonce( $_POST['mcp_nonce_field'], 'mcp_save_action' ) ) {
        wp_die( 'Security check failed.' );
    }

    Capability checks: Always verify the user has permission before performing sensitive actions.

    Sanitization: Never trust user input. Use sanitize_text_field, absint, wp_kses_post, or the appropriate sanitization function for your data type.

    Escaping output: Use esc_html, esc_url, esc_attr when outputting anything to the browser.

    Best Practices I Follow on Every Plugin

    After 5 years of building custom plugins for clients across the 20+ countries worldwide, a few habits have become non-negotiable for me:

    One plugin, one responsibility. Keep your plugin focused. If it starts growing beyond a single purpose, consider splitting it.

    Use classes and namespaces for anything beyond trivial functionality. This keeps code organized and reduces the risk of global namespace collisions.

    Register all assets properly using wp_enqueue_scripts and wp_enqueue_style. Never echo