Think of Drupal's token system as a mail-merge feature for your whole site: instead of hardcoding "Dear John" into a thousand emails, you write "Dear [user:name]" once and let Drupal fill in the real value at the last possible moment. Tokens are exactly that — small bracketed placeholders like [node:title] or [user:mail] that get swapped for real data wherever a site builder is allowed to type text: email templates, page titles, URL patterns, block content. This lesson covers the first half of adding your own: telling Drupal your tokens exist in the first place, with hook_token_info().
What you'll learn in this lesson
- What a token actually is, and where site builders encounter them
- How
hook_token_info()registers a new token type and the individual tokens within it - Why every token name and description must be wrapped in
t() - How to confirm your new tokens actually appear in the UI
Registering a token type and its tokens
hook_token_info() doesn't replace anything by itself — it's purely a catalog entry. It tells Drupal "here's a group of tokens called example, and here's what each one inside it is called and what it does." The actual substitution logic lives in a separate hook you'll meet in the next lesson.
/**
* Implements hook_token_info().
*/
function example_token_token_info() {
$info = [];
// Register a new token TYPE — a named group tokens live under.
$info['types']['example'] = [
'name' => t('Example tokens'),
'description' => t('Tokens related to the example module.'),
];
// Register the individual tokens inside that type.
$info['tokens']['example']['username'] = [
'name' => t('Username'),
'description' => t('The name of the current user.'),
];
$info['tokens']['example']['user_email'] = [
'name' => t('User email'),
'description' => t('The email address of the current user.'),
];
return $info;
}
The array has two top-level keys: types (one entry per token group you're introducing) and tokens (keyed by the type machine name, then one entry per individual token). Once this is registered, editors will see [example:username] and [example:user_email] as selectable options anywhere Drupal's token browser appears — even though, at this point, nothing has told Drupal what value to substitute yet.
hook_token_info(), run drush cache-rebuild (or clear caches from the admin UI) before it shows up anywhere — otherwise you'll be staring at code that looks correct and wondering why nothing changed.Seeing your tokens in the UI
With the core Token module enabled, any field that supports tokens (most configurable text areas — email templates, URL alias patterns, block content) shows a "Browse available tokens" link. Your new example group and its two tokens should appear there immediately after the cache rebuild, each with the name and description you supplied — which is exactly why wrapping both in t() matters: those strings are user interface text, not internal identifiers, and need to be translatable like anything else an administrator reads.
Quick check: if you deploy this code to production but a site builder reports that
[example:username]just shows up as literal text instead of a real name, is that a bug inhook_token_info()? (Not necessarily —hook_token_info()only registers that the token exists and is selectable. Whether it actually gets replaced with real data is the job ofhook_tokens(), covered next. Registering a token and implementing its replacement are two separate steps, and it's easy to do one without the other.)
Key takeaways
hook_token_info()only catalogs tokens — it doesn't replace them with real values, that'shook_tokens()'s job.- The return array has two keys:
typesfor the token group,tokensfor the individual entries inside it. - Every
nameanddescriptionstring must go throught()— this is user-facing UI text. - Newly registered tokens won't appear anywhere until you rebuild caches — token discovery is cached like most Drupal hook discovery.
- A token showing up as literal, unreplaced bracket text in rendered output almost always means the replacement hook is missing or not matching, not that registration failed.
Coming up next
Right now your tokens exist but do nothing — pick one in the browser and it just sits there as inert bracket syntax if actually rendered. Next, you'll implement hook_tokens(), the companion hook that actually looks up real data and performs the substitution, bringing [example:username] to life as the current user's real name.