Timber

You are reading the documentation for Timber v1.x. Switch to the documentation for Timber v2.x.

WordPress Integration

Timber plays nicely with your existing WordPress setup. You can still use other plugins, etc.

the_content #

You’re probably used to call the_content() in your theme file. This is good. Before outputting, WordPress will run all the filters and actions that your plugins and themes are using. If you want to get this into your new Timber theme (and you probably do), call it like this:

<div class="my-article">
{{ post.content }}
</div>

This differs from {{ post.post_content }}, which will display the raw text stored in the database.

Hooks #

Timber hooks to interact with WordPress use this/style/of_hooks instead of this_style_of_hooks. This matches the same methodology as Advanced Custom Fields.

Full documentation to come.

Actions #

Call them in your Twig template...

{% do action('my_action') %}
{% do action('my_action_with_args', 'foo', 'bar') %}

... in your functions.php file:

<?php
add_action( 'my_action', 'my_function' );

function my_function( $context ) {
// $context stores the template context in case you need to reference it

// Outputs title of your post
echo $context['post']->post_title;
}
<?php
add_action( 'my_action_with_args', 'my_function_with_args', 10, 2 );

function my_function_with_args( $foo, $bar ){
echo 'I say ' . $foo . ' and ' . $bar;
}

You can still get the context object when passing args, it’s always the last argument...

<?php
add_action( 'my_action_with_args', 'my_function_with_args', 10, 3 );

function my_function_with_args( $foo, $bar, $context ){
echo 'I say ' . $foo . ' and ' . $bar;
echo 'For the post with title ' . $context['post']->post_title;
}

Please note the argument count that WordPress requires for add_action.

Filters #

Timber already comes with a set of useful filters. If you have your own WordPress filters that you want to easily apply in Twig, you can use apply_filters.

{{ post.content|apply_filters('default_message') }}
{{ "my custom string"|apply_filters('default_message', param1, param2, ...) }}

You can use your filter with a Twig filter tag.

{% filter apply_filters( 'default_message') %}
{{ post.content }}
{% endfilter %}

{% filter apply_filters('default_message', 'foo', 'bar, 'baz' ) %}
I love pizza
{% endfilter %}

In PHP, you can get the content of the block with the first parameter and the rest of parameters like that.

add_filter( 'default_message', 'my_default_message', 10, 4 );

function my_default_message( $tag, $param1, $param2, $param3 ) {
var_dump( $tag, $param1, $param2, $param3 ); // 'I love pizza', 'foo', 'bar, 'baz'

echo 'I have a message: ' . $tag; // I have a message: I love pizza
}

Real world example with WooCommerce #

Sometimes in WooCommerce we found very long line of code:

echo '<li class="woocommerce-notice woocommerce-notice--info woocommerce-info">' . apply_filters( 'woocommerce_no_available_payment_methods_message', WC()->customer->get_billing_country() ? esc_html__( 'Sorry, it seems that there are no available payment methods for your state. Please contact us if you require assistance or wish to make alternate arrangements.', 'woocommerce' ) : esc_html__( 'Please fill in your details above to see available payment methods.', 'woocommerce' ) ) . '</li>';

In Twig:

<li class="woocommerce-notice woocommerce-notice--info woocommerce-info">
{{ customer.get_billing_country() ? __( 'Sorry, it seems that there are no available payment methods for your state. Please contact us if you require assistance or wish to make alternate arrangements.', 'woocommerce' ) : __( 'Please fill in your details above to see available payment methods.', 'woocommerce' ) | apply_filters( 'woocommerce_no_available_payment_methods_message' ) }}
</li>

And with filter tag:

<li class="woocommerce-notice woocommerce-notice--info woocommerce-info">
{% filter apply_filters( 'woocommerce_no_available_payment_methods_message' ) %}
{% if customer.get_billing_country() %}
{{ __( 'Sorry, it seems that there are no available payment methods for your state. Please contact us if you require assistance or wish to make alternate arrangements.', 'woocommerce' ) }}
{% else %}
{{ __( 'Please fill in your details above to see available payment methods.', 'woocommerce' ) }}
{% endif %}
{% endfilter %}
</li>

Widgets #

Everyone loves widgets! Of course they do...

<?php
$data['footer_widgets'] = Timber::get_widgets( 'footer_widgets' );

...where footer_widgets is the registered name of the widgets you want to get (in twentythirteen these are called sidebar-1 and sidebar-2).

Then use it in your template:

base.twig

<footer>
{{ footer_widgets }}
</footer>

Using Timber inside your own widgets #

You can also use twig templates for your widgets! Let’s imagine we want a widget that shows a random number each time it is rendered.

Inside the widget class, the widget function is used to show the widget:

<?php
public function widget( $args, $instance ) {
$number = rand();

Timber::render( 'random-widget.twig', array(
'args' => $args,
'instance' => $instance,
'number' => $number
) );
}

The corresponding template file random-widget.twig looks like this:

{{ args.before_widget|raw }}
{{ args.before_title|raw }}{{ instance.title|apply_filters('widget_title') }}{{ args.after_title|raw }}

<p>Your magic number is: <strong>{{ number }}</strong></p>

{{ args.after_widget|raw }}

The raw filter is needed here to embed the widget properly.

You may also want to check if the Timber plugin was loaded before using it:

<?php
public function widget( $args, $instance ) {
if ( ! class_exists( 'Timber' ) ) {
// if you want to show some error message, this is the right place
return;
}

$number = rand();

Timber::render( 'random-widget.twig', array(
'args' => $args,
'instance' => $instance,
'number' => $number
) );
}

Shortcodes #

Well, if it works for widgets, why shouldn't it work for shortcodes? Of course it does!

Let’s implement a [youtube] shortcode which embeds a youtube video. For the desired usage of [youtube id=xxxx], we only need a few lines of code:

<?php
// Should be called from within an init action hook
add_shortcode( 'youtube', 'youtube_shortcode' );

function youtube_shortcode( $atts ) {
if( isset( $atts['id'] ) ) {
$id = sanitize_text_field( $atts['id'] );
} else {
$id = false;
}

// This time we use Timber::compile since shortcodes should return the code
return Timber::compile( 'youtube-short.twig', array( 'id' => $id ) );
}

In youtube-short.twig we have the following template:

{% if id %}
<iframe width="560" height="315" src="//www.youtube.com/embed/{{ id }}" frameborder="0" allowfullscreen></iframe>
{% endif %}

Now, when the YouTube embed code changes, we only need to edit the youtube-short.twig template. No need to search your PHP files for this one particular line.

Layouts with Shortcodes #

Timber and Twig can process your shortcodes by using the {% filter shortcodes %} tag. Let’s say you're using a [tab] shortcode, for example:

{% filter shortcodes %}
[tabs tab1="Tab 1 title" tab2="Tab 2 title" layout="horizontal" backgroundcolor="" inactivecolor=""]
[tab id=1]
Something something something
[/tab]

[tab id=2]
Tab 2 content here
[/tab]
[/tabs]
{% endfilter %}

Password protected posts #

It’s recommended to use the post_password_required() function to check if a post requires a password. You can add this check in all your single PHP template files

single.php

$context = Timber::context();
$post = Timber::query_post();
$context['post'] = $post;
if ( post_password_required( $post->ID ) ) {
Timber::render( 'single-password.twig', $context );
} else {
Timber::render( array( 'single-' . $post->ID . '.twig', 'single-' . $post->post_type . '.twig', 'single.twig' ), $context );
}

single-password.twig

{% extends "base.twig" %}

{% block content %}
{{ function('get_the_password_form') }}
{% endblock %}

Using a Filter #

With a WordPress filter, you can use a specific PHP template for all your password protected posts. Note: this is accomplished using only standard WordPress functions. This is nothing special to Timber

functions.php

/**
* Use specific template for password protected posts.
*
* By default, this will use the `password-protected.php` template file. If you want password
* templates specific to a post type, use `password-protected-$posttype.php`.
*/

add_filter( 'template_include', 'get_password_protected_template', 99 );

function get_password_protected_template( $template ) {
global $post;

if ( ! empty( $post ) && post_password_required( $post->ID ) ) {
$template = locate_template( [
'password-protected.php',
"password-protected-{$post->post_type}.php",
] ) ?: $template;
}

return $template;
};

With this filter, you can use a password-protected.php template file with the following contents:

<?php

$context = Timber::context();
$context['post'] = new Timber\Post();
$context['password_form'] = get_the_password_form();

Timber::render( 'password-protected.twig', $context );

To display the password on the page, you could then use {{ password_form }} in your Twig file.