Filters allow you to change interpreted data conditionally.
We recommend against using filters if you aren’t an avid PHP developer. This is because you must revalidate (and possibly update) your implementation on every major release, although that shouldn’t happen often.
As of v5.0.0, The SEO Framework registers over 205 filters.
Below you’ll find frequently requested filter implementations. Most examples below only work on TSF v5.0.0 or higher. It’s not an exhaustive list, because many filters now have options and only exist for backward compatibility reasons. Advanced filters, such as those used for script loading, aren’t listed here either. For these, we urge you to review the code.
If you don’t know how filters work or where to place them, read our filter guide first.
Query related
The query-related filters affect how The SEO Framework interacts with the interface.
Disable SEO support
N.B. This example filter does not affect the admin interface.
add_filter(
'the_seo_framework_query_supports_seo',
function ( $supported ) {
$tsf = tsf();
// This TSF method also works in the admin area, and prevents ID collision with terms.
if ( $tsf->query()->is_singular() ) {
// Define your excluded page IDs here.
$excluded_ids = [ 42, 9001 ];
// This TSF method can more accurately get the request ID than the standard API.
$current_id = $tsf->query->get_the_real_id();
if ( in_array( $current_id, $excluded_ids, true ) ) {
$supported = false;
}
}
return $supported;
},
);
Title related
The title-related filters affect all functions that make use of the titles. This includes (and is not limited to) the meta title, generated descriptions, social metadata, and structured data (Schema.org).
Run shortcodes in the custom title.
N.B. This example filter is for custom titles, not generated titles. You can use them interchangeably.
add_filter(
'the_seo_framework_title_from_custom_field',
function ( $title, $args ) {
return apply_shortcodes( $title );
},
10,
2,
);
Set generated title prefix
Set an “Antique: ” title-prefix for all WooCommerce products.
Please note that the title input fields will not consider this filter’s output well, but the front-end will. It’s tricky considering moving parts, such as updating a title in the Block Editor.
N.B. This example filter is for generated titles, not custom titles. You can use them interchangeably.
add_filter(
'the_seo_framework_title_from_generation',
function ( $title, $args ) {
$tsf = tsf();
if ( null === $args ) {
// We're in the loop.
if (
$tsf->query()->is_singular()
&& 'product' === $tsf->query()->get_current_post_type()
) {
$title_prefix = 'Antique: ';
}
} else {
// We're on an admin page or generating a breadcrumb.
if (
'single' === The_SEO_Framework\get_query_type_from_args( $args )
&& 'product' === $tsf->query()->get_post_type_real_id( $args['id'] )
) {
$title_prefix = 'Antique: ';
}
}
if ( isset( $title_prefix ) ) {
// Sanitizing isn't necessary. TSF will do that for you.
$title = "$title_prefix $title";
}
return $title;
},
10,
2,
);
Remove title branding on all WooCommerce product categories.
N.B. The use of this filter is not recommended because search engines tend to ignore unbranded titles.
add_filter(
'the_seo_framework_use_title_branding',
function ( $use, $args ) {
// Get taxonomy with admin support.
$taxonomy = null === $args
? tsf()->query()->get_current_taxonomy()
: $args['taxonomy'];
// If taxonomy is a WooCommerce product category, disable the branding.
if ( 'product_cat' === $taxonomy ) {
$use = false;
}
return $use;
},
10,
2,
);
Description related
The description-related filters affect all functions that make use of the description. This includes (and is not limited to) the meta description, social metadata, and structured data (Schema.org).
Run shortcodes in the custom description.
add_filter(
'the_seo_framework_custom_field_description',
function ( $description, $args ) {
return apply_shortcodes( $description );
},
10,
2,
);
Use long descriptions instead of short descriptions for WooCommerce products
add_filter(
'the_seo_framework_description_excerpt',
function ( $excerpt, $args ) {
$tsf = tsf();
$tsf_query = $tsf->query();
if ( null === $args ) {
// In the loop.
$is_product = $tsf_query->is_product();
} else {
// Out the loop, admin etc.
if ( 'single' === The_SEO_Framework\get_query_type_from_args( $args ) ) {
$is_product = $tsf_query->is_product( $args['id'] );
}
}
if ( $is_product ?? false ) {
$product = get_post( $args['id'] ?? $tsf_query->get_the_real_id() );
$product_id = $product->ID;
$tsf_post = $tsf->data()->post();
// Don't leak protected content.
if ( $tsf_post->is_protected( $product_id ) )
return $excerpt;
$old_excerpt = $excerpt;
// Don't get the excerpt from a non-HTML page builders. This cannot be deciphered early.
$excerpt = $tsf_post->uses_non_html_page_builder( $product_id )
? ''
: $tsf_post->get_content( $product );
if ( $excerpt ) {
$tsf_html = $tsf->format()->html();
// Apply the first half of TSF's description AI here: selectively strip content and HTML.
$excerpt = $tsf_html->strip_paragraph_urls( $tsf_html->strip_newline_urls( $excerpt ) );
$excerpt = $tsf_html->extract_content( $excerpt );
}
// Fall back to old excerpt if the new one has nothing useful.
$excerpt = $excerpt ?: $old_excerpt;
}
return $excerpt;
},
10,
2,
);
Robots related
The robots-related filters can be used to direct crawlers, like Googlebot.
Set noindex and nofollow for all post tags
add_filter(
'the_seo_framework_robots_meta_array',
function ( $meta, $args, $options ) {
// Get taxonomy with admin support.
$taxonomy = null === $args
? tsf()->query()->get_current_taxonomy()
: $args['taxonomy'];
// If taxonomy is a Post Tag, disable indexing and link following.
if ( 'post_tag' === $taxonomy ) {
$meta['noindex'] = 'noindex';
$meta['nofollow'] = 'nofollow';
}
return $meta;
},
10,
3,
);
Add robots.txt rules
N.B. The SEO Framework overwrites the dynamic file to prevent plugin conflicts. So, you should always set your callback priority to a greater value than 10.
// This is a WordPress Core filter.
add_filter(
'robots_txt',
function ( $robots ) {
$robots .= "\nDisallow: /my-custom-folder/\n";
return $robots;
},
11,
);
Image related
The image-related filters affect social metadata as well as structured data.
Append image generators for social images
This filter is advanced and powerful. For real-world usage, see this issue comment and this issue comment.
add_filter(
'the_seo_framework_image_generation_params',
function ( $params, $args, $context ) {
// Let's not mess with non-social sharing images.
if ( 'social' !== $context ) return $params;
if ( null === $args ) {
// In the loop.
if ( is_singular() ) {
// Append. Use array_unshift() on 'cbs' to prepend.
$params['cbs']['custom'] = 'my_tsf_custom_singular_image_generator';
}
} else {
// Out the loop. Use $args to evaluate the query...
if ( 'single' === The_SEO_Framework\get_query_type_from_args( $args ) ) {
// Singular.
// Append. Use array_unshift() on 'cbs' to prepend.
$params['cbs']['custom'] = 'my_tsf_custom_singular_image_generator';
}
}
return $params;
},
10,
3,
);
function my_tsf_custom_singular_image_generator( $args = null, $size = 'full' ) {
// Arbitrary URL and ID.
$url = 'https://example.com/path/to/image/';
$id = 42;
if ( $url ) {
yield [
'url' => $url,
'id' => $id, // Optional. Used for alt-tag fetching and dimension testing.
];
}
}
Sitemap related
The sitemap is sufficient for all websites as-is. The sitemap doesn’t need to have all URLs of your website listed.
“Improving” the sitemap is utterly redundant… but, go ahead.
Add categories and tags to base sitemap
add_filter(
'the_seo_framework_sitemap_additional_urls',
function ( $custom_urls ) {
// Set the taxonomy types you'd like to include.
$taxonomies = [
'category',
'post_tag',
];
$tsf = tsf();
// Sitemaps can loop over thousands of items. Run as little as possible in the loop: cache objects.
$tsf_taxonomy = $tsf->taxonomy();
$tsf_sitemap_utils = $tsf->sitemap()->utils();
$tsf_uri = $tsf->uri();
foreach ( $taxonomies as $tax ) {
// When the taxonomy is disabled or isn't public, don't include it.
if ( ! $tsf_taxonomy->is_supported( $tax ) ) continue;
$terms = get_terms( [
'taxonomy' => $tax,
'hide_empty' => true,
] );
if ( is_wp_error( $terms ) ) continue;
foreach ( $terms as $term ) {
if ( ! $tsf_sitemap_utils->is_term_included_in_sitemap( $term->term_id, $term->taxonomy ) )
continue;
$url = $tsf_uri->get_term_url( $term->term_id, $term->taxonomy );
$custom_urls[ $url ] = [
'lastmod' => null, // difficult to determine accurately.
];
}
}
return $custom_urls;
},
);
List edit settings related
These filters are applicable to the post and term overview pages.
Hide quick-and bulk-edit
add_action(
'the_seo_framework_after_admin_init',
function () {
// Hide quick and bulk-edit.
remove_action( 'admin_init', [ The_SEO_Framework\Admin\Settings\ListEdit::class, 'init_quick_and_bulk_edit' ] );
// Hide custom post states.
remove_filter( 'display_post_states', [ The_SEO_Framework\Admin\Lists\PostStates::class, 'add_post_state' ] );
},
);
Post SEO settings related
The post SEO settings are placed in a meta box on each public post type edit screen.
Change meta box priority
N.B. Only new users are affected by this filter. Users can drag and position the meta box to their liking, the location is stored and locked as a user-setting.
add_filter(
'the_seo_framework_metabox_priority',
function () {
return 'default'; // Accepts 'high', 'default', 'low'. Default is 'high'.
},
);
Move meta box to the sidebar
N.B. Only new users are affected by this filter. Users can drag and position the meta box to their liking, the location is stored and locked as a user-setting.
add_filter(
'the_seo_framework_metabox_context',
function () {
return 'side'; // Accepts 'normal', 'side', and 'advanced'. Default is 'normal'.
},
);
SEO Bar related
The SEO Bar helps you see what needs attention for each page. Since TSF v4.0, we provide an API. These are actions, not filters, but you’re probably reading this page because you want to adjust things — to which filters make more sense.
Register custom SEO Bar item
add_action(
'the_seo_framework_seo_bar',
function ( $interpreter, $builder ) {
/**
* This is a collector method. However, it's best to use
* `register_seo_bar_item()` and `edit_seo_bar_item()` instead.
*/
$items = $interpreter::collect_seo_bar_items();
// Don't do anything if there's a blocking redirect.
if ( ! empty( $items['redirect']['meta']['blocking'] ) ) return;
// Add a custom item:
$interpreter::register_seo_bar_item(
'something',
[
'symbol' => 'S',
'title' => __( 'Some assessment', 'my-text-domain' ),
'status' => $interpreter::STATE_BAD,
'reason' => __( 'Something bad happened.', 'my-text-domain' ),
'assess' => [
'something' => __( 'Something is not right.', 'my-text-domain' ),
'else' => __( 'Something else is not right either.', 'my-text-domain' ),
],
]
);
},
10,
2,
);
Adjust SEO Bar item
add_action(
'the_seo_framework_seo_bar',
function ( $interpreter, $builder ) {
/**
* This is a collector method. However, it's best to use
* `register_seo_bar_item()` and `edit_seo_bar_item()` instead.
*/
$items = $interpreter::collect_seo_bar_items();
// Don't do anything if there's a blocking redirect.
if ( ! empty( $items['redirect']['meta']['blocking'] ) ) return;
// Edit known items. Warning: Advanced magic! Know your PHP.
// NB: If the item isn't registered, this won't produce errors, but all changes will be voided.
$index_item = &$interpreter::edit_seo_bar_item( 'indexing' );
$index_item['status'] = $interpreter::STATE_UNKNOWN;
$index_item['reason'] = __( 'Robots.txt blocks all crawlers.', 'my-text-domain' );
$index_item['assess'] = []; // clear all assessments... be considerate!
$index_item['assess']['plugin'] = __( 'This is a developer site. Plugin "MyNoRobots - Robots.txt Robot Blocker" is activated.', 'my-text-domain' );
},
10,
2,
);