10 Simple WordPress Plugin Ideas You Can Build This Weekend
You’ve learned the basics of WordPress plugin development, and now you’re thinking, “Okay, what should I actually build?”
I’ve been there. You know the theory, you understand hooks and filters, but you’re stuck staring at an empty code editor wondering where to start.
That’s exactly why I put together this list. These are 10 plugin ideas that are:
- Simple enough to build in a weekend (or even a few hours)
- Practical — you’ll actually use them or learn something valuable
- Great learning exercises — each one teaches you different aspects of WordPress development
I’ve personally built variations of all these plugins at some point. Some were for clients, some were just for learning, and a few are still running on my own sites.
Let’s dive in!
Why Building Plugins Is the Best Way to Learn
Before we get to the ideas, let me quickly explain why building plugins (even simple ones) is the best way to learn WordPress development.
You learn by doing, not reading. You can read about add_action() a hundred times, but until you use it to solve a real problem, it won’t click.
Each plugin solves one problem. This focused approach prevents overwhelm. You’re not trying to build the next WooCommerce — you’re building something small and achievable.
You build a portfolio. Even simple plugins demonstrate your skills to potential clients or employers.
You start to understand WordPress internals. The more plugins you build, the more you understand how WordPress actually works under the hood.
Okay, enough philosophy. Let’s build something.
1. Custom Login Logo
What it does: Replaces the default WordPress logo on the login page with your own custom logo.
Why build it: This is probably the simplest plugin you can build, and almost every client wants this. It’s a perfect first project.
Difficulty Level: ⭐ (Very Easy)
Key hooks/functions:
login_enqueue_scripts(action hook to add custom CSS)login_headerurl(filter to change the logo link)login_headertext(filter to change the logo title attribute)
Implementation outline:
<?php
/**
* Plugin Name: Custom Login Logo
* Description: Replace the WordPress login logo with your own
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function custom_login_logo() {
$logo_url = plugin_dir_url( __FILE__ ) . 'logo.png';
?>
<style type="text/css">
#login h1 a, .login h1 a {
background-image: url(<?php echo esc_url( $logo_url ); ?>);
height: 80px;
width: 320px;
background-size: contain;
background-repeat: no-repeat;
padding-bottom: 30px;
}
</style>
<?php
}
add_action( 'login_enqueue_scripts', 'custom_login_logo' );
function custom_login_logo_url() {
return home_url();
}
add_filter( 'login_headerurl', 'custom_login_logo_url' );
function custom_login_logo_url_title() {
return get_bloginfo( 'name' );
}
add_filter( 'login_headertext', 'custom_login_logo_url_title' );
Extension ideas:
- Add a settings page to upload the logo through the admin panel
- Customize the entire login page design (background, colors, button styles)
- Add custom CSS option for even more control
2. Disable Comments Completely
What it does: Turns off comments across your entire WordPress site — useful for portfolios, business sites, or any site that doesn’t need comments.
Why build it: Teaches you about WordPress post types, admin menus, and how to remove WordPress default features.
Difficulty Level: ⭐⭐ (Easy)
Key hooks/functions:
comments_open(filter to disable comment status)pings_open(filter to disable pingbacks)comments_array(filter to hide existing comments)admin_menu(action to remove comments from admin menu)
Implementation outline:
<?php
/**
* Plugin Name: Disable Comments
* Description: Completely disable comments across the entire site
* Version: 1.0.0
* Author: Taufik Hidayat
*/
// Disable comments on the frontend
function disable_comments_status() {
return false;
}
add_filter( 'comments_open', 'disable_comments_status', 20, 2 );
add_filter( 'pings_open', 'disable_comments_status', 20, 2 );
// Hide existing comments
function disable_comments_hide_existing( $comments ) {
return array();
}
add_filter( 'comments_array', 'disable_comments_hide_existing', 10, 2 );
// Remove comments page from admin menu
function disable_comments_admin_menu() {
remove_menu_page( 'edit-comments.php' );
}
add_action( 'admin_menu', 'disable_comments_admin_menu' );
// Remove comments from admin bar
function disable_comments_admin_bar() {
global $wp_admin_bar;
$wp_admin_bar->remove_menu( 'comments' );
}
add_action( 'wp_before_admin_bar_render', 'disable_comments_admin_bar' );
// Remove comments from dashboard
function disable_comments_dashboard() {
remove_meta_box( 'dashboard_recent_comments', 'dashboard', 'normal' );
}
add_action( 'admin_init', 'disable_comments_dashboard' );
// Remove comments from post types
function disable_comments_post_types_support() {
$post_types = get_post_types();
foreach ( $post_types as $post_type ) {
if ( post_type_supports( $post_type, 'comments' ) ) {
remove_post_type_support( $post_type, 'comments' );
remove_post_type_support( $post_type, 'trackbacks' );
}
}
}
add_action( 'admin_init', 'disable_comments_post_types_support' );
What you’ll learn:
- How to modify WordPress default behavior
- Working with filters and actions
- Manipulating the admin interface
Extension ideas:
- Add an option to disable comments only on specific post types
- Create a settings page to toggle comments on/off without deactivating the plugin
- Add an option to delete all existing comments
3. Reading Time Display
What it does: Calculates and displays estimated reading time for blog posts (e.g., “5 min read”).
Why build it: Great for learning content filters and working with post data. Plus, it’s genuinely useful for content-heavy sites.
Difficulty Level: ⭐⭐ (Easy)
Key hooks/functions:
the_content(filter to add reading time to post content)get_post_field()(to retrieve post content)str_word_count()(PHP function to count words)
Implementation outline:
<?php
/**
* Plugin Name: Reading Time Display
* Description: Display estimated reading time for posts
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function calculate_reading_time( $post_id = null ) {
$post = get_post( $post_id );
if ( ! $post ) {
return 0;
}
// Get the content
$content = $post->post_content;
// Strip shortcodes and HTML tags
$content = strip_shortcodes( $content );
$content = wp_strip_all_tags( $content );
// Count words
$word_count = str_word_count( $content );
// Average reading speed: 200 words per minute
$reading_time = ceil( $word_count / 200 );
return $reading_time;
}
function display_reading_time( $content ) {
// Only on single posts
if ( ! is_single() ) {
return $content;
}
$reading_time = calculate_reading_time();
if ( $reading_time > 0 ) {
$reading_time_html = sprintf(
'<div class="reading-time" style="margin-bottom: 20px; color: #666; font-size: 14px;">
<span>📖 %d min read</span>
</div>',
$reading_time
);
$content = $reading_time_html . $content;
}
return $content;
}
add_filter( 'the_content', 'display_reading_time' );
What you’ll learn:
- Content filtering
- Working with post data
- PHP string manipulation
Extension ideas:
- Add a shortcode so users can place reading time anywhere
- Make reading speed adjustable via settings
- Show reading time in post listings/archives
- Add visual reading progress bar
4. Custom Dashboard Widget
What it does: Adds a custom widget to the WordPress admin dashboard — great for showing quick stats, notes, or custom information.
Why build it: Teaches you how to modify the WordPress admin dashboard, which is useful for client projects.
Difficulty Level: ⭐⭐ (Easy)
Key hooks/functions:
wp_dashboard_setup(action to add dashboard widgets)wp_add_dashboard_widget()(function to register widget)
Implementation outline:
<?php
/**
* Plugin Name: Custom Dashboard Widget
* Description: Add a custom widget to the WordPress dashboard
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function add_custom_dashboard_widget() {
wp_add_dashboard_widget(
'custom_dashboard_widget', // Widget ID
'Quick Site Stats', // Widget title
'custom_dashboard_widget_content' // Display function
);
}
add_action( 'wp_dashboard_setup', 'add_custom_dashboard_widget' );
function custom_dashboard_widget_content() {
// Get some basic stats
$post_count = wp_count_posts()->publish;
$page_count = wp_count_posts('page')->publish;
$comment_count = wp_count_comments()->approved;
$user_count = count_users();
?>
<div class="custom-dashboard-stats">
<style>
.stat-item {
display: inline-block;
width: 48%;
padding: 15px;
margin: 1%;
background: #f0f0f1;
border-radius: 4px;
text-align: center;
}
.stat-number {
font-size: 32px;
font-weight: bold;
color: #2271b1;
}
.stat-label {
font-size: 14px;
color: #666;
margin-top: 5px;
}
</style>
<div class="stat-item">
<div class="stat-number"><?php echo esc_html( $post_count ); ?></div>
<div class="stat-label">Published Posts</div>
</div>
<div class="stat-item">
<div class="stat-number"><?php echo esc_html( $page_count ); ?></div>
<div class="stat-label">Published Pages</div>
</div>
<div class="stat-item">
<div class="stat-number"><?php echo esc_html( $comment_count ); ?></div>
<div class="stat-label">Approved Comments</div>
</div>
<div class="stat-item">
<div class="stat-number"><?php echo esc_html( $user_count['total_users'] ); ?></div>
<div class="stat-label">Total Users</div>
</div>
</div>
<?php
}
What you’ll learn:
- Dashboard customization
- WordPress count functions
- Creating admin interfaces
Extension ideas:
- Show recent post drafts or scheduled posts
- Display Google Analytics data (with API integration)
- Add quick action buttons
- Show system status information
5. Maintenance Mode
What it does: Shows a “Coming Soon” or “Under Maintenance” page to visitors while you work on the site. Logged-in admins can still access everything.
Why build it: Teaches you about WordPress user roles, capabilities, and frontend template control.
Difficulty Level: ⭐⭐ (Easy)
Key hooks/functions:
template_redirect(action to intercept page loading)current_user_can()(function to check user permissions)wp_die()(function to stop execution and display message)
Implementation outline:
<?php
/**
* Plugin Name: Maintenance Mode
* Description: Show a maintenance page to visitors
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function maintenance_mode_check() {
// Only show maintenance page if option is enabled
$is_maintenance = get_option( 'maintenance_mode_enabled', false );
if ( ! $is_maintenance ) {
return;
}
// Allow admins to access the site
if ( current_user_can( 'manage_options' ) ) {
return;
}
// Show maintenance page to everyone else
$message = get_option( 'maintenance_mode_message', 'Website is under maintenance. Please check back soon!' );
wp_die(
'<div style="text-align: center; padding: 50px;">
<h1>Under Maintenance</h1>
<p>' . esc_html( $message ) . '</p>
</div>',
'Under Maintenance',
array( 'response' => 503 )
);
}
add_action( 'template_redirect', 'maintenance_mode_check' );
// Add admin menu
function maintenance_mode_menu() {
add_options_page(
'Maintenance Mode',
'Maintenance Mode',
'manage_options',
'maintenance-mode',
'maintenance_mode_settings_page'
);
}
add_action( 'admin_menu', 'maintenance_mode_menu' );
function maintenance_mode_settings_page() {
if ( isset( $_POST['maintenance_mode_save'] ) ) {
check_admin_referer( 'maintenance_mode_settings' );
$enabled = isset( $_POST['maintenance_mode_enabled'] ) ? 1 : 0;
$message = sanitize_textarea_field( $_POST['maintenance_mode_message'] );
update_option( 'maintenance_mode_enabled', $enabled );
update_option( 'maintenance_mode_message', $message );
echo '<div class="updated"><p>Settings saved!</p></div>';
}
$enabled = get_option( 'maintenance_mode_enabled', false );
$message = get_option( 'maintenance_mode_message', 'Website is under maintenance. Please check back soon!' );
?>
<div class="wrap">
<h1>Maintenance Mode Settings</h1>
<form method="post">
<?php wp_nonce_field( 'maintenance_mode_settings' ); ?>
<table class="form-table">
<tr>
<th scope="row">Enable Maintenance Mode</th>
<td>
<label>
<input type="checkbox" name="maintenance_mode_enabled" value="1" <?php checked( $enabled, 1 ); ?> />
Show maintenance page to visitors
</label>
</td>
</tr>
<tr>
<th scope="row">Message</th>
<td>
<textarea name="maintenance_mode_message" rows="5" class="large-text"><?php echo esc_textarea( $message ); ?></textarea>
</td>
</tr>
</table>
<?php submit_button( 'Save Settings', 'primary', 'maintenance_mode_save' ); ?>
</form>
</div>
<?php
}
What you’ll learn:
- User capability checking
- Template redirection
- Creating settings pages
- Security with nonces
Extension ideas:
- Custom maintenance page template with logo and styling
- Allow specific IP addresses to bypass maintenance mode
- Add countdown timer for scheduled launch
- Email notification when someone tries to access the site
6. Social Share Buttons
What it does: Adds social sharing buttons (Twitter, Facebook, LinkedIn) to your blog posts.
Why build it: Learn content manipulation, external links, and basic JavaScript integration.
Difficulty Level: ⭐⭐ (Easy to Medium)
Key hooks/functions:
the_content(filter to add buttons to content)get_permalink()(get post URL)get_the_title()(get post title)
Implementation outline:
<?php
/**
* Plugin Name: Simple Social Share
* Description: Add social sharing buttons to posts
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function add_social_share_buttons( $content ) {
// Only on single posts
if ( ! is_single() ) {
return $content;
}
$post_url = urlencode( get_permalink() );
$post_title = urlencode( get_the_title() );
$twitter_url = "https://twitter.com/intent/tweet?text={$post_title}&url={$post_url}";
$facebook_url = "https://www.facebook.com/sharer/sharer.php?u={$post_url}";
$linkedin_url = "https://www.linkedin.com/sharing/share-offsite/?url={$post_url}";
$buttons = '
<div class="social-share-buttons" style="margin: 30px 0; padding: 20px; background: #f9f9f9; border-radius: 5px;">
<p style="margin: 0 0 10px 0; font-weight: bold;">Share this post:</p>
<a href="' . esc_url( $twitter_url ) . '" target="_blank" rel="noopener" style="display: inline-block; margin-right: 10px; padding: 10px 20px; background: #1DA1F2; color: white; text-decoration: none; border-radius: 3px;">
Twitter
</a>
<a href="' . esc_url( $facebook_url ) . '" target="_blank" rel="noopener" style="display: inline-block; margin-right: 10px; padding: 10px 20px; background: #4267B2; color: white; text-decoration: none; border-radius: 3px;">
Facebook
</a>
<a href="' . esc_url( $linkedin_url ) . '" target="_blank" rel="noopener" style="display: inline-block; padding: 10px 20px; background: #0077B5; color: white; text-decoration: none; border-radius: 3px;">
LinkedIn
</a>
</div>
';
return $content . $buttons;
}
add_filter( 'the_content', 'add_social_share_buttons' );
Extension ideas:
- Add more social networks (Pinterest, Reddit, WhatsApp)
- Add share counts via APIs
- Allow users to choose button position (before/after content)
- Add click tracking
- Create shortcode for manual placement
7. Custom Footer Credits
What it does: Replace or customize the footer credits text on your site without editing theme files.
Why build it: Simple but practical, teaches you about WordPress filters and theme hooks.
Difficulty Level: ⭐ (Very Easy)
Key hooks/functions:
wp_footer(action hook)- Filter hooks vary by theme
Implementation outline:
<?php
/**
* Plugin Name: Custom Footer Credits
* Description: Customize footer credits without editing themes
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function custom_footer_credits() {
$year = date('Y');
$site_name = get_bloginfo('name');
$credits = get_option( 'custom_footer_text', "© {$year} {$site_name}. All rights reserved." );
?>
<style>
.custom-footer-credits {
text-align: center;
padding: 20px;
background: #f8f8f8;
margin-top: 50px;
}
</style>
<div class="custom-footer-credits">
<?php echo wp_kses_post( $credits ); ?>
</div>
<?php
}
add_action( 'wp_footer', 'custom_footer_credits', 99 );
// Settings page
function custom_footer_credits_menu() {
add_options_page(
'Footer Credits',
'Footer Credits',
'manage_options',
'footer-credits',
'custom_footer_credits_page'
);
}
add_action( 'admin_menu', 'custom_footer_credits_menu' );
function custom_footer_credits_page() {
if ( isset( $_POST['footer_credits_save'] ) ) {
check_admin_referer( 'footer_credits_settings' );
$credits = wp_kses_post( $_POST['footer_credits_text'] );
update_option( 'custom_footer_text', $credits );
echo '<div class="updated"><p>Footer credits updated!</p></div>';
}
$year = date('Y');
$site_name = get_bloginfo('name');
$credits = get_option( 'custom_footer_text', "© {$year} {$site_name}. All rights reserved." );
?>
<div class="wrap">
<h1>Footer Credits Settings</h1>
<form method="post">
<?php wp_nonce_field( 'footer_credits_settings' ); ?>
<table class="form-table">
<tr>
<th scope="row">Footer Text</th>
<td>
<textarea name="footer_credits_text" rows="3" class="large-text"><?php echo esc_textarea( $credits ); ?></textarea>
<p class="description">HTML is allowed. Use {year} for current year, {site} for site name.</p>
</td>
</tr>
</table>
<?php submit_button( 'Save Credits', 'primary', 'footer_credits_save' ); ?>
</form>
</div>
<?php
}
Extension ideas:
- Add template tags like
{year},{site},{author} - Allow multiple footer sections
- Add styling options
- Support for social media icons
8. Post View Counter
What it does: Tracks and displays how many times each post has been viewed.
Why build it: Introduces you to custom post meta, database operations, and analytics basics.
Difficulty Level: ⭐⭐⭐ (Medium)
Key hooks/functions:
wp_head(action to track views)get_post_meta()/update_post_meta()(store view count)the_content(filter to display view count)
Implementation outline:
<?php
/**
* Plugin Name: Post View Counter
* Description: Track and display post view counts
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function track_post_views() {
if ( ! is_single() ) {
return;
}
global $post;
// Don't track admin views
if ( current_user_can( 'manage_options' ) ) {
return;
}
// Get current views
$views = get_post_meta( $post->ID, 'post_views_count', true );
// If no views yet, start at 0
if ( empty( $views ) ) {
$views = 0;
}
// Increment and update
$views = (int)$views + 1;
update_post_meta( $post->ID, 'post_views_count', $views );
}
add_action( 'wp_head', 'track_post_views' );
function get_post_views( $post_id = null ) {
if ( ! $post_id ) {
global $post;
$post_id = $post->ID;
}
$views = get_post_meta( $post_id, 'post_views_count', true );
return $views ? $views : 0;
}
function display_post_views( $content ) {
if ( ! is_single() ) {
return $content;
}
$views = get_post_views();
$views_text = $views == 1 ? 'view' : 'views';
$views_html = sprintf(
'<div class="post-views" style="margin-bottom: 20px; color: #666; font-size: 14px;">
<span>👁️ %s %s</span>
</div>',
number_format( $views ),
$views_text
);
return $views_html . $content;
}
add_filter( 'the_content', 'display_post_views' );
// Add views column to posts list in admin
function add_views_column( $columns ) {
$columns['post_views'] = 'Views';
return $columns;
}
add_filter( 'manage_posts_columns', 'add_views_column' );
function display_views_column( $column, $post_id ) {
if ( $column === 'post_views' ) {
$views = get_post_views( $post_id );
echo number_format( $views );
}
}
add_action( 'manage_posts_custom_column', 'display_views_column', 10, 2 );
// Make the views column sortable
function make_views_column_sortable( $columns ) {
$columns['post_views'] = 'post_views';
return $columns;
}
add_filter( 'manage_edit-post_sortable_columns', 'make_views_column_sortable' );
What you’ll learn:
- Post meta (custom fields)
- Admin column customization
- Conditional logic
- Data formatting
Extension ideas:
- Add date range filtering (views this week/month)
- Create a “popular posts” widget
- Add charts/graphs in admin
- Export view data to CSV
- Track unique vs. total views
9. Auto Table of Contents
What it does: Automatically generates a table of contents based on headings (H2, H3) in your post content.
Why build it: Great for learning content parsing, DOM manipulation, and JavaScript integration.
Difficulty Level: ⭐⭐⭐ (Medium)
Key hooks/functions:
the_content(filter to parse and inject TOC)- DOM parsing with DOMDocument
- JavaScript for smooth scrolling
Implementation outline:
<?php
/**
* Plugin Name: Auto Table of Contents
* Description: Automatically generate table of contents for posts
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function generate_table_of_contents( $content ) {
// Only on single posts with minimum 3 headings
if ( ! is_single() ) {
return $content;
}
// Parse content for headings
preg_match_all( '/<h([2-3])>(.*?)<\/h[2-3]>/i', $content, $headings );
// Need at least 3 headings for TOC
if ( count( $headings[0] ) < 3 ) {
return $content;
}
$toc = '<div class="table-of-contents" style="background: #f9f9f9; padding: 20px; margin: 20px 0; border-left: 4px solid #0073aa;">';
$toc .= '<h3 style="margin-top: 0;">Table of Contents</h3>';
$toc .= '<ul style="margin: 0; padding-left: 20px;">';
$index = 0;
foreach ( $headings[0] as $heading ) {
$level = $headings[1][$index];
$text = strip_tags( $headings[2][$index] );
$id = sanitize_title( $text );
// Add ID to heading in content
$content = str_replace(
$heading,
str_replace( '>', ' id="' . $id . '">', $heading ),
$content
);
$indent = $level == 3 ? 'margin-left: 20px;' : '';
$toc .= '<li style="' . $indent . '"><a href="#' . $id . '">' . esc_html( $text ) . '</a></li>';
$index++;
}
$toc .= '</ul></div>';
// Insert TOC after first paragraph
$content = preg_replace( '/(<p>.*?<\/p>)/i', '$1' . $toc, $content, 1 );
return $content;
}
add_filter( 'the_content', 'generate_table_of_contents' );
// Add smooth scrolling
function toc_smooth_scroll() {
if ( ! is_single() ) {
return;
}
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
const links = document.querySelectorAll('.table-of-contents a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
});
});
});
</script>
<?php
}
add_action( 'wp_footer', 'toc_smooth_scroll' );
What you’ll learn:
- Regular expressions
- Content parsing
- JavaScript integration
- DOM manipulation
Extension ideas:
- Add toggle to expand/collapse TOC
- Sticky TOC sidebar
- Progress indicator
- Highlight current section while scrolling
- Customization options (which heading levels to include)
10. Simple Contact Form
What it does: Creates a basic contact form with spam protection (honeypot) that sends emails.
Why build it: Teaches form handling, security, email sending, and shortcodes.
Difficulty Level: ⭐⭐⭐ (Medium)
Key hooks/functions:
add_shortcode()(create [contact_form] shortcode)wp_mail()(send emails)wp_nonce_field()(security)- Form validation and sanitization
Implementation outline:
<?php
/**
* Plugin Name: Simple Contact Form
* Description: A basic contact form with spam protection
* Version: 1.0.0
* Author: Taufik Hidayat
*/
function simple_contact_form_shortcode() {
ob_start();
// Handle form submission
if ( isset( $_POST['scf_submit'] ) ) {
// Verify nonce
if ( ! wp_verify_nonce( $_POST['scf_nonce'], 'simple_contact_form' ) ) {
echo '<p style="color: red;">Security check failed.</p>';
return ob_get_clean();
}
// Honeypot check (anti-spam)
if ( ! empty( $_POST['scf_website'] ) ) {
echo '<p style="color: red;">Spam detected.</p>';
return ob_get_clean();
}
// Sanitize inputs
$name = sanitize_text_field( $_POST['scf_name'] );
$email = sanitize_email( $_POST['scf_email'] );
$message = sanitize_textarea_field( $_POST['scf_message'] );
// Validate
$errors = array();
if ( empty( $name ) ) {
$errors[] = 'Name is required.';
}
if ( empty( $email ) || ! is_email( $email ) ) {
$errors[] = 'Valid email is required.';
}
if ( empty( $message ) ) {
$errors[] = 'Message is required.';
}
if ( empty( $errors ) ) {
// Send email
$to = get_option( 'admin_email' );
$subject = 'New Contact Form Submission from ' . $name;
$body = "Name: $name\n";
$body .= "Email: $email\n\n";
$body .= "Message:\n$message";
$headers = array( 'Reply-To: ' . $email );
if ( wp_mail( $to, $subject, $body, $headers ) ) {
echo '<p style="color: green; padding: 15px; background: #e7f7e7; border: 1px solid #4caf50;">Thank you! Your message has been sent.</p>';
return ob_get_clean();
} else {
echo '<p style="color: red;">Failed to send message. Please try again.</p>';
}
} else {
foreach ( $errors as $error ) {
echo '<p style="color: red;">' . esc_html( $error ) . '</p>';
}
}
}
?>
<form method="post" class="simple-contact-form" style="max-width: 600px;">
<?php wp_nonce_field( 'simple_contact_form', 'scf_nonce' ); ?>
<!-- Honeypot field (hidden from users) -->
<div style="position: absolute; left: -5000px;">
<input type="text" name="scf_website" tabindex="-1" autocomplete="off">
</div>
<p>
<label for="scf_name">Name *</label><br>
<input type="text" id="scf_name" name="scf_name" required style="width: 100%; padding: 10px; border: 1px solid #ddd;">
</p>
<p>
<label for="scf_email">Email *</label><br>
<input type="email" id="scf_email" name="scf_email" required style="width: 100%; padding: 10px; border: 1px solid #ddd;">
</p>
<p>
<label for="scf_message">Message *</label><br>
<textarea id="scf_message" name="scf_message" required rows="6" style="width: 100%; padding: 10px; border: 1px solid #ddd;"></textarea>
</p>
<p>
<button type="submit" name="scf_submit" style="padding: 12px 30px; background: #0073aa; color: white; border: none; cursor: pointer;">Send Message</button>
</p>
</form>
<?php
return ob_get_clean();
}
add_shortcode( 'contact_form', 'simple_contact_form_shortcode' );
What you’ll learn:
- Form handling in WordPress
- Security (nonces, honeypots)
- Data validation and sanitization
- Email sending
- Shortcodes
- Output buffering
Extension ideas:
- Add file upload support
- Store submissions in database
- AJAX form submission (no page reload)
- ReCAPTCHA integration
- Custom email templates
- Auto-response to sender
Tips for Turning Ideas Into Real Projects
Now you have 10 plugin ideas. Here’s how to actually build them:
1. Start with the simplest one. Don’t jump to the contact form if you’ve never built a plugin before. Build the login logo changer first.
2. Set a time limit. Give yourself 2-4 hours. If you get stuck, Google it, ask ChatGPT, or check the WordPress documentation. Don’t give up.
3. Make it work, then make it better. Get a basic version working first. You can always add features later.
4. Test thoroughly. Try to break your own plugin. What happens if someone enters weird data? What if they’re using a different theme?
5. Actually use it. Install your plugin on a real site (even a personal one). There’s something motivating about seeing your code in production.
6. Share it. Put it on GitHub. Write a blog post about building it. Teaching others solidifies your own knowledge.
Final Thoughts
The plugins in this list are intentionally simple. You can build each one in a few hours. But here’s the secret: the skills you learn building these “simple” plugins are the exact same skills you need for complex ones.
It’s just more of the same patterns, repeated and combined in different ways.
So pick one. Any one. Open your code editor, create a new plugin folder, and start typing.
You’ll get stuck. That’s normal. Google the error message. Read the WordPress documentation. Ask to AI or try something different.
And when you see your plugin working — when you type a shortcode and your contact form appears, or when you see your custom dashboard widget displaying stats — you’ll feel that rush.
That’s the feeling that turned me from someone who “knew a bit of WordPress” into someone who builds custom solutions for clients.
Now go build something.
— Taufik Hidayat