Skip to main content

Component-Based Architecture

As custom code grows, a single functions.php becomes a dumping ground. A component approach keeps customizations maintainable: small classes/functions that render one thing, with scoped assets and clear conditions.

A Practical Folder Structure

Even in a child theme, you can keep things organized.

PathPurpose
functions.phpBootstrap only (requires, setup)
inc/components/Component PHP files (one feature each)
inc/templates/Small template partials (optional)
assets/css/Component-scoped CSS
assets/js/Small JS helpers
note

The goal is not "framework". The goal is: when something breaks, you can find the right file quickly.

Bootstrap: Require Your Components

Keep functions.php as a bootstrap that loads files.

functions.php: load component files
<?php
require_once __DIR__ . '/inc/components/banner.php';
require_once __DIR__ . '/inc/components/post-cta.php';

What "Component" Means in WordPress

A component is a unit with:

  • Input (context: page type, settings)
  • Output (HTML)
  • Optional assets (CSS/JS)
  • Conditions (where it should run)
flowchart TD
A[Request] --> B[WP query]
B --> C[Hook runs]
C --> D[Component condition]
D -->|match| E[Render HTML]
D -->|no| F[No output]

Example: A Small Banner Component

Child theme: simple component function
<?php
namespace GPChild;

function render_banner( string $message ): void {
echo '<div class="site-banner" role="status">' . esc_html( $message ) . '</div>';
}

add_action( 'wp_body_open', function () {
if ( is_page( 'landing-audit' ) ) {
render_banner( 'Limited audit slots available.' );
}
} );

Conditional Assets (So You Don't Load Everything)

If a component needs CSS/JS, enqueue it only where the component can appear.

Component: enqueue CSS only where needed
<?php
namespace GPChild;

add_action( 'wp_enqueue_scripts', function () {
if ( ! is_page( 'landing-audit' ) ) {
return;
}
wp_enqueue_style(
'gp-child-banner',
get_stylesheet_directory_uri() . '/assets/css/banner.css',
[],
'1.0.0'
);
} );
caution

If you cannot describe where a component runs, you cannot safely enqueue its assets. Add explicit conditions first.

Template Partials (Optional, But Useful)

If a component's HTML grows, move markup into a small partial so the PHP stays readable.

Component: render a template partial
<?php
namespace GPChild;

function render_template( string $name, array $data = [] ): void {
$path = __DIR__ . '/../templates/' . $name . '.php';
if ( ! file_exists( $path ) ) {
return;
}
extract( $data, EXTR_SKIP );
include $path;
}
inc/templates/banner.php (example partial)
<?php
/** @var string $message */
?>
<div class="site-banner" role="status">
<?php echo esc_html( $message ); ?>
</div>
tip

Partials are best for presentation-only markup. Keep business logic in the component file.

Component Checklist

ItemWhy it matters
One responsibilityEasier to test and reuse
Scoped conditionsAvoids running everywhere
Escaped outputPrevents XSS bugs
Scoped CSSPrevents global side effects
tip

If you find yourself adding lots of unrelated code to one component, split it. That's your signal the boundaries are wrong.

Hands-On: Refactor One Customization

  1. Pick one customization from functions.php.
  2. Move it into a function/class with a clear name.
  3. Add a condition guard.
  4. Scope its CSS.

Stretch goal: create a second component that runs only on posts and has its own small stylesheet.

QA Checklist

CheckPass criteria
ConditionsComponent output appears only where intended
OutputHTML is escaped and valid
AssetsCSS/JS loads only on relevant pages
NamingFile and function names match the purpose

Troubleshooting

SymptomLikely causeFix
Function name collisionsNo prefix/namespaceAdd a namespace or unique prefix
Component outputs on wrong pagesMissing conditionalsAdd explicit guards
Styling leaks globallyUnscoped selectorsPrefix with a component class

Quick Reference

  • Components = small units with clear inputs/outputs
  • Use namespaces/prefixes
  • Render via hooks with explicit conditions

What's Next