I use Twig and Twig Tweak to do most of the processing. However, from time to time I need to rely on Preprocess functions to get the desired result.

This led me to a situation where now most of my variables are created in a Preprocess function before handing them over to Twig (this keeps my templates clean, simple and most of all easy to read). I also don't like my code being scattered between Templates and Theme functions.

But regarding Drupal Documentation, this method seems discouraged.

function mytheme_preprocess_node(&$variable) {
 if ($variable['node']->getType() === 'my_content_type_machine_name') {
 $variable['my_title'] = $variable['node']->field_my_title->value;

You may now use the more concise {{ my_title }} in your twig file to refer to {{ node->field_my_title->value }}.

However, while this might be possible, it is discouraged to create new variables in this manner. Using {{ node.field_my_title.value }} is fairly reasonable to type and avoids any potential issues and should be preferred.

So basically my question is: Are there any simple rules to follow regarding Twig versus Preprocess functions for additional logic?

  • 2
    I'm with you. But it's an opinion-based thing. How ugly does that look when you have {{ file_url(content.field_custom_image[0]['#media'].field_media_image.entity.uri.value) }} with maybe a couple of ifs and else around it when you could simple preprocess it and have {{ my_file_url }} instead? In my opinion templates should be kept clean and logic should be kept out of them. Heavy templating involves the danger of invalidating the UI. I find it important that the UI is kept intact and instead use custom field formatters, view modes and pseudo/extra fields which can be set in the backend.
    – leymannx
    Feb 9 at 17:02
  • 2
    @leymannx, this is indeed a bad example, because this is referencing a render array and not an object like in the linked documentation.
    – 4k4
    Feb 9 at 17:07
  • 2
    I agree and don't like code scattered between preprocess and templates - you're always going to have to make a judgement call for what's right in a situation. You can always help mitigate any confusion about where a variable is created by clearly documenting it in your twig template, e.g. my_special_var: Special var, see my_module_preprocess() - you see this in core's twig templates.
    – sonfd
    Feb 9 at 18:25

Best practice is documented in the node template. These are the two most important variables:

* Available variables:
 * - node: The node entity with limited access to object properties and methods.
 *   Only method names starting with "get", "has", or "is" and a few common
 *   methods such as "id", "label", and "bundle" are available. For example:
 *   - node.getCreatedTime() will return the node creation timestamp.
 *   - node.hasField('field_example') returns TRUE if the node bundle includes
 *     field_example. (This does not indicate the presence of a value in this
 *     field.)
 *   - node.isPublished() will return whether the node is published or not.
 *   Calling other methods, such as node.delete(), will result in an exception.
 *   See \Drupal\node\Entity\Node for a full list of public properties and
 *   methods for the node object.
 * - label: (optional) The title of the node.
 * - content: All node items. Use {{ content }} to print them all,
 *   or print a subset such as {{ content.field_example }}. Use
 *   {{ content|without('field_example') }} to temporarily suppress the printing
 *   of a given child element.

It clearly states that you should print the render array {{ content }} and use the object {{ node }} with its properties and methods to get the node data you need.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.