WordPress lets you put PHP in a theme's functions.php, in a plugin or in a must-use plugin, and all three run in the same process. The code works wherever you put it. The question is what happens later, when the theme changes, a plugin is deactivated or another developer takes over. This lesson gives you a rule that answers that question every time.
The rule
Ask one question about each feature:
If the site switched to a different theme tomorrow, should this keep working?
- Yes: it belongs in a plugin.
- No, it is only about how this theme looks: it belongs in the theme.
Themes are for presentation. Plugins are for functionality and data.
Why it matters: theme lock-in
Imagine a theme that registers a "Projects" post type in its functions.php. The client adds 200 projects. Two years later they redesign and activate a new theme. The post type is no longer registered, so:
- The Projects menu vanishes from the admin.
- Every project URL returns a 404.
- The 200 projects are still in the database, but nothing can display or edit them.
The content is effectively held hostage by the old theme. This is theme lock-in, and it is one of the most common problems agencies inherit. Had the post type lived in a plugin, the redesign would only have needed new templates.
The same applies to shortcodes. A shortcode registered by a theme leaves raw text such as [team_members] in every post once the theme is gone.
What goes where
| Feature | Location | Why |
|---|---|---|
| Custom post types and taxonomies | Plugin | Content must survive a redesign |
| Custom fields and meta registration | Plugin | Data, not presentation |
| Shortcodes and custom blocks used in content | Plugin | Content depends on them |
| REST API endpoints | Plugin | Other systems depend on them |
| Integrations (CRM, payments, email) | Plugin | Business logic |
| Scheduled tasks | Plugin | Behaviour, not display |
| Templates and template parts | Theme | Pure presentation |
Styles, fonts, colour palettes, theme.json | Theme | Pure presentation |
| Registering menu locations and image sizes used only by the theme's layout | Theme | Tied to the design |
add_theme_support() calls | Theme | They describe what the theme supports |
| Block patterns that only make sense with this design | Theme | Tied to the design |
Some items sit in between. Image sizes are a good example: if content editors pick from them, they may belong in a plugin so their names do not disappear. When in doubt, ask the rule again.
Plugins: the default home for code
A plugin is a folder in wp-content/plugins with a main file that has a header comment. You created one in the setup lesson. Plugins have useful properties that theme code does not:
- They can be activated and deactivated independently.
- They have activation, deactivation and uninstall hooks, so they can set up and clean up.
- Several can be active at once, so features stay separate.
- They can be reused on other sites.
For a client site, it is normal to have one "site plugin" that holds all custom functionality for that site, alongside any general-purpose plugins.
Themes: presentation only
A theme can still contain PHP. Classic themes use PHP templates, and both classic and block themes can have a functions.php. Keep it focused on the theme itself:
// wp-content/themes/mytheme/functions.php
add_action( 'after_setup_theme', 'mytheme_setup' );
function mytheme_setup() {
add_theme_support( 'post-thumbnails' );
add_theme_support( 'title-tag' );
register_nav_menus(
array(
'primary' => __( 'Primary menu', 'mytheme' ),
)
);
}
Everything in this function describes what the theme can display. None of it creates content.
Child themes
If you need to change a theme you did not write, do not edit it: updates overwrite your changes. Create a child theme instead. A child theme is a folder with its own style.css whose header names a parent with Template:. WordPress uses the child's files first and falls back to the parent's. Child themes are for presentation changes too. Functionality still belongs in a plugin.
Must-use plugins
A must-use plugin is a PHP file placed directly in wp-content/mu-plugins. They differ from normal plugins:
| Normal plugin | Must-use plugin | |
|---|---|---|
| Activation | Activated in the admin | Always on, no activation |
| Can be deactivated by an admin | Yes | No, only by removing the file |
| Load order | After must-use plugins | First, before normal plugins |
| Subfolders | Loaded from its own folder | Only PHP files directly in mu-plugins load automatically |
| Activation and deactivation hooks | Yes | No |
| Updates from WordPress.org | Yes | No |
Use must-use plugins for code that must never be switched off by accident:
- Security rules, such as disabling XML-RPC or blocking user enumeration.
- Environment-specific settings, such as forcing a mail server on staging.
- Code other plugins depend on, which must load first.
- Agency or host-level code across many client sites.
Because only top-level files load, a common pattern is a small loader file in mu-plugins that requires the main file of a plugin folder inside it.
Must-use plugins are easy to forget because they do not appear in the normal plugin list. They show under a separate "Must-Use" tab on the Plugins screen. Document them in the project's README.
Do not paste code into functions.php
Tutorials often say "add this to your theme's functions.php". For a two-line presentation tweak, that is fine. For anything that creates content, changes data or integrates with other systems, put it in a plugin, even if the snippet is short. The few minutes it takes to create a plugin file saves a broken site after the next redesign.
Decision checklist
Before writing a feature, run through these questions:
- Does it create or change content or data? Use a plugin.
- Will other systems or other sites depend on it? Use a plugin.
- Must it always run, even if an admin deactivates plugins? Use a must-use plugin.
- Is it only about this design's layout and styles? Use the theme.
- Is the theme someone else's? Use a child theme for presentation, a plugin for the rest.
Summary
- Themes handle presentation; plugins handle functionality and data.
- Ask whether the feature should survive a theme change. If yes, use a plugin.
- Post types, taxonomies, shortcodes, endpoints and integrations in a theme cause theme lock-in.
- Must-use plugins always run, load first and cannot be deactivated from the admin.
- Change third-party themes with a child theme, never by editing them.
In the next module you will learn hooks, the mechanism that every plugin and theme uses to change WordPress.