WordPress plugin development opens endless possibilities for extending WordPress functionality. This beginner-friendly guide teaches you to build your first plugin from scratch, even if you’ve never written PHP code before. You’ll learn fundamental concepts, best practices, and create a functioning plugin by the end.
What Are WordPress Plugins?
Plugins are PHP files that extend WordPress core functionality without modifying core files. They add features, modify behavior, and integrate services. WordPress’s extensive plugin API provides hooks and functions that let you tap into WordPress at precise moments during execution.
Understanding this principle is crucial: never modify WordPress core. Always build plugins (or themes) to add functionality. This keeps your changes upgrade-safe and maintainable.
Prerequisites and Setup
Before starting, ensure you have:
- Basic PHP knowledge – Understand variables, functions, arrays
- HTML/CSS basics – Know how to structure and style content
- Local development environment – LocalWP, MAMP, or Docker
- Code editor – VS Code, PhpStorm, or Sublime Text
Install LocalWP for the easiest local WordPress setup. It handles server configuration, making development straightforward for beginners.
Your First Plugin File
Create a new folder in wp-content/plugins/ named my-first-plugin. Inside, create my-first-plugin.php:
<?php
/**
* Plugin Name: My First Plugin
* Plugin URI: https://example.com/my-first-plugin
* Description: A simple plugin to learn WordPress development
* Version: 1.0.0
* Author: Your Name
* Author URI: https://example.com
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: my-first-plugin
* Domain Path: /languages
*/
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}The header comment tells WordPress about your plugin. Required fields are Plugin Name and Description. The security check prevents direct file access.
Navigate to Plugins in your WordPress admin. You’ll see “My First Plugin” listed. Click Activate.
Understanding WordPress Hooks
Hooks are WordPress’s event system. They allow your plugin to execute code at specific moments.
Actions let you run code at specific points:
function dprt_welcome_message() {
echo '<div class="notice notice-info"><p>Welcome to our site!</p></div>';
}
add_action( 'admin_notices', 'dprt_welcome_message' );This displays a message in the WordPress admin when the admin_notices action fires.
Filters let you modify data:
function dprt_modify_excerpt_length( $length ) {
return 30;
}
add_filter( 'excerpt_length', 'dprt_modify_excerpt_length' );This changes excerpt length to 30 words by filtering the default value.
Common Hooks for Beginners
Start with these essential hooks:
init – Fires after WordPress finishes loading. Use it for registering post types, taxonomies, and initialization:
function dprt_init_plugin() {
// Register custom post types, taxonomies, etc.
}
add_action( 'init', 'dprt_init_plugin' );wp_enqueue_scripts – Load CSS and JavaScript for frontend:
function dprt_enqueue_assets() {
wp_enqueue_style(
'dprt-styles',
plugin_dir_url( __FILE__ ) . 'assets/css/style.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'dprt-scripts',
plugin_dir_url( __FILE__ ) . 'assets/js/script.js',
array( 'jquery' ),
'1.0.0',
true
);
}
add_action( 'wp_enqueue_scripts', 'dprt_enqueue_assets' );admin_menu – Add admin menu pages:
function dprt_add_menu_page() {
add_menu_page(
'My Plugin Settings', // Page title
'My Plugin', // Menu title
'manage_options', // Capability required
'my-plugin', // Menu slug
'dprt_settings_page', // Function to display page
'dashicons-admin-generic', // Icon
20 // Position
);
}
add_action( 'admin_menu', 'dprt_add_menu_page' );
function dprt_settings_page() {
echo '<div class="wrap">';
echo '<h1>My Plugin Settings</h1>';
echo '<p>Settings page content goes here.</p>';
echo '</div>';
}Plugin Activation and Deactivation
Run code when plugins activate or deactivate:
function dprt_activate_plugin() {
// Set default options
add_option( 'dprt_welcome_message', 'Welcome to our site!' );
// Create custom database tables if needed
// Flush rewrite rules if registering custom post types
flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'dprt_activate_plugin' );
function dprt_deactivate_plugin() {
// Clean up temporary data
// Flush rewrite rules
flush_rewrite_rules();
// Don't delete user data on deactivation
// Save that for uninstall
}
register_deactivation_hook( __FILE__, 'dprt_deactivate_plugin' );Never delete user data on deactivation. Users might temporarily deactivate to troubleshoot. Save deletion for uninstall.
The Options API
Store plugin settings using the Options API:
// Save option
update_option( 'dprt_setting_name', 'value' );
// Get option
$setting = get_option( 'dprt_setting_name', 'default value' );
// Delete option
delete_option( 'dprt_setting_name' );Options are stored in the wp_options table. Use them for plugin configuration, user preferences, and cached data.
Plugin Security Basics
Security is paramount. Follow these practices from day one:
Sanitize input – Clean data users provide:
$user_input = sanitize_text_field( $_POST['input_field'] );
$email = sanitize_email( $_POST['email_field'] );Escape output – Prevent XSS attacks:
echo '<p>' . esc_html( $user_data ) . '</p>';
echo '<a href="' . esc_url( $link ) . '">Link</a>';Use nonces – Verify form submissions:
// Create nonce
wp_nonce_field( 'dprt_save_settings', 'dprt_settings_nonce' );
// Verify nonce
if ( ! wp_verify_nonce( $_POST['dprt_settings_nonce'], 'dprt_save_settings' ) ) {
wp_die( 'Security check failed' );
}Check capabilities – Ensure users have permission:
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Unauthorized access' );
}Organizing Plugin Files
As plugins grow, organize code across multiple files:
my-first-plugin/
├── my-first-plugin.php (Main file)
├── includes/
│ ├── class-admin.php
│ ├── class-frontend.php
│ └── functions.php
├── assets/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── script.js
└── languages/
Include files in your main plugin file:
require_once plugin_dir_path( __FILE__ ) . 'includes/functions.php';
require_once plugin_dir_path( __FILE__ ) . 'includes/class-admin.php';Debugging Your Plugin
Enable WordPress debugging in wp-config.php:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );Log debug messages:
error_log( 'Debug message: ' . print_r( $variable, true ) );Check /wp-content/debug.log for logged errors.
Building a Complete Example Plugin
Let’s build a simple quote display plugin:
<?php
/**
* Plugin Name: Simple Quote Display
* Description: Display random quotes on your site
* Version: 1.0.0
*/
if ( ! defined( 'WPINC' ) ) {
die;
}
// Add admin menu
function sqd_add_menu() {
add_menu_page(
'Quotes',
'Quotes',
'manage_options',
'sqd-quotes',
'sqd_quotes_page',
'dashicons-format-quote'
);
}
add_action( 'admin_menu', 'sqd_add_menu' );
// Admin page
function sqd_quotes_page() {
if ( isset( $_POST['sqd_add_quote'] ) ) {
if ( ! wp_verify_nonce( $_POST['sqd_nonce'], 'sqd_add_quote' ) ) {
wp_die( 'Security check failed' );
}
$quote = sanitize_textarea_field( $_POST['quote'] );
$quotes = get_option( 'sqd_quotes', array() );
$quotes[] = $quote;
update_option( 'sqd_quotes', $quotes );
}
$quotes = get_option( 'sqd_quotes', array() );
?>
<div class="wrap">
<h1>Manage Quotes</h1>
<form method="post">
<?php wp_nonce_field( 'sqd_add_quote', 'sqd_nonce' ); ?>
<textarea name="quote" rows="3" cols="50"></textarea>
<input type="submit" name="sqd_add_quote" value="Add Quote" class="button button-primary">
</form>
<h2>Existing Quotes</h2>
<ul>
<?php foreach ( $quotes as $quote ) : ?>
<li><?php echo esc_html( $quote ); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php
}
// Shortcode to display random quote
function sqd_display_quote() {
$quotes = get_option( 'sqd_quotes', array() );
if ( empty( $quotes ) ) {
return '';
}
$random_quote = $quotes[ array_rand( $quotes ) ];
return '<blockquote class="sqd-quote">' . esc_html( $random_quote ) . '</blockquote>';
}
add_shortcode( 'random_quote', 'sqd_display_quote' );This complete plugin adds quotes via admin, stores them, and displays them with [random_quote] shortcode.
Common Beginner Mistakes
Avoid these pitfalls:
- Not using prefixes – Prefix all functions to avoid conflicts
- Forgetting security – Always sanitize, escape, and verify
- Ignoring WordPress functions – Use WordPress APIs instead of raw PHP/SQL
- Not testing – Test across WordPress versions and with other plugins
- Poor error handling – Anticipate failures and handle gracefully
Next Steps
After mastering basics:
- Study the Plugin Handbook thoroughly
- Explore the Settings API for complex settings pages
- Learn custom post types and taxonomies
- Understand the REST API for modern integrations
- Study established plugins’ code on GitHub
Conclusion
WordPress plugin development is accessible to beginners willing to learn. Start with simple plugins, follow security best practices, use WordPress APIs, and gradually build complexity. Your first plugin might be basic, but it establishes the foundation for building sophisticated WordPress extensions.
External Links
- WordPress Plugin Handbook
- Plugin Basics
- Local by Flywheel
- WordPress Coding Standards
- WordPress Developer Resources
Call to Action
Supercharge your development! ACF Copilot Pro generates ACF field groups with AI, exports to PHP, and accelerates custom field workflows—try it free!

