WooCommerce plugin development means adding your own code to a store through a normal WordPress plugin. You never edit WooCommerce core. A small plugin can add a product field, a setting or a checkout rule.
I built the example plugin in this guide in October 2026 on WordPress 7.1.3 and WooCommerce 11.2.0, and tested each step on a local site. That includes the activation block, the order storage warning, the product field and the settings tab. The scaffold commands come from the official WooCommerce developer docs, and I did not run those.
You will build a plugin called DD Product Badge. It adds a text field to every product and shows that text above the product title. A new settings tab sets a default badge for products that have none.
What Is WooCommerce Plugin Development?
WooCommerce plugin development is writing a WordPress plugin that changes how a store works through WooCommerce hooks. It adds fields, settings, prices or checkout rules without editing WooCommerce core. Because it is a normal plugin, you can switch it off and update WooCommerce without losing your code.
Most plugins do one small job. They hook into a moment in the store, such as saving a product or showing a price, and run your code there. The WooCommerce docs describe extensions the same way: they change store behavior by injecting code through WordPress hooks.
Common uses include custom product fields, new shipping or payment logic, order rules and changes to how products display. When you develop WooCommerce features this way, each one is a few hooks plus a little data. A custom WooCommerce plugin should still do one job well, because a plugin that does five jobs is harder to test and to hand over.
Before you build, check whether a ready plugin already does the job. Building makes sense for a small, specific feature, while a common feature is cheaper to buy. For example, loyalty features are well covered, so compare the best WooCommerce points and rewards plugins before you write your own.
Sometimes a shortcode is enough. If you only need to show products in a page, read our guide to WooCommerce shortcodes first.

What You Need Before You Start
You need basic PHP, a local WordPress site with WooCommerce and a code editor. Debug logging and a staging copy catch errors before customers see them. Node, Docker and Composer are needed only if you choose the official scaffold later in this guide.
Set up these pieces first:
- A local site: Run WordPress and WooCommerce on your computer, never on the live store. I used WordPress Playground.
- A code editor: Any editor that highlights PHP works, such as VS Code.
- Debug logging: Turn on the three constants below in
wp-config.phpwhile you build.
define( 'WP_DEBUG', true ); define( 'WP_DEBUG_LOG', true ); define( 'WP_DEBUG_DISPLAY', false );
The WordPress debugging guide says WP_DEBUG_LOG saves errors to debug.log in the wp-content folder. Setting WP_DEBUG_DISPLAY to false keeps those errors off the page.
How to Create a WooCommerce Plugin: Step by Step
To create a WooCommerce plugin, make a folder in wp-content/plugins, add a main PHP file with a plugin header, then hook your code into WooCommerce. Test it on a copy of your store before you use it live. The steps below build one real plugin from start to finish.

Step 1: Create the Plugin Folder and Main File
Create a folder named dd-product-badge inside wp-content/plugins. Put one PHP file with the same name inside it. WordPress finds the plugin by reading the header of that file.
wp-content/plugins/dd-product-badge/ dd-product-badge.php
Use a short, unique folder name. A name that is too common can clash with another plugin. Keep the file name the same as the folder, so the plugin is easy to find in the code editor.
Step 2: Add the Plugin Header
The header is a comment block at the top of the main file. WordPress and WooCommerce read it to show your plugin in the list and to check its requirements. Here is the header from my test plugin:
<?php /** * Plugin Name: DD Product Badge * Description: Adds a badge text field to WooCommerce products and shows it above the product title. * Version: 1.0.0 * Requires at least: 6.5 * Requires PHP: 7.4 * Requires Plugins: woocommerce * WC requires at least: 9.0 * WC tested up to: 11.2 * Author: DevDiggers * License: GPL-2.0-or-later * Text Domain: dd-product-badge */ defined( 'ABSPATH' ) || exit;
The last line stops anyone from loading the file directly.
Set Requires at least to the oldest WordPress version you test on, and not the newest one. A number you never tested is a promise you cannot keep. These header fields matter most:
- Plugin Name: The name shown in the Plugins list.
- Requires at least: The lowest WordPress version your code supports.
- Requires PHP: The lowest PHP version your code supports.
- Requires Plugins: A list of plugin slugs that must be active first, as the WordPress header documentation explains.
- WC requires at least and WC tested up to: The WooCommerce versions you support and have tested.
- License: Use a GPL-compatible license if you plan to publish.
Step 3: Make WooCommerce a Requirement
The Requires Plugins header tells WordPress that your plugin needs WooCommerce. The WordPress header documentation says it takes a comma-separated list of WordPress.org slugs. It does not accept the folder/file.php form.
I tested what this does. Follow these steps to see it:
- Copy the plugin: Put the folder in
wp-content/pluginson a site without WooCommerce. - Open Plugins: Go to Plugins, then Installed Plugins.

- Read the message: The Activate link is greyed out, and a notice says the plugin cannot be activated because required plugins are missing or inactive.
The check works the other way too.
With WooCommerce active, its row on my site read “Required by: DD Product Badge”. The row said WooCommerce cannot be deactivated while another plugin needs it. That saves you from a fatal error on a site where someone switches WooCommerce off.
Step 4: Declare HPOS Compatibility
High-Performance Order Storage, or HPOS, keeps orders in their own database tables. According to the WooCommerce HPOS documentation, it is on by default for new stores from WooCommerce 8.2. Your plugin must say whether it works with it.
Add this code under the header. It runs before WooCommerce starts and declares compatibility:
add_action(
'before_woocommerce_init',
function () {
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
}
}
);
This is the pattern from the official HPOS recipe book. I also tested what happens without it.
On a copy of the plugin with this block removed, WooCommerce showed “1 Incompatible plugin detected (DD Product Badge)” under Settings, Advanced, Features. The High-performance order storage option was greyed out.

With the declaration in place, that warning did not appear. Only declare compatibility after you check that your code reads and writes orders through WooCommerce functions.
Step 5: Add a Field to the Product Screen
Two hooks add and save the field. The first prints the input in the General tab of the product editor. The second saves it when you click Update:
add_action(
'woocommerce_product_options_general_product_data',
function () {
woocommerce_wp_text_input(
array(
'id' => '_ddpb_badge',
'label' => __( 'Badge text', 'dd-product-badge' ),
'desc_tip' => true,
'description' => __( 'Short text shown above the product title.', 'dd-product-badge' ),
)
);
}
);
add_action(
'woocommerce_admin_process_product_object',
function ( $product ) {
$badge = isset( $_POST['_ddpb_badge'] ) ? sanitize_text_field( wp_unslash( $_POST['_ddpb_badge'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
$product->update_meta_data( '_ddpb_badge', $badge );
}
);
WooCommerce checks the nonce before this hook runs, which is why the code carries a note for the coding standards checker.
The key starts with an underscore on purpose. WordPress treats meta keys that start with an underscore as protected, so they stay out of the Custom Fields box on the edit screen. Now test the field:
- Activate the plugin: Go to Plugins and click Activate.
- Edit a product: Go to Products, then open any product.
- Find the field: Open the General tab under Product data. The Badge text field sits below the prices.

- Save it: Type a short text, such as New arrival, and click Update.
Step 6: Show the Badge on the Product Page
A third hook prints the badge on the single product page. It reads the product’s own value first, then falls back to a default option that you will create in the next step:
add_action(
'woocommerce_single_product_summary',
function () {
global $product;
if ( ! $product ) {
return;
}
$badge = $product->get_meta( '_ddpb_badge' );
if ( '' === $badge ) {
$badge = get_option( 'ddpb_default_text', '' );
}
if ( '' === $badge ) {
return;
}
printf( '<span class="ddpb-badge" style="display:inline-block;background:#0256ff;color:#fff;padding:4px 12px;border-radius:99px;font-size:.8rem">%s</span>', esc_html( $badge ) );
},
4
);
The number 4 is the hook priority. WooCommerce prints the product title at priority 5, so a lower number places the badge above it. The output goes through esc_html(), which turns special characters into safe text.
On the Storefront theme my badge appeared above the title. It also appeared on Twenty Twenty-Five with WooCommerce’s default product template.

Step 7: Add a Settings Tab
A settings tab lets store owners change defaults without code. Three hooks create the tab, print its fields and save them. A small function holds the field definitions:
add_filter(
'woocommerce_settings_tabs_array',
function ( $tabs ) {
$tabs['ddpb'] = __( 'Product Badge', 'dd-product-badge' );
return $tabs;
},
50
);
function ddpb_settings() {
return array(
array(
'title' => __( 'Product Badge', 'dd-product-badge' ),
'type' => 'title',
'id' => 'ddpb_section',
),
array(
'title' => __( 'Default badge text', 'dd-product-badge' ),
'desc' => __( 'Shown on products that have no badge text of their own.', 'dd-product-badge' ),
'id' => 'ddpb_default_text',
'type' => 'text',
'default' => '',
),
array(
'type' => 'sectionend',
'id' => 'ddpb_section',
),
);
}
add_action(
'woocommerce_settings_tabs_ddpb',
function () {
woocommerce_admin_fields( ddpb_settings() );
}
);
add_action(
'woocommerce_update_options_ddpb',
function () {
woocommerce_update_options( ddpb_settings() );
}
);
The tab key ddpb is also the suffix of the two action names, so they must match. After you activate the plugin, a Product Badge tab appears under WooCommerce, then Settings. I typed a default of Free gift wrap, saved it, and the value stayed in the field after the reload.

Step 8: Test, Debug and Use a Staging Site
Test every path before you ship. Run through the plugin on a staging site that copies your live store. Check these cases:
- Empty and filled fields: Save a product with and without badge text, and confirm the default shows when it is empty.
- Odd input: Paste HTML and quotes into the field, and read the page source.
- Plugin off and on: Deactivate the plugin and confirm the store still loads.
- The log file: Open
wp-content/debug.logand fix every notice.
For the odd-input test I typed <b>Hot</b> <script>alert(1)</script> & "sale" into the field. WordPress stored Hot & "sale", because sanitize_text_field() removes tags and the script contents. The page printed it as Hot & "sale", so nothing ran.
Step 9: Package and Publish Your Plugin
Zip the plugin folder so the folder itself is the top level of the archive. A site owner can then upload it under Plugins, then Add Plugin, then Upload Plugin. For WordPress.org, the plugin guidelines require GPL-compatible code, and they recommend GPLv2 or later.
Trial versions are not allowed in the directory. The same guidelines forbid features locked behind payment, so premium code belongs in a separate add-on that you host yourself. You can also sell the plugin on your own site if you keep the license GPL-compatible.
How a Plugin Grows: Structure and Good Habits
A single file is fine for a small plugin like this one. Once the plugin grows, split the code so that each file has one clear job. Short files are easier to read, test and hand over to another developer.
Here is a common layout for a larger plugin:
dd-product-badge/ dd-product-badge.php includes/ assets/ languages/ uninstall.php readme.txt
The main file loads the other files and registers hooks. Put admin code in one file, front-end code in another, and keep scripts and styles in assets. An uninstall.php file runs when someone deletes the plugin, so use it to remove saved options.
Notice that Step 6 reads the badge with $product->get_meta() instead of calling get_post_meta(). The product object talks to WooCommerce’s own storage layer, so your code keeps working if the storage changes. The HPOS documentation says it uses the WooCommerce CRUD design for this reason, so prefer the object methods everywhere.
WooCommerce also has its own log viewer under WooCommerce, then Status, then Logs. On my test site it listed log files by source, such as the image regeneration log. Write your plugin’s messages there with a source name of your own, and you can filter them from the rest.
Keep the WC tested up to header current. After each major WooCommerce release, run your staging checks again and bump the number only when they pass.
How WooCommerce Hooks Work
WooCommerce hooks are named points in the code where your function can run. An action lets you do something at that point, such as print a field. A filter lets you change a value before WooCommerce uses it, such as a tab list.
This plugin used both. The field, the save step and the badge are actions. The settings tab list is a filter, because it receives the tabs and returns them with yours added.
Find the right hook by searching the WooCommerce source for the screen you want to change. Read the arguments each hook passes, and check the priority when your output lands in the wrong place. A hook that fires early in the page may need a priority higher or lower than the default of 10.
If the plugin changes what search engines see, such as titles or product data, read our WooCommerce SEO guide before you ship it.
WooCommerce Extension Development With the Official Scaffold
WooCommerce extension development can start from an official template instead of a blank folder. The WooCommerce guide to building your first extension lists Node.js with NPM, Docker and Composer as requirements. It generates a plugin with block support, unit tests and linting already set up.
The guide gives this command to create the project:
npx @wordpress/create-block -t @woocommerce/create-woo-extension my-extension-name
Then run npm install and npm run build inside the new folder. The guide starts a test site with wp-env start, which opens a WordPress site with WooCommerce at localhost:8888. I did not run this route myself, so check the guide for the current steps.
The manual route in this post teaches the parts the scaffold hides. The scaffold suits larger plugins that use JavaScript blocks.
Three Mistakes That Break Your First WooCommerce Plugin
Most first plugins fail in the same three ways. They read order data the old way, they use names that clash with other plugins, or they trust user input. Each one is easy to avoid once you know it, and the fixes take only minutes.
Mistake 1: Querying wp_posts Directly for Order Data
Orders no longer live in wp_posts on stores with HPOS. The HPOS documentation says orders are stored in custom tables when it is active, and in the posts tables under the legacy method. A direct database query for orders returns nothing on an HPOS store.
Use WooCommerce functions such as wc_get_orders() and the order object instead. They work with both storage methods.
Mistake 2: Not Prefixing Your Function Names
Every function, option and meta key shares one global space. A function called show_badge() can clash with another plugin and cause a fatal error. In my plugin, the function is ddpb_settings() and the meta key is _ddpb_badge.
Pick a short prefix from your plugin name and use it everywhere. Anonymous functions, like the ones in this guide, avoid the problem for hooks.
Mistake 3: Skipping Sanitization and Escaping
Sanitize data when it comes in, and escape it when it goes out. This guide used sanitize_text_field() on save and esc_html() on output. My test in Step 8 shows both at work.
Skipping either one opens the store to injected scripts. Never print a value from the database or the browser without escaping it.
Before You Ship: A Quick Checklist
Run through this checklist before you release a plugin to anyone. Each item catches a problem covered earlier in this guide, and each takes only a few minutes to check. Skipping them is how a small plugin ends up breaking a live store.
Check each of these items:
- Header: Check the version numbers and the
Requires Pluginsline. - HPOS: Declare compatibility only after you test with HPOS on.
- Security: Sanitize every input and escape every output.
- Names: Prefix every function, option and meta key.
- Logs: Open
debug.logand confirm it holds no new notices. - Clean up: Add an
uninstall.phpfile if your plugin saves options that should go away on delete. - License: Pick a GPL-compatible license before you publish.
Should You Build a Plugin Yourself or Hire a Developer?
WooCommerce custom plugin development suits a small feature that you understand well. Hire a developer when the work touches checkout, payments or orders, or when a deadline is tight. A mistake in those areas costs real sales and real customer trust.
Build it yourself when the feature is small and you know PHP. An experienced reviewer pays for itself on risky work.
Our team builds custom features for stores, and you can see the scope on our WooCommerce development services page. A good brief names the goal, the screens that change and the plugins already in use.
Conclusion
Now you know the basics of WooCommerce plugin development. Create a folder and header, require WooCommerce, declare HPOS support, then add fields, output and settings through hooks.
Keep every function prefixed, and sanitize input before you escape output. Test on a staging copy with debug logging on. Buy a plugin when the feature is small and common, and hire a developer when the work is complex.
Frequently Asked Questions (FAQs)
Q1. Do I need to know PHP to create a WooCommerce plugin?
Yes. WooCommerce plugins are written in PHP, and you also need to understand WordPress hooks. HTML, CSS and some JavaScript help for front-end features, but without PHP you should buy a ready plugin or hire a developer.
Q2. What is the difference between a WooCommerce plugin and a WordPress plugin?
A WooCommerce plugin is a WordPress plugin that depends on WooCommerce and uses its hooks and functions. It follows the same structure and header rules. With Requires Plugins: woocommerce in the header, WordPress blocks activation when WooCommerce is missing.
Q3. Can I submit my WooCommerce plugin to the WordPress.org directory?
Yes, if it follows the plugin guidelines. The code must be GPL-compatible and cannot lock features behind payment. Include a readme that explains any external service the plugin uses.
Q4. How long does it take to build a WooCommerce plugin?
A small plugin like the one here takes an afternoon if you know PHP. A plugin that touches checkout, shipping or payments takes much longer, mainly for testing. Plan time for staging tests and for fixes after WooCommerce updates.
Q5. How do I add a settings page to my WooCommerce plugin?
Add a tab with the woocommerce_settings_tabs_array filter. Then print fields with woocommerce_admin_fields() and save them with woocommerce_update_options(). Step 7 shows the full code.
Q6. Do I need JavaScript for WooCommerce plugin development?
Not for server-side features such as fields, settings and output, which this guide covers. You need JavaScript for blocks and for interactive admin screens. The official scaffold includes a block build setup.
Q7. Can I sell a WooCommerce plugin I built myself?
Yes. WordPress plugins must be GPL-compatible, but you can sell them on your own site or a marketplace. The WordPress.org directory itself does not allow paid-only features inside a free plugin.
Q8. Will my plugin break when WooCommerce updates?
It can, so keep the WC tested up to header current and test on staging before each major update. Use WooCommerce functions instead of direct database queries, because they follow storage changes such as HPOS.
Q9. Do I need a staging site once my plugin is live?
Yes. A staging copy lets you test plugin updates and WooCommerce updates without risking real orders. Test checkout, the product page and the admin screens before you copy changes to the live store.
