Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

CacheableDependencyInterface

13/04/2025, by Ivan

The CacheableDependencyInterface in Drupal 8 simplifies working with cache tags, cache contexts, and max-age.

Why?

Imagine having to manually define cache tags for every object and configuration used in a render array (or any other calculation). On multilingual sites, you'd also need to add the appropriate cache contexts manually (for translated entities or configuration overrides).

This was the case early in Drupal 8's development — it was unreliable and error-prone.

That's why the CacheableDependencyInterface was introduced. As the name suggests, objects implementing this interface can automatically become cacheable dependencies.

For example, when building a render array for <p>Hi, %user, welcome to %site!</p>, you depend on both the User entity and the system.site configuration. When the render array is cached, it includes these as cacheable dependencies.

CacheableDependencyInterface can be implemented by any value object (a data logic unit). See the API docs — it's implemented by many meaningful objects in Drupal core. You can safely assume it's used by most objects you interact with when writing Drupal 8 code.

There are two common edge cases, with handy traits: one for immutable (permanently cacheable) objects — UnchangingCacheableDependencyTrait (max-age = permanent), and another for dynamic, never-cacheable objects — UncacheableDependencyTrait (max-age = 0).

RefinableCacheableDependencyInterface

CacheableDependencyInterface only supports "canonical" cacheability metadata. Sometimes an object has multiple variants.

Examples include entity translations (same entity ID, different translation) and config translations (same name, but language override).

The original object's cacheability metadata (e.g., node:5 tag) remains valid. But the translated entity needs a cache context like 'languages:' . LanguageInterface::TYPE_CONTENT. Likewise, translated config needs 'languages:' . LanguageInterface::TYPE_INTERFACE.

Another example: a "Pirate Day" module that overrides config with pirate-themed terms on certain days might add a pirate_day cache context.

In all these cases, we refine the object’s cacheability metadata. That’s what RefinableCacheableDependencyInterface enables — it allows adding cache tags, contexts, or updating max-age.

To simplify implementation, there's RefinableCacheableDependencyTrait.

Entities and Configuration Objects

All entities in Drupal 8 implement EntityInterface, which extends both CacheableDependencyInterface and RefinableCacheableDependencyInterface. They also extend Entity, the base class. This ensures every entity has cache tags (like node:5 or user:3) and proper cache contexts for translation.

All config objects in core extend ConfigBase, which implements the same interfaces. Thus, config objects also get consistent cache tags (like config:system.performance) and translation-related cache contexts.

Translation contexts for entities and config are handled automatically via EntityManager::getTranslationFromContext() and LanguageConfigFactoryOverride::getCacheableMetadata().

Using Objects as Cacheable Dependencies

Rendering is the most common case where objects are cacheable dependencies. Use RendererInterface::addCacheableDependency($build, $dependency) — it absorbs the object’s metadata into the render array. This means the render array is invalidated correctly if the object’s cache tags are invalidated, translation varies, or max-age expires.

See a complete example here.

Another example is access checking via AccessResult objects. Use AccessResult::addCacheableDependency($dependency) — it adds the object’s cacheability directly.

Related Interfaces and Classes

See Also