Timber Logo

Version 2 👋

You’re looking at the documentation for version 2 of Timber, which is still in development. You can’t use it yet, but you may have a look at the upcoming changes.

Follow us on Twitter or start watching new releases of our GitHub repository to be notified about a version you can use.

Upgrade to 2.0

Version 2.0 of Timber

  • Will only work as a Composer package.
  • Tries to make the naming of functions and filters more consistent.
  • Refactors how Timber Core works under the hood to improve compatibility with WordPress Core and be ready for future challenges.
  • Removes a lot of deprecated code

New requirements #

We upgraded Timber to work with more modern PHP and functionalities that only were introduced in newer versions of Timber. That’s why we now have the following requirements to run Timber:

  • Timber 2.0 requires you to use a PHP version >= 7.0.
  • Timber 2.0 requires WordPress 5.3 or greater. This was changed to take advantage of new date/time improvements built into WordPress. If you need to use WordPress 5.2 or earlier, please continue to use Timber 1.x.
  • Timber now requires Twig in a version >= 2.10.

No more plugin support #

As of version 2.0, you can’t install Timber as a plugin. You need to install it through Composer. Follow the Setup Guide for how to install Timber. Timber will continue to exist as a WordPress plugin in version 1.x.

Removed functionality #

Removed Routes #

The Routing feature was removed in Timber 2.0. Routing in Timber is an edge case. Many of its use cases can usually be solved via existing WordPress functionality. In case you still need Routing, you can use one of the available libraries and hook it into your code. Follow the Routing Guide for more information.

Removed support for Co-Authors Plus #

We dropped support for the Co-Authors Plus plugin, because of compatibility issues with Timber 2.0. Maybe we’ll add it back if we find a good way to support it.

If you still rely on Co-Authors Plus, we don’t recommend you to update to Timber 2.0 just yet.

Namespaced class names #

Namespaced class names were already introduced in Timber version 1.0. Up until now, you could still the use the old, non-namespaced class names. Only namespaced class names are used now. In version 2.0, we removed the following class aliases:

  • TimberArchives, use Timber\Archives instead
  • TimberComment, use Timber\Comment instead
  • TimberCore, use Timber\Core instead
  • TimberFunctionWrapper, use Timber\FunctionWrapper instead
  • TimberHelper, use Timber\Helper instead
  • TimberImage, use Timber\Image instead
  • TimberImageHelper, use Timber\ImageHelper instead
  • TimberIntegrations, use Timber\Integrations instead
  • TimberLoader, use Timber\Loader instead
  • TimberMenu, use Timber\Menu instead
  • TimberMenuItem, use Timber\MenuItem instead
  • TimberPost, use Timber\Post instead
  • TimberPostGetter, without replacement
  • TimberPostCollection, use Timber\PostCollection instead
  • Timber\PostsCollection, use Timber\PostCollection instead
  • TimberQueryIterator, without replacement
  • TimberRequest, use Timber\Request instead
  • TimberSite, use Timber\Site instead
  • TimberTerm, use Timber\Term instead
  • TimberTermGetter, without replacement
  • TimberTheme, use Timber\Theme instead
  • TimberTwig, use Timber\Twig instead
  • TimberURLHelper, use Timber\URLHelper instead
  • TimberUser, use Timber\User instead
  • TimberCommand, use Timber\Command instead
  • Timber_WP_CLI_Command, use Timber\Timber_WP_CLI_Command instead

Timber\Timber #

A special case is the class alias Timber for the Timber\Timber class. We decided to keep it, because it’s more convenient to write Timber::render() instead of Timber\Timber::render().

Twig classes #

Timber now requires Twig in a version >= 2.4. This means that Twig comes with its own namespaced classes:

  • Instead of Twig_Function or Twig_SimpleFunction, you need to use Twig\TwigFunction.
  • Instead of Twig_Filter or Twig_SimpleFilter, you need to use Twig\TwigFilter.
  • Instead of Twig_Environment, you need to use Twig\Environment.

You maybe use one of those classes with the timber/twig filter. Make sure you update them.

In Timber 1.x, we used to have Timber\Twig_Function and Timber\Twig_Filter as interim classes that could be used for better compatibility with the different class names that exist with Twig. These are now removed as well. Use the classes Twig\TwigFunction and Twig\TwigFilter instead.

An updated API to get Timber objects #

In Timber 2.0, we updated the API to get Timber objects to avoid certain pitfalls. Here’s the short list:

  • Use Timber::get_post() to get a post. Using new Timber\Post() will not work anymore.
  • Use Timber::get_posts() to get posts. Using new Timber\PostQuery() will not work anymore.
  • Use Timber::get_term() to get a term. Using new Timber\Term() will not work anymore.
  • Use Timber::get_terms() to get terms.
  • Use Timber::get_comment() to get a comment. Using new Timber\Comment() will not work anymore.
  • Use Timber::get_comments() to get comments.
  • Use Timber::get_menu() to get a menu. Using new Timber\Menu() will not work anymore.
  • Use Timber::get_user() to get a user. Using new Timber\User() will not work anymore.
  • Use Timber::get_users() to get users.

You will find more details in the following sections.

Factories #

Behind the scenes, Timber now uses a Factory Pattern to get Timber objects. No worries, you don’t have to understand this programming pattern to work with Timber. But it is very helpful for developers who use advanced coding patterns and extend Timber with their own classes, because now it’s easier to control which PHP classes are used to create Timber objects.

Removed classes #

By using the Factory Pattern, we refactored a lot of code and by moving logic into the factory classes were able to remove the following classes:

  • Timber\PostGetter
  • Timber\TermGetter
  • Timber\QueryIterator

Posts #

Before version 2.0, when you wanted to get a collection of posts, the standard way was to use Timber::get_posts(), and Timber::get_post() to get a single post. But you could also use new Timber\Post() or new Timber\PostQuery(). We deprecated the possibility to instantiate objects directly. Instead, you should only use Timber::get_post() and Timber::get_posts().

Make sure you also read the new Posts Guide.

Timber::get_post() #

Use Timber::get_post() instead of new Timber\Post()

It’s not possible anymore to directly instantiate a post with new Timber\Post(). Instead, you always need to use Timber::get_post() and pass in the ID of the post.


// Figure out post to get from current query.
$post = Timber::get_post();

Any template

// Pass in a post ID to get a particular post.
$post = Timber::get_post( 56 );
Updated function signature for Timber::get_post()

We updated the function parameters for Timber::get_post().


function get_post( $query = false, $PostClass = 'Timber\Post' );


function get_post( mixed $query = false, array $options = [] );

We deprecated the $PostClass parameter. If you want to control the class your post should be instantiated with, use a Class Map filter. Instead, we now have an $options array.

Timber::get_posts() #

Use Timber::get_posts() instead of new Timber\PostQuery()


$query = [
'post_type' => 'book',
'posts_per_page' => 10,
'post_status' => 'publish',

$latest_books = new Timber\PostQuery( $query );


$query = [
'post_type' => 'book',
'posts_per_page' => 10,
'post_status' => 'publish',

$latest_books = Timber::get_posts( $query );

foreach ( $latest_books->to_array() as $book ) {
// Do something.
Use an array instead of query strings in Timber::get_posts()

It will no longer be possible to pass in arguments to Timber::get_posts() as a query string. Instead, you will have to use the array notation.


Timber::get_posts( 'post_type=article' );


Timber::get_posts( [
'post_type' => 'article',
] );
Updated function signature for Timber::get_posts()

We updated the function parameters for Timber::get_posts().


function get_posts( $query = false, $PostClass = 'Timber\Post', $return_collection = false );


function get_posts( array $query, array $options = [] );

The function still accepts the $query parameters as the first argument. Most of your queries will still work.

The following two parameters were deprecated:

  • $PostClass – You can’t directly pass the class to instantiate the object with anymore. Instead, you will have to use a Class Map filter.
  • $return_collection – You can’t tell the function whether it should return a Timber\PostCollection or an array of posts. It will always return a Timber\PostCollection. But you can always convert to collection to posts when you use to_array():
$posts_as_array = Timber::get_posts()->to_array();


$args = [
'post_type' => 'book',
'posts_per_page' => 10,
'post_status' => 'publish',

$latest_books_collection = Timber::get_posts( $args, 'Book', true );
$latest_books_array = Timber::get_posts( $args, 'Book' );


$args = [
'post_type' => 'book',
'posts_per_page' => 10,
'post_status' => 'publish',

$latest_books_collection = Timber::get_posts( $args );
$latest_books_array = Timber::get_posts( $args )->to_array();

The new $options parameter can be used to pass in options for the query. Check out the documentation for Timber::get_posts() to see all options.

Post queries in Twig #

You can run post queries in Twig. Pass the parameters in an argument hash (in Twig, key-value arrays are called hashes, and you use curly braces {} instead of brackets [] for them).

{# Hash notation #}
{% for post in get_posts({
post_type: 'post',
post_status: 'publish',
posts_per_page: 10
}) %}

<a href="{{ post.link }}">{{ post.title }}</a>
{% endfor %}

We still recommend you to run queries and prepare posts in PHP and not in Twig. But sometimes this is not possible when you don’t have access to the relevant PHP files, but only to Twig.

Post Collections #

We added some documentation about how to work with Post Collections, which are returned when you use Timber::get_posts().

Make sure you also read about the Laziness of posts.

Post Serialization #

When converting post data to JSON, for example to use post data in JavaScript, then we talk about Serialization. We added some documentation for that.

Terms #


Updated default query parameters for Timber::get_terms() #

Before Timber 2.0, we set the default argument hide_empty to false in the term query, which is not what WordPress does by default. WordPress always hides empty terms by default in term queries. For version 2, we don’t change these defaults and follow what WordPress does.

If you need empty terms, you need to explicitly set hide_empty to false.

$terms = Timber::get_terms( [
'taxonomy' => 'category',
'hide_empty' => false,
] );

Comments #


Before version 2.0, when you wanted to get a menu, the standard way was to use new Timber\Menu(). We deprecated the possibility to instantiate objects directly. Instead, you should only use Timber::get_menu().


$menu = new Timber\Menu( 'primary' );


$menu = Timber::get_menu( 'primary' );

A parameter is always required #

Before 2.0, you could pass in nothing to the constructor of Timber\Menu to get the first menu Timber found. We removed the possibility to pass in nothing when getting a menu because it led to confusing cases.


This will cause an error.

$menu = new Timber\Menu();


Always pass a parameter.

$menu = Timber::get_menu( 'primary' );

The Pages Menu #

Previously, if you didn’t provide a parameter to Timber\Menu() and didn’t have any menus registered, Timber would build a menu from your existing pages. To achieve this same functionality, you must now use the new Timber::get_pages_menu() function.


$menu = new Timber\Menu();


$menu = Timber::get_pages_menu();

If Timber::get_menu() can’t find a menu with the parameters you used, it will now return false.

Users #


Context #

Global context #

The context variables {{ wp_head }} and {{ wp_footer }} were removed definitely from the global context. Use {{ function('wp_head') }} and {{ function('wp_footer') }} in your Twig template directly.

Template context #

Version 2.0 introduces the concept of template contexts for Timber. This means that Timber will automatically set post in your context for singular templates and posts for archive templates. Through the context, compatibility for third party plugins will be improved as well. Refer to the new Context Guide to learn more.

In short:

  • If you use $context['post'] = Timber::get_post() or $context['posts'] = Timber::get_posts() in your template files, you can probably omit these, because the context will do this for you. You might also benefit from better compatibility with third party plugins, because for singular templates, Timber will handle certain globals and hooks when setting up post.
  • If you decide to still use $context['post'] = … in your template file, then you should to set up your post through $context['post']->setup(). The setup() function improves compatibility with third-party plugins.
  • It’s now possible to overwrite the default arguments that are passed to the default query for posts.
  • When you need the global context in partials, then use Timber::context_global() to only load the global context.

Twig #

Updated function names #

In Twig, you could use functions that were called the same as classes to convert objects or IDs of objects into Timber object. To have the same function names as in Timber’s public API, we’ve added the following functions:

  • {{ get_post() }}
  • {{ get_posts() }}
  • {{ get_attachment_by() }}
  • {{ get_term() }}
  • {{ get_terms() }}
  • {{ get_user() }}
  • {{ get_users() }}
  • {{ get_comment() }}
  • {{ get_comments() }}

The following functions are now deprecated:

Deprecated functionsUse one these instead
{{ TimberPost() }}
{{ Post() }}
{{ get_post() }}
{{ get_posts() }}
{{ TimberTerm() }}
{{ Term() }}
{{ get_term() }}
{{ get_terms() }}
{{ TimberImage() }}
{{ Image() }}
{{ get_image() }}
{{ get_attachment() }}
{{ get_posts() }}
{{ get_attachment_by() }}
{{ TimberUser() }}
{{ User() }}
{{ get_user() }}
{{ get_users() }}

Namespaced Twig locations #

You can now use namespaced Twig locations. Read more about this in the Template Locations Guide.

Better compatibility with Twig date functions #

Twig has a date filter as well as a date() function. Timber 2.0 has much better compatibility with this functionality than before. Now, you can use any of the functionality described in Twig’s documentation.

Timber will automatically load the timezone as well as the default date format from the WordPress settings when you work with dates in Twig. Even if it’s advised in the Twig documentation, you shouldn’t update the timezone in Twig (unless you know what you’re doing):

// Don’t do this!
$twig = new \Twig\Environment( $loader );
$twig->getExtension( \Twig\Extension\CoreExtension::class )->setTimezone( 'Europe/Paris' );

Also refer to the new Date/Time Guide for extended information on working with dates in Timber.

No context argument when calling actions in Twig #

In version 1.x of Timber, you would always get the context as a last argument in the hook function:

{% do action('my_action', 'foo') %}
add_action( 'my_action_with_args', 'my_function_with_args', 10, 3 );

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

In version 2.0, a context argument will no longer be passed to the hook function. Now, if you want anything from the template’s context, you’ll need to pass in the argument manually:

{% do action('my_action', 'foo', post) %}
add_action( 'my_action_with_args', 'my_function_with_args', 10, 2 );

function my_function_with_args( $foo, $post ) {
echo 'I say ' . $foo . '!';
echo 'For the post with title ' . $post->title();

Deprecated Twig filters #

The following Twig filters have been deprecated and will be removed in future versions of Timber:

  • get_class
  • print_r

In addition, the confusingly named (and non-functional) get_type filter has been removed.

Meta #

Deprecating direct access to meta values #

In Timber 1.x, it was possible to access meta values via dynamic properties. For example, you could do:

{{ post.my_custom_field_name }}

This is no longer the recommended way to access meta values because there might be conflicts with existing Timber methods or properties. For example, if you named a custom field date and accessed it through {{ post.date }}, it wasn’t clear if you would get the posts’ date or the value for your custom field named date.

Access meta values through meta() #

The new recommended way to access meta values is through meta():

{{ post.meta('my_custom_field_name') }}

This way, your values will be filtered by third-party plugins like ACF.

Access raw meta values #

If you want to access the raw and unfiltered value directly from the database instead, you can use the new raw_meta() method:

{{ post.raw_meta('my_custom_field_name') }}

The custom property #

Maybe you were also used to use the $custom property on an object:

{{ post.custom.my_custom_field_name }}

This property was removed and you can no longer access meta values through it. It was only meant as a reference for you to see which meta values exist for an object when you use {{ dump() }} or var_dump(). To access the values, you should always use the meta('field_name') and raw_meta('field_name') methods. If you still need to know which meta values exist on an object, you can use meta() or raw_meta() without a field name:

{{ dump(post.meta()) }}
{{ dump(post.raw_meta()) }}

This is only recommended for development purposes, because it might affect your performance if you always request all values.

The meta() and raw_meta() methods work the same way for all Timber\Post, Timber\Term, Timber\User and Timber\Comment objects. You can read more about this in the Custom Fields Guide as well as the ACF Integrations Guide.

New classes #

New Attachment class #

Up until now, there was only a representation for WordPress image attachments in Timber. With version 2.0, we introduce a new Timber\Attachment class that represents WordPress attachments – including the ones that might not necessarily be images, like PDF files.

  • The Timber\Image class now extends the Timber\Attachment class. All your code should already be compatible with this change. But in the future, you could use the new Timber\Attachment class if you work with an attachment that is not an image.
  • We’ve added new methods for Timber\Attachment. See the section below.
  • To get attachments from attachment IDs in Twig, you can use {{ get_attachment(attachment_id) }}. Behind the curtains, Timber uses Class Maps to use the Timber\Image and Timber\Attachment classes for attachment posts. (@todo: Add link to documentation)

New PagesMenu class #

To handle cases where you wanted to build a menu from your existing pages separately, we added a Timber\PagesMenu class that will be used when you use the new Timber::get_pages_menu() function.

Factory classes #


Deprecated functions and variables #

The following functions are being deprecated and will be removed in a future version of Timber.

Timber\Timber #

  • get_context() - use context() instead.
  • Timber::$autoescape – use the timber/twig/environment/options filter instead.
  • Timber::$twig_cache – use the timber/twig/environment/options filter instead.
  • Timber::$cache – use the timber/twig/environment/options filter instead.

Timber\Post #

  • get_preview() - use excerpt() instead.
  • get_field() - use meta() instead.
  • import_field() - use meta() instead.
  • preview() - use excerpt() instead.
  • update() - use WordPress core’s update_post_meta() instead.

Timber\Image and Timber\Attachment #

  • get_pathinfo() – use pathinfo() instead.
  • get_dimensions() – use width() or height() instead.
  • get_dimensions_loaded() – use get_dimension_loaded() instead.
  • get_post_custom() – use meta() instead.

Timber\Term #

  • get_children() – use children() instead.
  • get_edit_url() – use edit_link() instead.
  • get_field() – use meta() instead.
  • get_meta_field() – use meta() instead.
  • get_posts() – use posts() instead.
  • update() - use WordPress core’s update_metadata() instead.

Timber\Comment #

  • get_field() - use {{ comment.meta('my_field_name') }} instead
  • get_meta_field() - use {{ comment.meta('my_field_name') }} instead
  • update() - use WordPress core’s update_metadata() instead.

Timber\MenuItem #

  • external() – use {{ item.is_external }} instead.
  • get_children() – use {{ item.children }} instead.
  • get_field() – use {{ item.meta }} instead.

Timber\User #

  • get_field() – use {{ user.meta('my_field_name') }} instead.
  • get_meta() – use {{ user.meta('my_field_name') }} instead.
  • get_meta_field() – use {{ user.meta('my_field_name') }} instead.

Timber\Site #

  • $pingback property – use $pingback_url.
  • meta() – use option() instead.
  • update() – use WordPress core’s update_blog_option() instead.
  • url() – use link() instead.

Timber\Archives #

  • get_items() – use items() instead.

Timber\Twig #

  • intl_date(), use Timber\DateTimeHelper::wp_date() instead.
  • time_ago(), use DateTimeHelper::time_ago() instead.

Timber\Loader #

  • template_exists() – No longer used internally.

Timber\LocationManager #

  • get_locations_user() – Use add_filter( 'timber/locations', $locations ) instead

Removed functions and properties #

The following functions were removed from the codebase, either because they were already deprecated or because they’re not used anymore.

Timber\Timber #

  • add_route() - The routes feature was completely removed in 2.0.
  • get_pagination() – Use {{ posts.pagination }} instead. Follow the Pagination Guide for more information.

Timber\Site #

  • get_link(), use {{ site.link }} instead
  • get_url(), use {{ site.link }} instead

Timber\Post #

  • get_author() – use {{ post.author }} instead
  • get_categories() – use {{ post.categories }} instead
  • get_category() – use {{ post.category }} instead
  • get_children() – use {{ post.children }} instead
  • get_comment_count() – use {{ post.comment_count }} instead
  • get_comments() – use {{ post.comments }} instead
  • get_content() – use {{ post.content }} instead
  • get_edit_url() – use link() instead
  • get_format() – use {{ post.format }} instead
  • get_image() – use {{ get_image(post.meta('my_image')) }} or {{ get_attachment(post.meta('my_file')) }} instead
  • get_link() – use {{ post.link }} instead
  • get_modified_author() – use {{ post.modified_author }} instead
  • get_modified_date() – use {{ post.modified_date }} instead
  • get_modified_time() – use {{ post.modified_time }} instead
  • get_next() – use {{ post.next }} instead
  • get_pagination() – use {{ post.pagination }} instead
  • get_parent() – use {{ post.parent }} instead
  • get_path() – use {{ post.path }} instead
  • get_permalink() – use {{ post.link }} instead
  • get_post_id_by_name()
  • get_post_type() – use {{ post.type() }} instead
  • get_prev() – use {{ post.prev }} instead
  • get_tags() – use {{ post.tags }} instead
  • get_terms() – use {{ post.term }} instead
  • get_thumbnail() – use {{ post.thumbnail }} instead
  • get_title() – use {{ post.title }} instead
  • init() – no replacement.
  • permalink() – use {{ post.link }} instead
  • prepare_post_info()

Timber\Term #

  • get_link() – use {{ term.link }} instead
  • get_path() – use {{ term.path }} instead

Timber\Image #

  • init() – only relevant if you’ve extended the Timber\Image class.
  • determine_id() – only relevant if you’ve extended the Timber\Image class.
  • get_attachment_info() – use get_info() instead.
  • get_src() – use {{ image.src }} instead
  • get_url() – use {{ image.src }} instead
  • url() – use {{ image.src }} instead

Timber\MenuItem #

  • get_link() – use {{ item.link }} instead
  • get_path() – use {{ item.path }} instead
  • permalink() – use {{ item.link }} instead

Timber\Comment #

Timber\CommentThread #

  • $CommentClass property – use Class Maps instead.

Timber\User #

  • $name property – use name() method instead. You can still use {{ user.name }} in Twig.

Timber\Helper #

  • function_wrapper() – use {{ function( 'function_to_call' ) }} instead
  • trim_words() – use TextHelper::trim_words() instead
  • close_tags() – use TextHelper::close_tags() instead
  • get_comment_form() – use {{ function('comment_form') }} instead
  • paginate_links() – use Pagination::paginate_links() instead
  • get_current_url() – use Timber\URLHelper::get_current_url() instead
  • filter_array() – use array_filter() or Timber\Helper::wp_list_filter() instead.

New functions #


  • context_global() - Gets the global context.
  • context_post() - Gets post context for a singular template.
  • context_posts() - Gets posts context for an archive template.
  • get_attachment_by() – Gets an attachment by its URL or absolute file path.
  • get_menu() – Gets a menu object for a specific menu.
  • get_pages_menu() – Gets a menu object built from your existing pages.
  • get_post_by() – Gets a post by title or slug.
  • get_user() – Gets a single user.
  • get_users() – Gets one or more users as an array.
  • get_user_by() – Gets a user by field.


  • raw_meta() – Gets a post meta value directly from the database.


  • raw_meta() – Gets a term meta value directly from the database.


  • raw_meta() – Gets a user meta value directly from the database.


  • raw_meta() – Gets a comment meta value directly from the database.


  • current_item() – Gets the current menu item. Read more about this in the functions’s documentation or the Menu Guide.
  • current_top_level_item() – Gets the top level parent of the current menu item.


  • target() – Gets the target of a menu item according to the «Open in new tab» option in the menu item options.
  • is_target_blank() – Checks whether the «Open in new tab» option checked in the menu item options in the backend.


  • size() - Gets the filesize of an attachment in bytes.
  • size_raw() - Gets the filesize of an attachment in a human readable format. E.g. 16 KB instead of 16555 bytes.
  • extension() - Gets the extension of the attached file.

New and updated functions #


  • get_post_by() – Gets a post by post slug or post title.


  • children() – We removed the $child_post_class parameter in this function. Use Class Maps instead to control which class to instantiate child posts with.
  • comments() – We removed the $CommentClass parameter in this function. Use Class Maps instead to control which class to instantiate child posts with.

Timber\Post::terms() #

The Timber\Term::terms() function already supported using an array of query arguments. But to make it more consistent with how other functions are used in Timber, we changed the signature to use two parameters: $query_args and $options.

Function Signature

// Old
function terms( $args = [], $merge = true, $term_class = '' );

// New
function terms( $query_args = [], $options = [] );

Old usage


$terms = $post->terms( 'category' );

// or

$terms = $post->terms( [
'query' => [
'taxonomy' => 'custom_tax',
'orderby' => 'count',
'merge' => false,
] );

And in Twig:

{% for term in post.terms('category') %}

{# or #}

{% for term in post.terms({
query: {
taxonomy: 'custom_tax',
orderby: 'count'
merge: false

New usage


$terms = $post->terms( [
'taxonomy' => 'category'
] );

// or

$terms = $post->terms( [
'taxonomy' => 'custom_tax',
'orderby' => 'count',
], [
'merge' => false,
] );

And in Twig:

{% for term in post.terms({ taxonomy: 'category' }) %}

{# or #}

{% for term in post.terms({
taxonomy: 'custom_tax',
orderby: 'count'
}, {
merge: false
}) %}

Timber\Term::posts() #

We changed the function parameters for the Timber\Term::posts() function to better support different use cases. Basically, you’ll use the same query parameters that you already know from WP_Query.

Function Signature

// Old
function posts( $numberposts_or_args = 10, $post_type_or_class = 'any', $post_class = '' );

// New
function posts( $query_args = [] );

Old usage


$genre->posts( -1, 'book' );

And in Twig:

{% for book in genre.posts(-1, 'book) %}

New usage


$genre->posts( [
'post_type' => 'book',
'post_per_page' => -1,
'orderby' => 'menu_order'
] );

And in Twig:

{% for book in genre.posts({
post_type: 'book',
posts_per_page: -1,
orderby: 'menu_order'
}) %}

You can still also pass an int as the sole argument, to tell it how many posts you want:

$genre->posts( 3 );

This is equivalent to:

$genre->posts( [
'posts_per_page' => 3,
'post_type' => 'any',
] );

Post Excerpts #

Excerpt instead of Preview #

We renamed the Timber\PostPreview class to Timber\PostExcerpt and also changed all instances of the term "preview" to "excerpt". The reason for this change is that the term "preview" in WordPress is mainly used for previewing a post before you save changes in the admin. For the short texts that are used in post teasers, the term "excerpt" is used. We wanted to follow the WordPress terminology here.

For this, we…

  • Renamed the Timber\PostPreview class to Timber\PostExcerpt.
  • Added a Timber\Post::excerpt() function that you should use instead of Timber\Post::preview().
  • Changed filter names.

We updated the logic for when read more links and end strings ( by default) will be added. For example, if the excerpt is generated from the post’s content, but the post’s content isn’t longer than the excerpt, then no read more link and no end string will be added.

You can control this behavior with these two new parameters for excerpts:

  • always_add_read_more – Controls whether a read more link should be added even if the excerpt isn’t trimmed (when the excerpt isn’t shorter than the post’s content). The default is false.
  • always_add_end – Whether the end string should be added even if the excerpt isn’t trimmed. The default is false.

Filter the defaults #

There’s a new timber/post/excerpt/defaults filter that can be used to update default options for excerpts, including always_add_read_more and always_add_end.

add_filter( 'timber/post/excerpt/defaults', function( $defaults ) {
// Only add a read more link if the post content isn’t longer than the excerpt.
$defaults['always_add_read_more'] = false;

// Set a default character limit.
$defaults['words'] = 240;

return $defaults;
} );

post.excerpt takes arguments in array/hash notation #

The {{ post.excerpt }} function was added as a replacement for {{ post.preview }}. When using {{ post.preview }}, you could pass in arguments by chaining them. It’s now possible to pass in arguments as an array in PHP or in hash style in Twig. This finally cleans up how the previews of posts are handled. Here’s an example:


$post->excerpt( [
'words' => 50,
'chars' => false,
'end' => '&hellip;',
'force' => false,
'strip' => true,
'read_more' => 'Read More',
] );


{{ post.excerpt({
words: 50,
chars: false,
end: "&hellip;",
force: false,
strip: true,
read_more: "Read More"
}) }}

Hooks #

In version 1.0, we already introduced some filters and actions that were namespaced with a timber/ prefix. In version 2.0, we refactored all filters to have the same naming standard. If you still use an old action or filter, you will see a warning with the name of the new hook. See the Hooks section in the documentation that lists all the current hooks. There, you’ll also see which hooks are deprecated.

Deprecated hooks #

You should update the following hooks because they will be removed in a future version of Timber. We use apply_filters_deprecated(), so you should get a proper warning when WP_DEBUG is set to true.


  • timber_render_file, use timber/render/file instead
  • timber_render_data, use timber/render/data instead
  • timber_compile_file, use timber/compile/file instead
  • timber_compile_data, use timber/compile/data instead
  • timber_compile_done, use timber/compile/done instead


  • timber_post_get_meta_field_pre, use timber/post/pre_meta instead
  • timber_post_get_meta_pre, use timber/post/pre_get instead
  • timber_post_get_meta_field, use timber/post/meta instead
  • timber_post_get_meta, use timber/post/meta instead


  • Timber\PostClassMap, use timber/post/classmap


  • timber/post/preview/read_more_class, use timber/post/excerpt/read_more_class instead
  • timber/post/get_preview/read_more_link, use timber/post/excerpt/read_more_link instead


  • timber/term/meta/field, use timber/term/meta instead
  • timber_term_get_meta_field, use timber/term/meta instead
  • timber_term_get_meta, use timber/term/meta instead


  • timber_comment_get_meta_field_pre, use timber/comment/pre_meta instead
  • timber_comment_get_meta_pre, use timber/comment/pre_meta instead
  • timber_comment_get_meta_field, use timber/comment/meta instead
  • timber_comment_get_meta, use timber/comment/meta instead


  • timber_user_get_meta_field_pre, use timber/user/pre_meta instead
  • timber_user_get_meta_pre, use timber/user/pre_meta instead
  • timber_user_get_meta_field, use timber/user/meta instead
  • timber_user_get_meta, use timber/user/meta instead


  • timber_site_set_meta, use timber/site/update_option instead


  • timber/cache/location, use an absolute path in the cache option in the timber/twig/environment/options filter instead.
  • timber/loader/paths, use timber/locations instead


The following filter names have changed to match the WordPress naming convention for hooks, which says that hooks should be all lowercase:

  • timber/URLHelper/url_to_file_system/path, use timber/url_helper/url_to_file_system/path instead
  • timber/URLHelper/file_system_to_url, use timber/url_helper/file_system_to_url instead
  • timber/URLHelper/get_content_subdir/home_url, use timber/url_helper/get_content_subdir/home_url instead

Deprecated without replacement #

The following filters were deprecated without a replacement and will be removed in the next major version:

  • The filters timber_term_set_meta and timber/term/meta/set were deprecated. They were used by Term::update(), which is now deprecated as well (without a replacement).

timber/term/meta #

If you’ve used timber/term/meta before, you might have to switch to timer/term/get_meta_fields. The timber/term/meta filter was introduced in 2.0 to be used instead of timber/term/meta/field and timber_term_get_meta_field. However, a filter timber/term/meta already existed in Term::get_meta_values() for version 1.0 and was renamed to timber/term/get_meta_fields to match the new naming conventions.

Removed hooks #

The following filters were removed without a replacement:

  • Timber\PostClassMap – use the Post Class Map instead.
  • timber/get_posts/mirror_wp_get_posts – This filter was used so that Timber::get_posts() mimicks the behavior of WordPress’s get_posts() function. We removed it because Timber::get_posts() is the new official API. If you want the same behavior, there are arguments that you can pass to this function.
  • timber_post_getter_get_posts – Remove because the PostGetter class doesn’t exist anymore.

New hooks #

We added new hooks that let you filter stuff you couldn’t filter before:

  • timber/term/classmap
  • timber/menu/classmap
  • timber/menuitem/classmap
  • timber/pages_menu/classmap
  • timber/user/classmap
  • timber/comment/classmap
  • timber/twig/environment/options

Escaping #

While Twig has escaping enabled by default, Timber doesn’t automatically enable espacing for Twig. To enable autoescaping in Timber 1.x, you would use Timber::$autoescape = true. The value true was deprecated for Twig 2.0, you now have to use html or another auto-escaping strategy instead. You’ll need to use the timber/twig/environment/options filter:

add_filter( 'timber/twig/environment/options', function( $options ) {
$options['autoescape'] = 'html';

return $options;
} );

Read more about this in the Escaping Guide.

Documentation #

We added a couple of new guides that you may want to read through in addition to this Upgrade Guide: