Build WordPress plugins from scratch: actions and filters, shortcodes, settings pages, custom post types, meta boxes, AJAX, REST endpoints, custom tables, cron and blocks.
Lessons
Plugins add screens to the WordPress dashboard with add_menu_page() and add_submenu_page(), hooked to admin_menu, and each page is protected by a capability that decides which users can see it.
<?php
function gdc_books_admin_menu() {
add_menu_page(
esc_html__( 'GDC Books', 'gdc-books' ), // <title> of the page.
esc_html__( 'Books Library', 'gdc-books' ), // Menu label.
'manage_options', // Capability required.
'gdc-books', // Unique page slug.
'gdc_books_render_dashboard', // Function that prints the page.
'dashicons-book-alt', // Icon.
26 // Position in the menu.
);
add_submenu_page(
'gdc-books', // Parent slug.
esc_html__( 'Books Settings', 'gdc-books' ),
esc_html__( 'Settings', 'gdc-books' ),
'manage_options',
'gdc-books-settings',
'gdc_books_render_settings_page'
);
}
add_action( 'admin_menu', 'gdc_books_admin_menu' );<?php
function gdc_books_render_dashboard() {
// Belt and braces: the menu already checks, but direct URL access should too.
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have permission to view this page.', 'gdc-books' ) );
}
$count = wp_count_posts( 'gdc_book' );
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<p>
<?php
printf(
/* translators: %s: number of published books */
esc_html__( 'You have %s published books.', 'gdc-books' ),
'<strong>' . esc_html( number_format_i18n( $count->publish ) ) . '</strong>'
);
?>
</p>
<a class="button button-primary" href="<?php echo esc_url( admin_url( 'post-new.php?post_type=gdc_book' ) ); ?>">
<?php esc_html_e( 'Add a book', 'gdc-books' ); ?>
</a>
</div>
<?php
}Wrap every admin page in <div class="wrap"> and use WordPress's own classes — button, button-primary, widefat, form-table, notice — so your page looks native and stays consistent through WordPress design updates.
| Capability | Who has it by default | Use for |
|---|---|---|
manage_options | Administrators | Plugin settings |
edit_others_posts | Editors and above | Content management tools |
edit_posts | Contributors and above | Tools any writer may use |
read | Every logged-in user | Personal dashboards |
Tip: Check capabilities, never roles.current_user_can( 'administrator' )breaks the moment a site uses a custom role, whilecurrent_user_can( 'manage_options' )keeps working.
| Helper | Adds a page under |
|---|---|
add_options_page() | Settings |
add_management_page() | Tools |
add_theme_page() | Appearance |
add_submenu_page( 'edit.php?post_type=gdc_book', ... ) | Your custom post type's menu |
Small plugins should prefer these. A plugin with one settings screen does not deserve its own top-level menu item — users find a crowded admin menu exhausting.
<?php
function gdc_books_missing_isbn_notice() {
$screen = get_current_screen();
if ( ! $screen || 'edit-gdc_book' !== $screen->id ) {
return; // Only on the Books list screen.
}
?>
<div class="notice notice-warning is-dismissible">
<p><?php esc_html_e( 'Some books are missing an ISBN.', 'gdc-books' ); ?></p>
</div>
<?php
}
add_action( 'admin_notices', 'gdc_books_missing_isbn_notice' );wp_die() stop you.In accordance with Section 6 & Section 7 of the DPDP Act 2023, third-party analytics and marketing trackers are strictly gated behind your affirmative consent. You may withdraw consent at any time under Section 6(4).
Essential for session authentication (ci_session), Cross-Site Request Forgery security (csrf_cookie_name), and Cloudflare DDoS/bot prevention. Cannot be disabled.
Gated under DPDP Section 7. Allows Google Analytics 4 (Google Consent Mode v2) to measure aggregate, anonymized tool usage.
Enables browser local storage to preserve your theme selection (dark/light mode) and code editor layout between visits.
Used to display developer sponsorships and native tool partner notices.