Twig templating support with Timber + Timber cache integration
Chisel uses Timber to bridge WordPress data with Twig templating, creating clean, maintainable views. Twig keeps markup expressive and logic-light while Timber provides elegant object-oriented access to WordPress content.
The integration includes environment-aware caching that auto-reloads in development and compiles templates for production. Custom Timber classes extend posts, terms, and images with domain-specific methods, keeping templates declarative.
Global context automatically includes menus, sidebars, logo, and title data. Custom Twig functions handle responsive images, BEM classes, breadcrumbs, icons, and more—all registered via a clean, extensible architecture.
Bootstrapping and architecture
Core initialization
File: core/WP/Site.php
Siteclass extendsTimber\Siteand initializes Timber integration- Registers global context additions via
add_to_context() - Configures class maps for custom Timber objects (posts, terms, images)
- Adds custom Twig template locations via
timber/locationsfilter
File: core/WP/Twig.php
Twigclass extends Twig environment with custom functions, filters, and tests- Registers helper functions accessible in all Twig templates
- Provides extension points via
chisel_twig_register_functions,chisel_twig_register_filters,chisel_twig_register_testsactions
File: core/Timber/Cache.php
Cacheclass configures Timber’s caching behavior- Sets cache mode via
timber/cache/modefilter (defaults toCACHE_NONE) - Controls Twig environment options:
cache,auto_reload,debug - Cache behavior adjusts automatically based on
WP_DEBUGand environment type
File: core/Timber/Components.php
- Static methods for retrieving reusable site components
- Includes:
get_menus(),get_logo(),get_sidebar(),get_footer_sidebars(),get_the_title(),get_icon() - Components are cached internally (request cache) to avoid repeated computation
Global context
Every Timber render automatically includes these context variables (added in Site::add_to_context()):
logo→ Responsive HTML for the custom logo fromComponents::get_logo()menus→ Array of registered navigation menus viaComponents::get_menus()sidebar→ Dynamic sidebar content based on context (blog, WooCommerce, or custom)copyright→ Copyright sidebar fromComponents::get_sidebar('copyright')footer_sidebars→ Footer columns with automatically calculated grid classesthe_title→ Page/archive title with class (respects ACFpage_title_displayfield)
Usage in templates
<header>
{{ logo }}
{{ get_nav_menu('primary') }}
</header>
<aside>
{{ sidebar.content }}
</aside>
TwigCustom Timber classes
Chisel provides custom Timber classes for posts, terms, and images with helpful methods that keep Twig templates clean.
File structure
Location: core/Timber/
ChiselPost.php→ Base class for posts and pagesChiselProduct.php→ WooCommerce products (extendsChiselPost)ChiselTerm.php→ Base class for termsChiselProductCategory.php→ WooCommerce product categories (extendsChiselTerm)ChiselImage.php→ Attachment images with responsive methods
Class mapping
Configured in: Site::post_classmap() and Site::term_classmap()
| Post Type | Mapped Class |
|---|---|
post | ChiselPost |
page | ChiselPost |
product | ChiselProduct |
attachment | ChiselImage |
| Any other CPTs | ChiselPost (default) |
| Taxonomy | Mapped Class |
|---|---|
category | ChiselTerm |
product_cat | ChiselProductCategory |
| Any other custom taxonomies | ChiselTerm (default) |
ChiselPost methods
File: core/Timber/ChiselPost.php
get_thumbnail(string $size = 'medium', array $attrs = [])→ Returns responsive<img>HTML with srcset- Automatically caches thumbnail HTML to prevent redundant calls
- Falls back to empty string if no thumbnail exists
Usage in Twig:
{{ post.get_thumbnail('large') }}
{{ post.get_thumbnail('medium', { class: 'c-image' }) }}TwigChiselProduct methods
File: core/Timber/ChiselProduct.php
- Extends
ChiselPostwith WooCommerce-aware thumbnail handling - Provides placeholder image fallback for products without thumbnails
- Respects WooCommerce image size filters
ChiselImage methods
File: core/Timber/ChiselImage.php
responsive(string $size, array $attrs = [])→ Responsive HTML for attachment images
Usage in Twig:
{% set image = TimberImage(post.meta('gallery_image')) %}
{{ image.responsive('large', { class: 'c-gallery__image' }) }}TwigExtending with custom CPT classes
1. Create your custom class
File: custom/app/Timber/MoviePost.php
namespace Chisel\Timber;
use Timber\Post as TimberPost;
class MoviePost extends TimberPost {
public function rating_badge(): string {
$rating = $this->meta('rating');
return $rating ? 'Rated: ' . esc_html($rating) : '';
}
public function release_year(): int {
return (int) $this->meta('release_year') ?: 0;
}
}PHP2. Register in class map
File: custom/app/WP/Site.php (override post_classmap())
use Chisel\Timber\MoviePost;
public function post_classmap(array $classmap): array {
$classmap['movie'] = MoviePost::class;
return $classmap;
}PHP3. Use in Twig
{% for movie in movies %}
<div class="c-movie">
<h3>{{ movie.title }}</h3>
<span>{{ movie.rating_badge() }}</span>
<span>{{ movie.release_year() }}</span>
</div>
{% endfor %}TwigExtending existing core Timber classes
To add custom methods to Timber classes already registered in core/Timber/ (like ChiselPost, ChiselProduct, ChiselTerm), extend the core class instead of replacing it. This preserves core functionality while adding your custom methods.
File:
namespace Chisel\Timber;
use Chisel\Timber\ChiselPost as CoreChiselPost;
class ChiselPost extends CoreChiselPost {
public function reading_time(): string {
$word_count = str_word_count(strip_tags($this->content()));
$minutes = ceil($word_count / 200);
return $minutes . ' min read';
}
public function formatted_date(): string {
return $this->date('F j, Y');
}
}PHPFile: custom/app/WP/Site.php (add to post_classmap())
public function post_classmap( array $classmap ): array {
$custom_classmap = array(
'post' => \Chisel\Timber\Custom\ChiselPost::class,
);
return array_merge( $classmap, $custom_classmap );
}PHPUsage in Twig:
<article>
<h1>{{ post.title }}</h1>
<time>{{ post.formatted_date() }}</time>
<span>{{ post.reading_time() }}</span>
{{ post.get_thumbnail('large') }} {# Core method still available #}
</article>TwigBy extending the core class, all existing methods like get_thumbnail() remain available while your custom methods are added.
Twig functions
All functions registered in core/WP/Twig.php via timber/twig filter.
Navigation and menus
get_nav_menu(string $name)→ Retrieves menu from global context by name (e.g.,'primary')
<nav class="c-nav">
{{ get_nav_menu('primary') }}
</nav>TwigImages
get_responsive_image(int $id, string $size = 'medium', array $attrs = [])→ Responsive<img>HTML with srcset/sizes
{{ get_responsive_image(post.thumbnail_id, 'large', { class: 'c-hero__image' }) }}TwigBEM class generation
bem(string $name, mixed ...$modifiers)→ Generates BEM classes with modifierspost_classes(string $classes, string $prefix = 'c-post')→ Transforms WordPress post classes to BEM format
<div class="{{ bem('c-card', 'featured', is_sticky ? 'sticky' : false) }}">
{# Outputs: c-card c-card--featured c-card--sticky #}
</div>
<article class="{{ post_classes(post.class, 'c-article') }}">TwigIcons and components
get_icon(array $args)→ Rendersviews/objects/icon.twigwith cachingshould_use_icons_module()→ Checks if icons module is configured
{{ get_icon({ name: 'arrow-right', size: 'small' }) }}TwigBreadcrumbs
breadcrumbs()→ Yoast SEO breadcrumbs (requires Yoast plugin)
{{ breadcrumbs() }}TwigComments
comments_template()→ WordPress comments list and form
{{ comments_template() }}TwigSlider helpers
slider_prepare_params(array $params)→ Prepares slider data attributes from ACF block settings
{% set slider_attrs = slider_prepare_params(block.fields.slider_settings) %}
<div {{ slider_attrs.attrs|join(' ')|raw }}>TwigWooCommerce
timber_set_product(object $post)→ Sets global$productfor WooCommerce templates
Template locations and structure
Base templates
Location: views/
base.twig→ Main layout templateindex.twig,page.twig,single.twig,archive.twig→ Page templates404.twig,search.twig,author.twig→ Special templates
Reusable components
Location: views/objects/, views/components/, views/partials/
objects/icon.twig→ SVG icon renderercomponents/→ Larger UI componentspartials/→ Smaller template fragments
Block templates
Native blocks: src/blocks/{block-name}/{block-name}.twig
- Automatically discovered via
Blocks::twig_files_locations() - Compiled to
build/blocks/{block-name}/{block-name}.twig
ACF blocks: src/blocks-acf/{block-name}/{block-name}.twig
- Automatically discovered via
AcfBlocks::twig_files_locations() - Compiled to
build/blocks-acf/{block-name}/{block-name}.twig
Custom template override
Location: custom/views/
Custom Twig files here override core views. The Site::twig_files_locations() method prepends this location to the search path.
Caching and environment
Cache configuration
File: core/Timber/Cache.php
Cache mode (via timber/cache/mode filter):
- Default:
Loader::CACHE_NONE - Other options:
,CACHE_USE_DEFAULTCACHE_OBJECT,CACHE_TRANSIENT,CACHE_SITE_TRANSIENT
Twig environment options (via timber/twig/environment/options filter):
| Option | Development | Production |
|---|---|---|
cache | false (filtered via chisel_environment_cache) | true or false (if WP_DEBUG is false) |
auto_reload | true | false |
debug | true | false |
Environment detection
Controlled by ThemeHelpers::is_dev_env():
- Returns
trueifWP_DEBUGistrueorWP_ENVIRONMENT_TYPEis'development' - Development: Twig auto-reloads templates on every change, debug mode enabled
- Production: Twig caches compiled templates, no auto-reload
Cache expiry
Property: Cache::$cache_expiry (default: HOUR_IN_SECONDS)
Filter: chisel_cache_expiry
Used by CacheHelpers::expiry() when compiling components like icons and blocks.
Enable environment cache via filter:
add_filter('chisel_environment_cache', '__return_true');PHPTypical render flow
1. WordPress template file (e.g., page.php):
$context = Timber::context();
$context['post'] = Timber::get_post();
Timber::render('views/page.twig', $context);PHP2. Global context automatically includes:
logo,menus,sidebar,footer_sidebars,the_title- Added by
Site::add_to_context()
3. Twig template (e.g., views/page.twig):
{% extends "base.twig" %}
{% block content %}
<article class="{{ post_classes(post.class) }}">
<h1>{{ post.title }}</h1>
{{ post.get_thumbnail('large') }}
<div class="c-content">
{{ post.content }}
</div>
</article>
{% endblock %}Twig4. Blocks render their Twig via:
- Native:
BlocksHelpers::render_twig_file() - ACF:
BlocksHelpers::acf_block_render()
Both discover and render {block-name}.twig from the appropriate build directory.
Extending Twig
Adding custom functions
File: custom/app/WP/Twig.php (using action hooks)
public function action_hooks(): void {
add_action( 'chisel_twig_register_functions', array( $this, 'register_functions' ), 10, 2 );
add_action( 'chisel_twig_register_filters', array( $this, 'register_filters' ), 10, 2 );
}
public function register_functions( \Twig\Environment $twig, \Chisel\WP\Twig $chisel_twig ): void {
$twig->addFunction( new \Twig\Function( 'custom_fn', array( $this, 'custom_fn_callback' ) ) );
}
public function register_filters( \Twig\Environment $twig, \Chisel\WP\Twig $chisel_twig ): void {
$twig->addFilter( new \Twig\Function( 'custom_filter', array( $this, 'custom_filter_callback' ) ) );
}PHPBenefits of Timber class maps
- Domain model: Business logic lives in classes, not templates
- Cleaner Twig:
{{ product.get_thumbnail() }}instead of function calls - Type safety: IDE autocomplete and type hints for custom methods
- Consistency: Every post type instance uses the same API
- Testing: Easier to unit test class methods vs template logic
- WooCommerce integration: Built-in placeholder and size handling for products
Best practices
- Keep Twig templates declarative—move logic into Timber classes
- Use
custom/views/to override core templates (avoids core modification) - Create custom Timber classes for CPTs with unique behavior
- Leverage global context for site-wide data (menus, sidebars)
- Use
get_icon()for icons to benefit from automatic caching - Use
bem()function for consistent BEM class generation - Place block-specific Twig templates alongside block code in
src/blocks/orsrc/blocks-acf/