| name | plugin-development |
| description | WordPress plugin development guidance — architecture, security, hooks, settings pages, AJAX, custom post types, shortcodes. Use when creating a plugin or adding plugin functionality. |
Skill: WordPress Plugin Development
Description
Expert guidance for creating WordPress plugins with proper architecture, security, and WordPress coding standards.
When to Use
- User asks to create a new plugin
- User needs to add custom functionality
- User wants to create admin pages
- User needs to integrate with WordPress hooks
- User wants to add custom post types or taxonomies
- User needs to create shortcodes or widgets
Verifying against the local environment
This repo runs WordPress through wp-env. To inspect or test live behavior:
npm start # boot the environment
npx wp-env run cli wp plugin list # any WP-CLI command
npx wp-env run cli wp shell # interactive PHP against the install
Key Concepts
Plugin Structure
my-plugin/
├── my-plugin.php # Main file with header
├── uninstall.php # Cleanup on uninstall
├── includes/ # PHP classes
│ ├── class-plugin.php
│ └── class-admin.php
├── admin/ # Admin assets
│ ├── css/
│ └── js/
├── public/ # Frontend assets
│ ├── css/
│ └── js/
└── languages/ # Translations
Required Plugin Header
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
Requires Plugins (WordPress 6.5+) declares plugin dependencies by wordpress.org slug —
WordPress blocks activation until they are installed and active. Omit the line when the
plugin has no dependencies. Code should still guard with class_exists() /
function_exists() and degrade usefully.
Plugin Constants
define( 'MY_PLUGIN_VERSION', '1.0.0' );
define( 'MY_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
define( 'MY_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
Common Tasks
1. Register Custom Post Type
add_action( 'init', function() {
register_post_type( 'book', array(
'labels' => array(
'name' => 'Books',
'singular_name' => 'Book',
),
'public' => true,
'show_in_rest' => true,
'supports' => array( 'title', 'editor', 'thumbnail' ),
'menu_icon' => 'dashicons-book',
'has_archive' => true,
) );
});
2. Add Admin Menu Page
add_action( 'admin_menu', function() {
add_menu_page(
'My Plugin Settings',
'My Plugin',
'manage_options',
'my-plugin',
'my_plugin_settings_page',
'dashicons-admin-generic',
30
);
});
function my_plugin_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( 'my_plugin_options' );
do_settings_sections( 'my-plugin' );
submit_button();
?>
</form>
</div>
<?php
}
3. Register Settings
add_action( 'admin_init', function() {
register_setting( 'my_plugin_options', 'my_plugin_setting', array(
'sanitize_callback' => 'sanitize_text_field',
) );
add_settings_section( 'general', 'General Settings', null, 'my-plugin' );
add_settings_field( 'api_key', 'API Key', function() {
$value = get_option( 'my_plugin_setting' );
echo '<input type="text" name="my_plugin_setting" value="' . esc_attr( $value ) . '" class="regular-text">';
}, 'my-plugin', 'general' );
});
4. Create Shortcode
add_shortcode( 'my_shortcode', function( $atts ) {
$atts = shortcode_atts( array(
'id' => 0,
'class' => '',
), $atts );
ob_start();
return ob_get_clean();
});
5. Add AJAX Handler
add_action( 'wp_ajax_my_action', 'my_ajax_handler' );
add_action( 'wp_ajax_nopriv_my_action', 'my_ajax_handler' );
function my_ajax_handler() {
check_ajax_referer( 'my_nonce', 'nonce' );
$data = sanitize_text_field( $_POST['data'] );
wp_send_json_success( array( 'result' => $data ) );
}
wp_localize_script( 'my-script', 'myPlugin', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'my_nonce' ),
));
6. Activation/Deactivation
register_activation_hook( __FILE__, function() {
add_option( 'my_plugin_version', '1.0.0' );
flush_rewrite_rules();
});
register_deactivation_hook( __FILE__, function() {
flush_rewrite_rules();
});
7. Uninstall (uninstall.php)
<?php
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) exit;
delete_option( 'my_plugin_setting' );
Security Checklist
Best Practices