In my_module I have the following:

function my_module_token_info()
  $info = [];
  $info['types']['my_module_tokens'] = [
      'name' => 'My Module Tokens',
      'description' => t('Tokens specific to my module.'),
  $info['tokens']['my_module_tokens']['my_token'][] = 'For my token in a node body';

  return $info;

function my_module_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];

  if ($type == 'my_module_tokens') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'my_token':
          $replacements[$original] = 'test';


  return $replacements;

In a node body I have [my_module_tokens:my_token]

Now, the token info is found and processed, and appears in the token list. The implementation of hook_tokens does not fire unless I add the following:

function my_module_preprocess_node(&$variables) {
  $token = Drupal::token();
  $token->replace("[my_module_tokens:my_token]", array('node'=>$variables['node']), array('clear' => TRUE));

The token information and replacement text are sent off to the tokens logic, but the result is that the token in the body text is unchanged.

  • Does your token work in other places, like a url pattern? I.e. is it only not working in your body field? Do other tokens work in the body field?
    – sonfd
    Dec 29, 2020 at 1:33
  • I'm not using the token anywhere else. It's meant (once it's firing and I add the rest of the logic) to insert an AdSense in-content ad. But getting the answer to your second question surprised me. I used the core token [user:mail] just below the other, and it also is not replaced.
    – JAyenGreen
    Dec 29, 2020 at 1:46

2 Answers 2


You need to add a way to process the token. The Token Filter module provides a ckeditor filter plugin to do this when the field is rendered.

From the module's README:

Install the module as any other module. Visit the text format administration page at /admin/config/content/formats/filters and edit a text format. Check the 'Replaces global tokens with their value' filter and save the text format.

When editing a form where this text format is used in a field, you can type global tokens that will be replaced when the field is rendered.


Ah, got it. I had my brain in backwards. Token replacement is on node save, not when it's rendered. Calling token replace in hook_node_presave did it.

  • I think you probably want to use a ckeditor filter plugin for this.
    – sonfd
    Dec 29, 2020 at 12:38
  • Sure, could, but I’d still have to write the logic that does the replacement of the token with text. Ckeditor would mostly just give me the dropping in of the token via a button.
    – JAyenGreen
    Dec 29, 2020 at 13:16
  • 1
    If you process the token in a presave, you'll actually store the processed value in the database. Imo, this defeats the point of a token.
    – sonfd
    Dec 29, 2020 at 13:24
  • Could go either way. It really depends on the specific token, whether it is just to save looking something up or a few keystrokes and isn’t likely to ever change, so that doing the replacement before ever render is a waste, or whether it makes sense to do so because it’s replacement is in any way needing a current dynamic value. Good point. I’ll put your comment as an answer and I’ll pick it instead, as most often it would apply even if not in this case.
    – JAyenGreen
    Dec 29, 2020 at 13:30
  • PS I think the down vote on the question was unnecessary. Wanting to know how to accomplish something via native Drupal without using a contrib module isn’t a bad thing. Contrib modules come and expire, while the business case remains.
    – JAyenGreen
    Dec 29, 2020 at 13:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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