Monday, 26 Oct 2026

Exclusive resources and tools for web design and development experts

Explore
BiblioWeb

Biblioweb.es

All About Web Technology

  • Start
  • WordPress
    WordPress
    • All about WordPress26
    • Plugins de WordPress16
    • Desarrollo web y plugins15
    Rendimiento y seguridad
    • Hosting y rendimiento8
    • Ciberseguridad web7
    • Solución de errores5
    Negocio e IA
    • IA y automatización10
    • WooCommerce y tiendas online6
    Lo último
    Restaurant Menu Widget — WordPress Plugin27 Sep 2026JetStudio Bookings — WordPress Plugin27 Sep 2026SC Heatmap — WordPress Plugin27 Sep 2026
    Ver el blog completoHistorial de noticiasAbout usNewsletter semanal
  • Plugins de WordPress
    Fichas de plugins
    • WP Translate Master
    • Cart Tool Kit
    • SC Heatmap
    • JetStudio Bookings
    • Restaurant Menu Widget
    • WP SEO Pro
    • Rank Math SEO in 2026
    • WP Rocket
    • WPCode
    • AI Engine para WordPress
    Comparativas y guías
    • The best SEO plugins for WordPress
    • The best AI plugins for WordPress
    • The best booking plugins for WordPress
    • WordPress multiidioma
    • Cómo crear un formulario de contacto en WordPress
    • Stripe o PayPal
    Por temática
    • Plugins de WordPress16
    • WooCommerce y tiendas online6
    • Desarrollo web y plugins15
    • IA y automatización10
    • Hosting y rendimiento8
    BibliotecaPlugin Library

    Los plugins de WordPress que desarrollamos: qué hace cada uno, cómo se ve en acción y dónde conseguirlo.

    Entrar en la Pluginteca
    Toda la categoría Plugins de WordPressHerramientas gratis
  • Tools
    Herramientas gratis, sin registro y desde el navegador
    Generador de contraseñasContraseñas largas y aleatorias sin salir del navegador.Generador de QRCódigos QR en PNG de alta resolución, sin caducidad.Contador de palabrasPalabras, caracteres y tiempo de lectura en vivo.JSON FormatterFormatea, valida y localiza el error exacto.WebP ConverterPNG, JPG y PDF a WebP para que la web cargue…PDF a WebPCada página del PDF, en una imagen ligera.Comprobador de emailsSintaxis, correos temporales y registros MX.Compresor de vídeoBaja el peso del vídeo sin subirlo a ningún servidor.Vídeo a MP3Extrae el audio de un vídeo en un par de clics.Firmas de emailFirma en HTML lista para Gmail y Outlook.
    También te sirveHerramientas externas que merecen la pena

    DNS Checker, Query Monitor, Local WP y el resto del kit que usamos a diario, con para qué sirve cada una.

    Ver la selección
    Todas las herramientasPlugin LibraryRecíbelas por correo
  • Plugin Library
  • Español
  • English ✓
  • 🇨🇳 中文(简体)
  • Français
  • Deutsch
  • Italiano
Newsletter
  • WordPress
  • Plugins de WordPress
  • WooCommerce
  • IA para WordPress
  • Security
  • Herramientas gratis
  • See everything
BiblioWebBiblioWeb
Font ResizerAa
  • WordPress
  • Plugins
  • WooCommerce
  • Errores
  • Tools
Search
  • Start
  • WordPress
    • All about WordPress
    • WordPress Plugins
    • Web Development and Plugins
    • AI and Web Automation
    • WooCommerce
    • Hosting and Web Performance
    • Cybersecurity and Web Security
    • Web Error Resolution
  • Plugins de WordPress
    • Rank Math SEO
    • AI Engine
    • WP Rocket
    • WPCode
    • WP Translate Master
    • Cart Tool Kit
    • SC Heatmap
    • JetStudio Bookings
    • Restaurant Menu Widget
    • WP SEO Pro
  • Tools
    • Password Generator
    • QR Code Generator
    • Word Counter
    • JSON Formatter
    • Image to WebP Converter
    • Email Checker
    • Video Compressor for Web
    • Email Signature Generator
    • Video to MP3 Converter
    • PDF to WebP Converter
  • Plugin Library
  • Blog
    • Español
    • English ✓
    • 🇨🇳 中文(简体)
    • Français
    • Deutsch
    • Italiano
Síguenos
© 2026 BiblioWeb — All rights reserved.
Cover » Blog » How to create a WordPress plugin from scratch
Web Development and Plugins

How to create a WordPress plugin from scratch

Adrián Alcalá
Actualizado: 28/09/2026 12:43
By Adrián Alcalá
Share
17 min de lectura
How to create a WordPress plugin from scratch
SHARE

Learn to create a WordPress plugin is the step that separates those who use the platform from those who truly master it. A plugin is nothing more than a set of PHP files that WordPress loads alongside the core and can modify or extend almost any system behavior: add functions, change texts, create content types, or connect the website with external services. The good news is that the entry barrier is much lower than it seems: with one file, a comment header, and basic knowledge of hooks, you already have a functional plugin. In this guide, we will build one from scratch, step by step and with complete code that you can copy, test, and extend. We will cover the recommended file structure, how actions and filters work, how to add a settings page, and what good security practices you should apply from day one to make your code robust and maintainable.

Índice
Why create a WordPress plugin and not touch the themeWhat you need before startingThe minimum structure: folder, file, and headerHooks: the heart of any pluginThe complete plugin: main class with settings and outputBest practices that make a differenceHow to test and debug your pluginNext steps: grow with orderFrequently Asked QuestionsConclusion

Why create a WordPress plugin and not touch the theme

Before writing a single line, it's good to understand where each type of code should live in WordPress. The general rule is simple: the theme controls how your website looks, and plugins control what your website does. If you add functionality to the file functions.php of your theme, that functionality will disappear as soon as you change themes or, even worse, as soon as the theme updates and overwrites your changes.

A plugin, on the other hand, is independent of the design. You can activate it, deactivate it, move it to another installation, or publish it in the official directory. These are the typical situations where the correct answer is create a WordPress plugin custom:

  • You need a specific function (a shortcode, a notice, an integration) that doesn't justify installing a huge commercial plugin.
  • You want the functionality to survive a theme change.
  • You are going to reuse the same code on several client websites.
  • An existing plugin does almost what you want, but not exactly, and you prefer to control the code.

For very small snippets, there's an intermediate alternative: snippet managers. If you only need to paste ten lines of PHP, you might be interested in reading our analysis of WPCode, the WordPress snippet manager. But as soon as the code grows or needs organization, a custom plugin wins by a landslide.

What you need before starting

The toolkit is minimal. No paid software or complicated environment is needed:

  • A test WordPress installation. Never develop directly in production. A local environment with Local WP, XAMPP, or similar is ideal.
  • A code editor. Visual Studio Code is the most popular and free option.
  • File access. Locally you have it direct; on a server, via SFTP or the hosting's file manager.
  • Basic PHP concepts. Variables, functions, and arrays are enough to start.

It's also advisable to activate debug mode while developing. Add this to your wp-config.php of the testing environment:

You Might Also Like

How to install and configure Yoast SEO in WordPress
What is a child theme in WordPress and how to create it
Custom Post Types in WordPress: complete guide
The WordPress REST API: what it is and how to use it
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

With this configuration, errors and notices are saved in wp-content/debug.log instead of being displayed on screen, which will allow you to catch problems without breaking the browsing experience.

The minimum structure: folder, file, and header

A plugin can be a single PHP file inside wp-content/plugins, but the recommended practice is to create its own folder from the beginning. For our example, we will build a real and useful plugin: a customizable notice at the top of the website, with an included settings page. The structure will be this:

wp-content/plugins/bw-aviso-superior/
├── bw-aviso-superior.php   (archivo principal)
├── includes/
│   └── class-bw-aviso.php  (lógica del plugin)
└── assets/
    └── css/
        └── aviso.css       (estilos del aviso)

The only essential thing for WordPress to recognize the plugin is the plugin header: a comment block at the beginning of the main file. Create bw-aviso-superior.php with this content:

<?php
/**
 * Plugin Name:       BW Aviso Superior
 * Plugin URI:        https://biblioweb.es/
 * Description:       Muestra una barra de aviso personalizable en la parte superior de la web.
 * Version:           1.0.0
 * Requires at least: 6.0
 * Requires PHP:      7.4
 * Author:            BiblioWeb
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       bw-aviso-superior
 */

// Seguridad: impedir el acceso directo al archivo.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

define( 'BW_AVISO_VERSION', '1.0.0' );
define( 'BW_AVISO_PATH', plugin_dir_path( __FILE__ ) );
define( 'BW_AVISO_URL', plugin_dir_url( __FILE__ ) );

require_once BW_AVISO_PATH . 'includes/class-bw-aviso.php';

// Arrancar el plugin cuando WordPress haya cargado los plugins.
add_action( 'plugins_loaded', array( 'BW_Aviso', 'init' ) );

With just this file (and the class we'll see now), the plugin will already appear in the list of Plugins of the dashboard, ready to be activated. Note the check for ABSPATH: prevents someone from directly executing the file by typing its URL, a basic security measure that should open all your PHP files.

Hooks: the heart of any plugin

WordPress is built on a system of hooks (hooks) that allows your code to attach to specific moments of execution. Without hooks, there is no plugin: they are the official mechanism to intervene without modifying the core. There are two types:

Actions: do something at a given moment

A action executes your function when an event occurs: WordPress finishes loading, a post is published, the footer is rendered. They are used with add_action():

add_action( 'wp_footer', 'bw_mensaje_en_footer' );
function bw_mensaje_en_footer() {
    echo '<!-- Generado por BW Aviso Superior -->';
}

Filters: modify data before it is used

A filter receives a value, transforms it, and returns it. WordPress uses it for everything: a post's title, content, excerpt length. They are used with add_filter():

add_filter( 'excerpt_length', 'bw_extracto_corto' );
function bw_extracto_corto( $length ) {
    return 25; // palabras del extracto
}

The key difference: an action does things and returns nothing; a filter always must return the value (modified or not). Forgetting the return in a filter is one of the most common mistakes when starting and can leave empty texts all over the website.

The complete plugin: main class with settings and output

Now let's move on to the central piece. Create includes/class-bw-aviso.php with the class that registers the settings, renders the options page, and displays the notice on the public side:

<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class BW_Aviso {

    const OPTION = 'bw_aviso_opciones';

    public static function init() {
        $instancia = new self();

        // Parte pública.
        add_action( 'wp_body_open', array( $instancia, 'mostrar_aviso' ) );
        add_action( 'wp_enqueue_scripts', array( $instancia, 'cargar_estilos' ) );

        // Administración.
        add_action( 'admin_menu', array( $instancia, 'registrar_pagina_ajustes' ) );
        add_action( 'admin_init', array( $instancia, 'registrar_ajustes' ) );
    }

    public function mostrar_aviso() {
        $opciones = get_option( self::OPTION );
        if ( empty( $opciones['activo'] ) || empty( $opciones['texto'] ) ) {
            return;
        }
        printf(
            '<div class="bw-aviso-superior">%s</div>',
            esc_html( $opciones['texto'] )
        );
    }

    public function cargar_estilos() {
        $opciones = get_option( self::OPTION );
        if ( empty( $opciones['activo'] ) ) {
            return;
        }
        wp_enqueue_style(
            'bw-aviso-superior',
            BW_AVISO_URL . 'assets/css/aviso.css',
            array(),
            BW_AVISO_VERSION
        );
    }

    public function registrar_pagina_ajustes() {
        add_options_page(
            'Aviso Superior',
            'Aviso Superior',
            'manage_options',
            'bw-aviso-superior',
            array( $this, 'render_pagina_ajustes' )
        );
    }

    public function registrar_ajustes() {
        register_setting(
            'bw_aviso_grupo',
            self::OPTION,
            array( 'sanitize_callback' => array( $this, 'sanear_opciones' ) )
        );
    }

    public function sanear_opciones( $entrada ) {
        return array(
            'activo' => ! empty( $entrada['activo'] ) ? 1 : 0,
            'texto'  => isset( $entrada['texto'] )
                ? sanitize_text_field( $entrada['texto'] )
                : '',
        );
    }

    public function render_pagina_ajustes() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }
        $opciones = get_option( self::OPTION, array( 'activo' => 0, 'texto' => '' ) );
        ?>
        <div class="wrap">
            <h1>Aviso Superior</h1>
            <form method="post" action="options.php">
                <?php settings_fields( 'bw_aviso_grupo' ); ?>
                <table class="form-table">
                    <tr>
                        <th scope="row">Mostrar aviso</th>
                        <td>
                            <label>
                                <input type="checkbox"
                                    name="<?php echo esc_attr( self::OPTION ); ?>[activo]"
                                    value="1" <?php checked( 1, $opciones['activo'] ); ?> />
                                Activar la barra de aviso
                            </label>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row">Texto del aviso</th>
                        <td>
                            <input type="text" class="regular-text"
                                name="<?php echo esc_attr( self::OPTION ); ?>[texto]"
                                value="<?php echo esc_attr( $opciones['texto'] ); ?>" />
                        </td>
                    </tr>
                </table>
                <?php submit_button(); ?>
            </form>
        </div>
        <?php
    }
}

And finally the styles, in assets/css/aviso.css:

.bw-aviso-superior {
    background: #1d2327;
    color: #ffffff;
    text-align: center;
    padding: 10px 16px;
    font-size: 15px;
}

Activate the plugin, go to Settings → Top Notice, check the box, type some text, and save. The bar will appear at the top of your website. You have now achieved create a WordPress plugin complete: with a settings page, options saved in the database, custom styles, and output on the public side.

Best practices that make a difference

The previous example already applies several rules that you should internalize. Let's review them along with others equally important:

Sanitize input and escape output

All incoming data (forms, URL, APIs) is sanitized with functions like sanitize_text_field(), and all data displayed on screen is escaped with esc_html(), esc_attr() o esc_url(). This pair of habits prevents the vast majority of XSS vulnerabilities. If you are interested in the defensive approach, we have a guide with 7 tricks to strengthen your WordPress security which complements very well what you do at the code level.

Unique prefixes everywhere

Functions, classes, options, and style handles must have their own prefix (in our case bw_ y BW_). PHP does not allow two functions with the same name: if your plugin declares enviar_email() and another plugin also, the website will crash with a fatal error.

Check capabilities and use nonces

Before displaying or operating on administration pages, verify permissions with current_user_can(). In custom forms, add nonces with wp_nonce_field() and verify them when processing. The settings API we use (settings_fields()) already manages the nonce for you, another reason to prefer it over processing forms manually.

Load resources only when needed

Our CSS is only enqueued if the notice is active. Apply the same criterion always: a plugin that loads scripts on all pages unnecessarily penalizes the performance of the entire website. In fact, many of the speed problems attributed to WordPress are actually poorly written plugins; in the guide on how to speed up WordPress with PHP settings you can see the real impact of these decisions.

How to test and debug your plugin

With WP_DEBUG activated, your workflow will be: save the file, reload the page, and check wp-content/debug.log if something doesn't work. Some additional tips:

  1. Test activation and deactivation. Activate and deactivate the plugin several times, checking that it doesn't throw warnings.
  2. Test with other active plugins. Conflicts between plugins are the main source of real incidents.
  3. Test with different users. Log in with an editor or subscriber and confirm that they don't see the settings page.
  4. Use error_log() as a cheat sheet. Writing error_log( print_r( $variable, true ) ); at a point in the code shows you the content of any variable in the log.

If your plugin is going to handle structured data (for example, API responses in JSON), a tool like the BiblioWeb JSON formatter and validator will save you time when inspecting and validating those responses during development.

Next steps: grow with order

From this base, you can extend the plugin in many directions: add a color picker for the bar, schedule start and end dates for the notice, create a shortcode, or expose options in the REST API. When the project grows, maintain structural discipline: the logic in includes/, the resources in assets/, and the main file only as an entry point.

The essential reference for in-depth study is the Official WordPress Plugin Handbook, which documents everything from available hooks to the directory publication process. And if you want to see how the big players solve things, nothing beats reading the code of established plugins: the ones we review in the 5 essential WordPress plugins for beginners are a good starting point for studying structure and style.

Frequently Asked Questions

Do I need to know a lot of PHP to create a WordPress plugin?

Not to start. With variables, functions, arrays, and conditionals, you can build useful plugins like the one in this guide. The development itself will naturally and progressively lead you to more advanced concepts (classes, namespaces, APIs).

Where are plugins stored in WordPress?

In the folder wp-content/plugins of your installation. Each plugin occupies its own subfolder (or a single PHP file in very simple cases). WordPress automatically detects any file with a valid plugin header within that path.

Can I break my website when developing a plugin?

A PHP syntax error in an active plugin can bring down the website, which is why development is always done in a testing environment. If it happens in production, simply renaming the plugin folder via SFTP is enough to deactivate it and instantly restore the site.

How do I publish my plugin in the official directory?

You must comply with the directory guidelines (GPL license, secure code, no obfuscation), prepare a file readme.txt and submit it for review from wordpress.org. After approval, you receive access to an SVN repository from which versions are distributed.

Conclusion

Creating a WordPress plugin is not exclusive territory for veteran programmers: it's a folder, a header, and a handful of well-chosen hooks. In this guide, you've built a complete one, with a settings page, data sanitization, conditional styles, and the security best practices used by professional plugins. The leap in quality compared to pasting code into the theme is enormous: your functionality is now portable, updatable, and isolated from the design. The best final advice is simple: choose a small, real problem on your own website and solve it with a plugin. There's no better school than maintaining your own code running on a real site.

Share This Article
Email Copiar enlace Print
ByAdrián Alcalá
Síguele:
Adrián Alcalá es desarrollador WordPress y consultor SEO, autor del plugin WP Translate y creador de BiblioWeb. Desde aquí comparte guías, tutoriales y herramientas gratuitas para quienes crean, optimizan y posicionan páginas web. Lleva años ayudando a negocios de toda España a sacar el máximo partido a WordPress, y todo lo que publica está probado en proyectos reales.
Previous Article What is Elementor and how to use it to design pages What is Elementor and how to use it: a getting started guide
Next Article WordPress backup: complete guide How to make a WordPress backup
Ilustración del plugin Restaurant Menu Widget: carta de restaurante con platos y precios editables en Elementor
Restaurant Menu Widget — WordPress Plugin
WordPress Plugins
Ilustración del plugin JetStudio Bookings: formulario de citas por pasos con calendario y bandeja de solicitudes
JetStudio Bookings — WordPress Plugin
WordPress Plugins
Ilustración del plugin SC Heatmap: mapa de calor de clics y scroll sobre una web, con los datos en tu servidor
SC Heatmap — WordPress Plugin
WordPress Plugins
Accesibilidad web en WordPress con IA
Accesibilidad web en WordPress: auditarla con IA en una tarde
AI & Web Automation

También te puede interesar

WordPress without coding: no-code
Web Development and Plugins

WordPress without coding: what you can build with no-code

28/09/2026
WordPress Shortcodes: what they are and how to create them
Web Development and Plugins

WordPress Shortcodes: what they are and how to create your own

08/08/2026
WordPress Plugins - Don't start without them The 5 Essential WordPress Plugins every beginner needs for a top-notch website
Web Development and Plugins

The 5 essential WordPress plugins for beginners

28/09/2026
Design WordPress Website - Design your PRO Website in WordPress: The Perfect Theme for a Brutal Impact (and AI tricks that will blow your mind
Web Development and Plugins

How to choose the perfect theme to design your website in WordPress

28/09/2026
Show More
  • Temas:
  • WordPress
  • WordPress Plugins
  • Web Development and Plugins
  • AI & Web Automation
  • Web Hosting & Performance
  • Cybersecurity and Web Security
  • WooCommerce
  • Troubleshooting web errors
BiblioWeb

Biblioweb.es

Web Technology News

Information you can trust: practical web technology guides and solutions — WordPress, plugins, cybersecurity, hosting, performance, and error troubleshooting.

RSS

Enlaces de interés

  • About us
  • Contact
  • Free tools
  • Plugin Library

Recibe nuestras nuevas guías de WordPress en tu correo. Suscríbete a la newsletter.

Legal

  • Legal Notice
  • Terms and Conditions
  • Cookies Policy
  • Privacy Policy

Últimas novedades

Ilustración del plugin Restaurant Menu Widget: carta de restaurante con platos y precios editables en Elementor

Restaurant Menu Widget — WordPress Plugin

27/09/2026
Continue reading
Ilustración del plugin JetStudio Bookings: formulario de citas por pasos con calendario y bandeja de solicitudes

JetStudio Bookings — WordPress Plugin

27/09/2026
Continue reading

© 2026 BiblioWeb — All rights reserved.

Gestionar consentimiento
Utilizamos tecnologías como las cookies para almacenar y/o acceder a la información del dispositivo. Lo hacemos para mejorar la experiencia de navegación y para mostrar anuncios (no) personalizados. El consentimiento a estas tecnologías nos permitirá procesar datos como el comportamiento de navegación o los ID's únicos en este sitio. No consentir o retirar el consentimiento, puede afectar negativamente a ciertas características y funciones.
Funcional Always active
El almacenamiento o acceso técnico es estrictamente necesario para el propósito legítimo de permitir el uso de un servicio específico explícitamente solicitado por el abonado o usuario, o con el único propósito de llevar a cabo la transmisión de una comunicación a través de una red de comunicaciones electrónicas.
Preferences
El almacenamiento o acceso técnico es necesario para la finalidad legítima de almacenar preferencias no solicitadas por el abonado o usuario.
Estadísticas
El almacenamiento o acceso técnico que es utilizado exclusivamente con fines estadísticos. El almacenamiento o acceso técnico que se utiliza exclusivamente con fines estadísticos anónimos. Sin un requerimiento, el cumplimiento voluntario por parte de tu proveedor de servicios de Internet, o los registros adicionales de un tercero, la información almacenada o recuperada sólo para este propósito no se puede utilizar para identificarte.
Marketing
El almacenamiento o acceso técnico es necesario para crear perfiles de usuario para enviar publicidad, o para rastrear al usuario en una web o en varias web con fines de marketing similares.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Ver preferencias
  • {title}
  • {title}
  • {title}
Accede a BiblioWeb
Usuario o correo electrónico
Contraseña

¿Has olvidado tu contraseña?