Extending GeneratePress via Code
GeneratePress encourages a "hooks over templates" approach. That makes customizations upgrade-safe if you keep code organized, scoped, and measurable.
Where Should This Code Live?
| Location | Best for | Watch-outs |
|---|---|---|
Child theme (functions.php) | Theme-adjacent presentation changes | Tied to the theme; avoid business logic |
| Site plugin | Site features that should survive theme changes | Slightly more setup, but cleaner separation |
MU-plugin (wp-content/mu-plugins) | Must-run essentials (security, must-have hooks) | Always active; mistakes are harder to bypass |
If the customization is about content semantics (schema output, CPTs, REST endpoints), prefer a plugin. If it is purely visual layout, a child theme is fine.
Hooks vs Filters (Practical)
| Type | You use it when... | Example |
|---|---|---|
| Action hook | You want to output or run logic at a point in execution | Add a banner above content |
| Filter hook | You want to modify a value and return it | Adjust excerpt length |
<?php
add_action( 'wp_footer', function () {
if ( is_admin() ) {
return;
}
echo '<div class="site-notice" role="status">New: free audit slots this week.</div>';
}, 20 );
<?php
add_filter( 'excerpt_length', function ( $length ) {
return 22;
} );
GeneratePress Hooks You Will Actually Use
GeneratePress exposes many theme hooks. You do not need to memorize them; you need a small set you reach for consistently.
| Hook (example) | Typical use | Notes |
|---|---|---|
generate_before_header | Add a top bar / announcement | Keep it light; avoid CLS |
generate_after_header | Breadcrumbs or page intro | Ensure only one breadcrumb source |
generate_before_content | Context intro on archives | Great for category hubs |
generate_after_entry_content | Post-end CTA | Target posts only |
generate_before_footer | Trust badges / secondary CTA | Avoid duplicating global CTAs |
<?php
add_action( 'generate_after_entry_content', function () {
if ( ! is_singular( 'post' ) ) {
return;
}
echo '<div class="post-cta">Want help with this? <a href="/contact/">Get in touch</a>.</div>';
} );
The exact hook names you use depend on your layout and GeneratePress version. When in doubt, place a temporary HTML comment marker to confirm where a hook renders.
Hook Priorities (So Output Order Stays Stable)
Multiple actions can run on the same hook. Priority controls the order.
<?php
add_action( 'wp_footer', function () {
echo '<!-- footer: first -->';
}, 5 );
add_action( 'wp_footer', function () {
echo '<!-- footer: second -->';
}, 50 );
Avoid relying on priority "magic" to fix overlapping Elements. Fix overlap by tightening rules first.
A Hook-First Workflow
flowchart TD
A[Requirement] --> B[Can layout settings do it?]
B -->|Yes| C[Use GP layout controls]
B -->|No| D[Can an Element do it?]
D -->|Yes| E[Use Element + display rules]
D -->|No| F["Write small code (hook/filter)"]
F --> G[Re-test + document]
Avoid dumping large blocks of PHP into a Hook Element. Prefer versioned code (child theme or plugin) for anything non-trivial.
Keep Code Predictable
Simple rules that prevent most production issues:
- Guard your code with conditionals (
is_page(),is_singular(),is_admin()) - Escape output (
esc_html,esc_url) and sanitize input - Use unique prefixes/namespaces to avoid collisions
- Enqueue assets only where needed
If you need to create HTML from user inputs, escape early and escape often.
<?php
add_action( 'wp_enqueue_scripts', function () {
if ( ! is_page( 'landing-audit' ) ) {
return;
}
wp_enqueue_style(
'gp-child-landing',
get_stylesheet_directory_uri() . '/assets/css/landing.css',
[],
'1.0.0'
);
} );
Hands-On: Create a Tiny MU-Plugin
Goal: add one sitewide customization that should survive theme changes.
- Create
wp-content/mu-plugins/gp-site.php. - Add a small hook (for example, a footer notice).
- Confirm it appears on the frontend.
<?php
/**
* Plugin Name: GP Site Tweaks
* Description: Must-run site tweaks for GeneratePress sites.
*/
add_action( 'wp_footer', function () {
echo '<!-- gp-site: active -->';
}, 1 );
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Custom code runs everywhere | Missing conditionals | Add explicit guards (is_singular, is_page) |
| Styles load on every page | Enqueue not scoped | Wrap enqueue in conditionals |
| White screen after edit | PHP syntax error | Use staging; check server logs; disable plugin if possible |
| Output appears in wrong spot | Wrong hook chosen | Move to a nearby hook and re-test with markers |
| Duplicate output | Element + code both render | Pick one source; remove/disable the other |
Debugging: Markers Beat Guessing
When you are not sure if code runs, add a harmless marker and search for it in page source.
<?php
add_action( 'wp_footer', function () {
echo '<!-- debug: gp-child-footer-marker -->';
}, 999 );
Quick Reference
- Prefer hooks/filters over editing theme files
- Use Elements for small, UI-driven injections
- Put durable features into a plugin or MU-plugin
What's Next
- Next: Git Integration