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.
| Path | Purpose |
|---|---|
functions.php | Bootstrap 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 |
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.
<?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
<?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.
<?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'
);
} );
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.
<?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;
}
<?php
/** @var string $message */
?>
<div class="site-banner" role="status">
<?php echo esc_html( $message ); ?>
</div>
Partials are best for presentation-only markup. Keep business logic in the component file.
Component Checklist
| Item | Why it matters |
|---|---|
| One responsibility | Easier to test and reuse |
| Scoped conditions | Avoids running everywhere |
| Escaped output | Prevents XSS bugs |
| Scoped CSS | Prevents global side effects |
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
- Pick one customization from
functions.php. - Move it into a function/class with a clear name.
- Add a condition guard.
- Scope its CSS.
Stretch goal: create a second component that runs only on posts and has its own small stylesheet.
QA Checklist
| Check | Pass criteria |
|---|---|
| Conditions | Component output appears only where intended |
| Output | HTML is escaped and valid |
| Assets | CSS/JS loads only on relevant pages |
| Naming | File and function names match the purpose |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Function name collisions | No prefix/namespace | Add a namespace or unique prefix |
| Component outputs on wrong pages | Missing conditionals | Add explicit guards |
| Styling leaks globally | Unscoped selectors | Prefix 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
- Next: Contribution & Community