Skip to main content

Template tags

Template tags are written %%tag%% and can be used in any title or description field — the site-wide templates in Settings → Titles & Meta, and a single page's own SEO title or description.

In the editor they appear as chips rather than raw text. Use the buttons above each field to insert the common ones, or View all tags to browse everything with example values for the page you are editing.

A tag that resolves to nothing disappears along with the separator holding its place, so %%title%% %%sep%% %%primary_category%% %%sep%% %%sitename%% on an uncategorised post renders My Post | My Site — not My Post | | My Site.

An unrecognised tag renders as nothing rather than printing itself, so a typo never leaks %%titel%% into a search result.


Site

TagWhat it renders
%%sitename%%The site title from Settings → General
%%sitedesc%%The site tagline
%%sep%%The separator chosen in the SEO settings
%%permalink%%The URL of the current page
%%currentdate%%Today, in the site date format
%%currenttime%%The current time
%%currentday%%The day of the month
%%currentmonth%%The month name, translated
%%currentyear%%The current year — useful for keeping a title evergreen

The current page

TagWhat it renders
%%title%%Title of the post, page or term
%%excerpt%%The excerpt, generated from the content when none is set
%%excerpt_only%%The hand-written excerpt only, empty if there is none
%%post_content%%The body text, as plain prose
%%date%%Publish date
%%modified%%Last modified date
%%id%%Numeric post ID
%%parent_title%%Title of the parent page
%%post_type_singular%%Singular label, e.g. Project
%%post_type_plural%%Plural label, e.g. Projects
%%primary_category%%The primary category, or the first one
%%post_categories%%All categories, comma separated
%%post_tags%%All tags, comma separated

Terms and archives

TagWhat it renders
%%term_title%%Name of the current category, tag or term
%%term_description%%Its description
%%taxonomy_title%%Label of the taxonomy, e.g. Categories
%%archive_title%%The title of whatever archive is being viewed
%%archive_date%%The period a date archive covers, e.g. March 2026

Author

TagWhat it renders
%%author_name%%Display name
%%author_bio%%Biographical info
%%author_id%%Numeric user ID

Search and paging

TagWhat it renders
%%searchphrase%%What the visitor searched for
%%page%%Page 2 of 7 — and nothing on page one, so it collapses away
%%pagenumber%%The current page number
%%pagetotal%%How many pages there are

Dynamic tags

These read data the framework has never heard of, so you are not limited to the list above.

PatternWhat it readsExample
%%cf_<key>%%Any custom field on the current post%%cf_subtitle%%
%%term_cf_<key>%%Any custom field on the current term%%term_cf_colour%%
%%tax_<taxonomy>%%The post's terms in any taxonomy%%tax_product_cat%%
%%user_<key>%%Any field on the author's profile%%user_twitter%%

Modifiers

Modifiers post-process a tag's value. Chain them with |, applied left to right.

ModifierWhat it does
truncate:<n>Trim to n characters, always on a word boundary
words:<n>Keep the first n words
upperUPPERCASE
lowerlowercase
capitalizeTitle Case
%%excerpt|words:20%%
%%title|truncate:40%%
%%excerpt|words:25|capitalize%%

Adding your own

Register a tag from a theme or another extension:

add_action( 'fw_seo_register_tags', function () {
FW_SEO_Tags::register( 'reading_time', [
'label' => __( 'Reading time', 'fw' ),
'group' => 'post',
'contexts' => [ FW_SEO_Context::SINGULAR ],
'resolve' => function ( FW_SEO_Context $ctx ) {
if ( ! $ctx->has_post() ) {
return '';
}

$words = str_word_count( FW_SEO_Content::text( $ctx ) );

return sprintf( '%d min read', max( 1, (int) ceil( $words / 200 ) ) );
},
] );
} );

Resolvers are lazy — yours only runs if a template actually uses the tag — and receive the resolved page context rather than having to work it out from globals. Returning an empty string is fine and expected: the tag and its separator simply collapse away.