Adding variables preprocess
Adding Variables in Preprocess
When to Use
When you need to pass data from PHP to a Twig template that is not available through the default variable set.
Decision
| What to pass | How | Notes |
|---|---|---|
| Simple string/bool/int | Direct assignment: $variables['my_var'] = 'value' |
Available as {{ my_var }} |
| Translated string | $variables['label'] = t('My Label') |
Use t(), not string literal |
| Render array | $variables['block'] = ['#theme' => '...'] |
Prints via {{ block }} |
| Entity view | Use ViewBuilder: see pattern below | Preferred over raw render arrays |
| URL string | Url::fromRoute(...)->toString() |
Already a string |
| URL object | Url::fromRoute(...) |
Available as {{ url(route) }} context |
| Config value | \Drupal::config('system.site')->get('name') |
Cache metadata needed |
| Attribute object | new Attribute(['class' => ['my-class']]) |
Prints via {{ attrs }} |
Pattern
Passing rendered entity:
function mytheme_preprocess_node(&$variables) {
$node = $variables['node'];
// Load and render a related entity
if ($node->hasField('field_author_profile') && !$node->field_author_profile->isEmpty()) {
$profile = $node->field_author_profile->entity;
if ($profile && $profile->access('view')) {
$view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
$variables['author_card'] = $view_builder->view($profile, 'card');
}
}
// Pass a simple computed value
$variables['word_count'] = str_word_count(strip_tags($node->body->value ?? ''));
// Pass an Attribute object for a custom element
$variables['card_attributes'] = new Attribute([
'class' => ['card', 'card--' . $node->bundle()],
'data-nid' => $node->id(),
]);
}
Cache metadata for custom variables (critical):
// If you add data from an entity or config, bubble its cache metadata
use Drupal\Core\Cache\CacheableMetadata;
function mytheme_preprocess_node(&$variables) {
$config = \Drupal::config('mymodule.settings');
$variables['show_badge'] = $config->get('show_badge');
// Bubble config cache metadata so page cache invalidates correctly
CacheableMetadata::createFromRenderArray($variables)
->addCacheableDependency($config)
->applyTo($variables);
}
Common Mistakes
- Adding data from external sources (config, entities) without bubbling cache metadata → stale cache issues
- Passing a raw entity object as a variable and then trying to render it directly → pass a render array, not the entity
- Using
\Drupal::service()in preprocess for services that should be injected → fine in.themefiles, avoid in modules - Passing large arrays or fully loaded entity lists → load lazily or only what's needed for the current view mode
- Forgetting that default variables (
attributes,title_attributes, etc.) are set byThemeManager::getDefaultTemplateVariables()and can be modified in preprocess
See Also
- Preprocess layers → Preprocess Functions
- Render arrays reference →
drupal-render-api.md - Cache metadata →
drupal-render-api.md