The question has five different answers today, and the choice between them determines maintenance cost for years. The simplest is a layout assembled in the block editor from a ready-made pattern. The most involved is a custom plugin with its own data model. Page templates, custom post types, and custom fields sit between them. The rule is straightforward: move down the list only when the level above genuinely will not do.
Five levels of solution
The order below follows increasing complexity and increasing maintenance cost.
1. Block editor and patterns
Most pages that differ visually from the rest of a site can now be built in the block editor. Block patterns are ready-made section layouts that a marketing team assembles on its own and saves for reuse.
This level has one advantage the others do not: it needs no developer for each change. A campaign landing page built from patterns takes an hour and is edited without raising a ticket with the technical team.
2. A template in a block theme
When the layout has to go beyond the content area, a page with no header and footer or a different column structure for instance, the answer is a block theme template: an HTML file in templates/, assignable from the editor.
Style variations come in on top of that if the section needs its own palette or typography. Still no PHP.
3. A classic page template
In classic themes, a custom template is a PHP file in the theme directory with a Template Name comment header, which makes it selectable in the page editor.
One practical note. Naming based on the page ID (page-{ID}.php) works but is fragile: IDs differ between staging and production and drift apart during migration. A slug-based name (page-services.php) or a Template Name template is safer.
This level carries a hidden cost. A template stored in the theme means every layout change needs a developer and a deployment. On pages changed once every three years, that is fine. On campaign pages it is a bottleneck. For a page built from raw markup instead of a theme template, see how to create a custom HTML template.
4. Custom post types and custom fields
When the requirement is a repeatable dataset rather than a single page, the right answer is custom post types. A product catalogue, knowledge base, location list, or job board organised as a separate type with its own taxonomies is easier to maintain than several dozen pages in a flat structure.
Advanced Custom Fields extends the data model with fields WordPress does not provide by default. It is the standard tool where editorial staff fill in structured data rather than free-form content.
Register the post type in a custom plugin, not in the theme’s functions.php. A type registered in the theme disappears from the admin whenever the theme changes, leaving the data in the database with no way to edit it. It is one of the most common problems on sites we take over. Once you’re at this level, scale becomes the real question — see how many pages you can actually create on a WordPress website.
5. A custom plugin
The highest level of complexity: custom database tables, integrations with external systems, multi-step processes, business logic. A membership portal, a product configurator, a multi-stage application.
At this point we are no longer discussing a page but an application embedded in WordPress. It is worth naming that at the planning stage, because the estimate, the schedule, and the maintenance requirements look nothing like the other four levels.
How to choose the right level
Ask three questions, in this order.
Is this about appearance or functionality? Appearance alone is covered by levels 1 and 2. Only functionality requires code.
Is this one page or a repeatable pattern? One page is a template. A repeatable dataset is a content type.
Who will change it, and how often? This question decides most cases and gets asked least. If marketing needs to update the content every fortnight, the solution has to be editable without a developer. Dropping to level 3 on a campaign page means every headline tweak requires a deployment.
Going a level lower than necessary is not neutral. It increases dependency on the technical team, lengthens the time to make changes, and raises maintenance cost across the whole life of the site.
What the code levels require
If a project drops to level 3 or below, specific skills come into play.
PHP is the foundation, since WordPress is built on it. Beyond the language itself, what matters is familiarity with platform-specific functions and extension points.
HTML and CSS are needed for structure and styling. JavaScript comes in for interactive elements and for working with the WordPress REST API.
Platform-specific knowledge covers:
- the WordPress loop and query functions
- actions and filters
- custom fields and metadata
- security practices: nonces, validation, and input sanitisation
- database structure and custom queries
Best practices
Security comes first. Input needs sanitising and validating, forms need nonces. No user data reaches the database or the output without verification.
Code organisation determines maintenance cost. Put customisations in a child theme or a custom plugin, not in parent theme files. Function and variable names should be readable, and the code commented enough that the next person does not have to reconstruct it.
Performance means limiting database queries and loading scripts only where they are needed. Assets loaded on every page of a site but used on one are among the most common reasons for poor Core Web Vitals.
WordPress coding standards make a project easier to maintain and hand over. Version control in Git is a condition, not an option.
Troubleshooting common problems
Template conflicts appear when several plugins or the theme try to control the same elements. The method is deactivating plugins one at a time and checking whether the intended template is being loaded at all.
Debug mode surfaces PHP errors and warnings. Enable it through WP_DEBUG in wp-config.php, on staging, never in production.
Styling problems usually come from conflicts between custom CSS and theme styles. Browser developer tools show which rules are being applied. The fix is a more precise selector or sorting out the order in which stylesheets load.
Avoid the !important declaration. It works as a quick workaround, but each use makes the next change harder, and after a dozen occurrences the stylesheet becomes unpredictable. If !important looks like the only way out, the problem usually sits in the structure of the styles rather than in one rule.
Common functional errors:
- the wrong extension point, or the right one called at the wrong time
- missing nonces in forms
- errors in database queries
- JavaScript conflicts
Test changes on staging before deploying to production. That limits the risk of breaking functionality that already works.
In summary
Choosing the level matters more than the quality of execution within it. A well-written PHP template where a block pattern would have done still creates an unnecessary dependency on the technical team for the entire life of the site.
Start at the top of the list and move down only when the level above genuinely will not do. On larger projects this decision is made once and shapes everything that follows.
An external team makes sense where a project sits at level 4 or 5: a custom data model, integrations with external systems, performance requirements, or a scale at which an architectural mistake costs more than the build itself.
FAQ: custom page in WordPress
Does a custom WordPress page require a developer?
Not always. A layout that differs from the rest of the site can usually be built with the block editor and patterns. Code is needed only for custom functionality.
When do I need a custom post type instead of a regular page?
When the requirement is a repeatable dataset, such as a catalogue, knowledge base, or location list, rather than a single page.
Where should custom post types be registered?
In a custom plugin, not in the theme’s functions.php. A type registered in the theme disappears from the admin when the theme changes.
Is page-{ID}.php a good template name?
No. Page IDs differ between environments and drift apart during migration. A slug-based name or a Template Name header is safer.



