register_taxonomy()WP 2.3.0

Creates a new custom WordPress taxonomy. Allows you to change an existing taxonomy.

The function allows to add (register) custom WP taxonomy or change data of taxonomy which was already registered.

When you change an existing taxonomy, the value of the $object_type parameter from the original registration will be overwritten. Therefore, it is important to re-specify it as it was or a new one.

Taxonomies are the ability to classify objects, such as headings for posts. Read more about taxonomies.

It is recommended to call the function on the hook init:

add_action( 'init', 'function_name' );

function function_name(){
	register_taxonomy( ... );
}

Reserved names

You cannot use reserved WordPress names in the taxonomy name ($taxonomy parameter) or in the 'query_var' parameter. For example, you cannot use author as name of taxonomy.

This is especially important if you are passing the name via a $_GET or $_POST array. This can cause WordPress to respond with a 404 error without any other explanations.

List of names You cannot use:

attachment
attachment_id
author
author_name
calendar
cat
category
category_name
custom
customize_messenger_channel
customized
cpage
day
debug
embed
error
exact
feed
fields
hour
link_category
m
minute
monthnum
more
name
nav_menu
nonce
nopaging
offset
order
orderby
p
page
page_id
paged
pagename
pb
perm
post
post_format
post_mime_type
post_status
post_tag
post_type
posts
posts_per_archive_page
posts_per_page
preview
robots
s
search
second
sentence
showposts
static
status
subpost
subpost_id
tag
tag_id
taxonomy
tb
term
terms
theme
title
type
types
w
withcomments
withoutcomments
year

Full list of reserved names (blacklist), which cannot be used as a new taxonomy name or in query-related parameters.

It is recommended to register the taxonomy first, and then the post type to which that taxonomy is linked!

This feature will save you bugs and a lot of wasted time in some cases.

// correct order of registering a post type and its taxonomy
register_taxonomy( ... );
register_post_type( ... );

You can tie the taxonomy to the post type later - after calling this function, using register_taxonomy_for_object_type().

Set the show_in_rest = true parameter to make the taxonomy panel appear in the sidebar of the block editor (Gutenberg).

Use register_post_type() when you want to register a new post type.

A handy plugin that lets you register new post types and taxonomies: Custom Post Type UI

Return

WP_Taxonomy|WP_Error. Registered taxonomy object WP_Taxonomy on success. Or WP_Error object on failure.

Usage Tempalte

add_action( 'init', 'create_my_taxonomy' );

function create_my_taxonomy(){

	register_taxonomy( 'taxonomy', [ 'post' ], [
		'label'                 => '', // Default taken from $labels->name
		// Full list: wp-kama.com/function/get_taxonomy_labels
		'labels'                => [
			'name'              => 'Genres',
			'singular_name'     => 'Genre',
			'search_items'      => 'Search Genres',
			'all_items'         => 'All Genres',
			'view_item '        => 'View Genre',
			'parent_item'       => 'Parent Genre',
			'parent_item_colon' => 'Parent Genre:',
			'edit_item'         => 'Edit Genre',
			'update_item'       => 'Update Genre',
			'add_new_item'      => 'Add New Genre',
			'new_item_name'     => 'New Genre Name',
			'menu_name'         => 'Genre',
			'back_to_items'     => '← Back to Genre',
		],
		'description'           => '',
		'public'                => true,
		// 'publicly_queryable'    => null, // same as argument public
		// 'show_in_nav_menus'     => true, // same as argument public
		// 'show_ui'               => true, // same as argument public
		// 'show_in_menu'          => true, // same as argument show_ui
		// 'show_tagcloud'         => true, // same as argument show_ui
		// 'show_in_quick_edit'    => null, // same as argument show_ui
		'hierarchical'          => false,

		'rewrite'               => true,
		//'query_var'           => $taxonomy, // query parameter name
		'capabilities'          => array(),
		'meta_box_cb'           => null, // metabox html. callback: `post_categories_meta_box` or `post_tags_meta_box`. false - the metabox is disabled.
		'show_admin_column'     => false, // auto-creation of a posts table column for the associated post type.
		'show_in_rest'          => null, // add to the REST API
		'rest_base'             => null, // $taxonomy
		// '_builtin'              => false,
		//'update_count_callback' => '_update_post_term_count',
	] );
}

Usage

register_taxonomy( $taxonomy, $object_type, $args );
$taxonomy(string) (required)
The name of the taxonomy to be created.
Can contain only lowercase Latin characters, numbers and _, i.e. a-z0-9_. Taxonomy name length should be between 1 and 32 characters (database limitation).
$object_type(string/array) (required)

The name of the post types to which the taxonomy will be attached.

In this parameter, for example, you can specify post, then ordinary WordPress posts will have a new taxonomy.

The names of post types must be written in lower case and without spaces. The maximum length allowed is 20 characters.

  • null - Setting null registers the taxonomy, but does not associate it with any object, so it will not be available in the admin panel interface. You can associate taxonomy with a post type later when for example registering a post type: see 'taxonomy' parameter in register_post_type(). Or use register_taxonomy_for_object_type() to associate taxonomy with an object.

Built-in WP post types:

  • post
  • page
  • attachment
  • revision
  • nav_menu_item
  • custom_css
  • customize_changeset
$args(string/array/object)
Arguments (parameters) of taxonomy. Arguments can be specified as a string, then they will be processed by wp_parse_args() function.
Default: array()

Arguments of the $args parameter

label(string)
Taxonomy name in plural (for display in the admin panel).
Default: the value of argument $labels->name is used
description(string)
A short description about taxonomy (what it is for).
Default: ''
labels(array)

Array describing taxonomy titles (for display in admin panel).

See get_taxonomy_labels() for complete list

By default, "tags" labels are used for non-tree-like taxonomies and "category" labels for tree-like taxonomies.

  • name
    The name of a taxonomy, usually in the plural. Default _x( 'Post Tags', 'taxonomy general name' ) or _x( 'Categories', 'taxonomy general name' );.

  • singular_name
    Name for one element of this taxonomy. Default _x( 'Post Tag', 'taxonomy singular name' ) or _x( 'Category', 'taxonomy singular name' );.

  • menu_name
    Text for the menu name. This string denotes the name for the menu items. Default value of 'name' parameter.

  • search_items
    Text for finding a taxonomy item. Default __( 'Search Tags' ) or __( 'Search Categories' ).

  • popular_items
    Text for a block of popular items. __( 'Popular Tags' ) or null.

  • all_items
    Text for all elements. __( 'All Tags' ) or __( 'All Categories' );.

  • parent_item
    Text for the parent taxonomy element. This argument is not used for non-tree taxonomies. Default null or __( 'Parent Category' );.

  • parent_item_colon
    Text for the parent taxonomy element, the same as parent_item but with a colon (smile at the end. Default '' or __( 'Parent Category:' );.

  • edit_item
    Text for editing an element. Default __( 'Edit Tag' ) or __( 'Edit Category' );.

  • update_item
    Text for updating an item. Default __( 'Update Tag' ) or __( 'Update Category' );.

  • add_new_item
    Text to add a new taxonomy element. Default __( 'Add New Tag' ) or __( 'Add New Category' );.

  • view_item
    Text for viewing a taxonomy term. Default: "View tag", "View category". Used for example in the admin bar (toolbar).

  • new_item_name
    Text for creating a new taxonomy element. Default __( 'New Tag Name' ) or __( 'New Category Name' );.

  • separate_items_with_commas
    Text in the admin panel saying that terms (labels) should be separated by commas. Not used for tree taxonomies. Default: __( 'Separate tags with commas' ) or null.

  • add_or_remove_items
    Text for "remove or add element" which is used in admin block when javascript is disabled. Not valid for tree taxonomies. Default __( 'Add or remove tags' ) or null.

  • choose_from_most_used
    Text for blog when editing a post "select from frequently used". Not valid for tree taxonomies. Default __( 'Choose from the most used tags' ) or null.

  • popular_items
    Search text for popular terms. This option is not used for tree taxonomies. Default: "Popular tags" or null.

  • not_found
    The text "not found", which is displayed if no term was found when clicking on a frequently used term.

  • back_to_items
    The text "← Go to categories". The label displayed when the term is updated.
public(true|false)
Whether to show this taxonomy in the admin panel interface. This value is passed to parameters publicly_queryable, show_ui, show_in_nav_menus if they are not set their own value.
Default: true
show_ui(true|false)
Show this taxonomy control unit in the admin panel.
Default: if no, equals the public argument
show_in_menu(true|false)

Whether to show taxonomy in admin menu.

  • true - the taxonomy will be shown as a submenu of the post type to which it is attached.
  • false - the submenu will not be shown.

Parameter $show_ui must be enabled (true).

Default: if no, equals 'show_ui' argument

show_in_nav_menus(true|false)
true will allow you to select elements of this taxonomy in the navigation menu.
Default: if no, equals the public argument
show_tagcloud(true|false)
Create a widget taxonomy elements cloud of this taxonomy (like a tag cloud).
Default: if no, equals 'show_ui' argument
show_in_rest(true|false) (WP 4.7)

Whether to enable the taxonomy in the REST API.

Also affects the Gutenberg block editor:

  • true - the taxonomy will be visible in the Gutenberg block editor.
  • false - the taxonomy will be visible only in the standard editor.

Default: false

rest_base(string) (WP 4.7)
Shortcut in the REST API. Default, taxonomy name.
Default: $taxonomy
rest_controller_class(string) (WP 4.7)
REST API Controller class name.
Default: 'WP_REST_Terms_Controller'
rest_namespace(string) (WP 5.9)
Specifies the prefix (namespace) for the REST API route URL.
Default: wp/v2
hierarchical(true|false)
  • true - the taxonomy will be tree-like (hierarchical) - it will have checkboxes on the post edit page (for example, post categories).
  • false - the taxonomy will be non-tree-like (non-hierarchical) - it will have just a blank text field to enter the name of the taxonomy element (e.g. post tags).

Default: false

update_count_callback(string)

The name of the function that will be called to update the number of posts of taxonomy item (the number of posts in the term). Posts of which post type to count is determined by the relationship between the taxonomy and the post type.

Default:

The callback function will get the following parameters:

  • $terms - The term_taxonomy_id of the terms to be updated.
  • $taxonomy - Taxonomy object.

The wp_update_term_count_now() function does the counting. If the taxonomy is only attached to post types (it may still be attached to users), then the _update_post_term_count() function will be used, which will only count published posts related to the term. If the taxonomy is attached not only to posts (but to some another object), then _update_generic_term_count() function will be used, which does not do this check.

This is important in the case of attachments. Since an attachment is a post type, by default _update_post_term_count() will be used. However, this may not work as it should, because it will only count attachments that are attached to the post (for example, when you add an image for the post). This means that attachments that you simply upload to WordPress using the media library, but don't actually attach to the post, will not be counted. If you were going to add a taxonomy for attachments to use the media library as some sort of document management solution, you're probably more suited to counting unattached media items than those that are attached to posts. In that case, you should force _update_generic_term_count(). To do this, you need to specify '_update_generic_term_count' as the value for this parameter.

One more important point: the _update_post_term_count() function only counts published posts. If you are using custom statuses or custom post types where the publication is not important, you will need to create your own counting function and use its name in this parameter.

Default: '' - '_update_post_term_count' or '_update_generic_term_count'

rewrite(array|false)

false - will disable reqrite rules. By default, the $taxonomy parameter will be used.

You can specify a custom query parameter (query var) using array as the value. Possible array arguments:

  • slug(string) - prefixes the taxonomy with this string. Default taxonomy name.
  • with_front(true|false) - allows you to set a prefix for the permanent link. Default true.
  • hierarchical(true|false) - true - enables hierarchical URL support (since version 3.1). Default false.
  • ep_mask(number) - Endpoint pattern. Default: EP_NONE. (Required for user-friendly permalinks). Assign an endpoint mask for this taxonomy - the default is EP_NONE. If you do not specify EP_MASK, user-friendly permalinks will not work. For more information, see Make WordPress Plugins summary of endpoints.

This array is passed to add_permastruct(), so you can also specify the arguments of that function here.

After changing this parameter or the first time you register a taxonomy, you need to recreate the rewrite rules. This can be done manually by simply going to the 'Settings > Permalink' admin page. Or you can call flush_rewrite_rules(). You only need to do this once, after registering the taxonomy, not with every request.

Default: true

publicly_queryable(true|false)
Whether users have access to taxonomy elements in the front part of the site. If not set, the value of the public parameter is taken.
Default: null (same as 'public' argument)
query_var(string|bool)

If you specify false, it will disable the query parameters and the query itself. That is, WordPress will not process or understand requests related to this taxonomy.

If you specify a string, it will be used as a query parameter to get elements of this taxonomy. Default the name of the taxonomy - $taxonomy parameter.

query_var is used for queries through WP_Query, such as

new WP_Query( [ 'people' => $person_name ] )

and URL requests like /?people=$person_name.

query_var=false will disable these options, but you can still retrieve posts by using WP_Query, for example:

new WP_Query( [ 'taxonomy'=>'people', 'term'=>$person_name ] )

Default: $taxonomy

capabilities(array)

An array of current user permissions for this taxonomy:

  • manage_terms - Default: manage_categories
  • edit_terms - Default: manage_categories
  • delete_terms - Default: manage_categories
  • assign_terms - Default: edit_posts

Default: presets

meta_box_cb(string)

callback function responsible for how the taxonomy will be displayed in the metabox.

Built-in function names:

  • post_categories_meta_box - show as categories
  • post_tags_meta_box - show as tags.

If set false, the metabox will be disabled.

This parameter is ignored by block editor (Gutenberg).

Default: null

meta_box_sanitize_cb(callable)
Callback function to sanitize taxonomy data saved from the meta-box. If not set, the function will be taken based on the value of $meta_box_cb.
show_admin_column(true|false)
Enable or disable auto-creation of a taxonomy column in admin post table of the associated post type.
Default: false
show_in_quick_edit(true|false)
Whether to show the taxonomy in the quick edit panel of the post (in the list of all posts table, when you click the "properties" button).
Default: null (value of 'show_ui' parameter)
sort(true|false)

Whether this taxonomy should remember the order in which items (terms) are attached to objects (posts).

For example, for tags, if this parameter is true, then when the tags are retrieved, they should be output in the order in which they were specified (added) for the post. That is, if this flag is set, the terms should not be sorted by name but by the term_order field.

If true, then in the wp_term_relationships table in the term_order field there will be written a number - the terms order of how posts were added to that terms. Most often this setting is not needed, in fact, this parameter not used in the code, it is nowhere spelled out and in fact does not affect anything.

Default: null

default_term(string|array) (WP 5.5)

The term that will be set by default (if no term of this taxonomy is set for the post).

The ID of such a term is stored in the default_term_{$taxonomy_name} option.

You can specify an array for a term to be created if it is not exists on the site yet. See wp_insert_term(). Possible array keys:

  • name(string) - Name of the default term.
  • slug(string) - URL name (slug) of the default term.
  • description(string) - Description for default term.
_builtin(true|false)
This parameter is intended for developers. If switched to true, it means that this taxonomy belongs to the internal WordPress taxonomy and is not (custom). INTERNAL USE ONLY!
Default: false

Examples

#1. Taxonomy registration

An example of registering two taxonomies "genres" and "writers" for "book" posts. You can paste this code into the theme's functions.php file.

// hook, through which the new taxonomies registering function is triggered
add_action( 'init', 'create_book_taxonomies' );

// function that creates 2 new taxonomies "genres" and "writers" for "book" post type
function create_book_taxonomies(){

	// Add a hierarchical taxonomy 'genre' (as categories)
	register_taxonomy( 'genre', [ 'book' ], [
		'hierarchical'  => true,
		'labels'        => array(
			'name'              => _x( 'Genres', 'taxonomy general name' ),
			'singular_name'     => _x( 'Genre', 'taxonomy singular name' ),
			'search_items'      =>  __( 'Search Genres' ),
			'all_items'         => __( 'All Genres' ),
			'parent_item'       => __( 'Parent Genre' ),
			'parent_item_colon' => __( 'Parent Genre:' ),
			'edit_item'         => __( 'Edit Genre' ),
			'update_item'       => __( 'Update Genre' ),
			'add_new_item'      => __( 'Add New Genre' ),
			'new_item_name'     => __( 'New Genre Name' ),
			'menu_name'         => __( 'Genre' ),
		),
		'show_ui'       => true,
		'query_var'     => true,
		//'rewrite'       => array( 'slug' => 'the_genre' ), // your slug in the URL
	] );

	// Add the NOT hierarchical taxonomy 'writer' (as tags)
	register_taxonomy( 'writer', 'book', [
		'hierarchical'  => false,
		'labels'        => array(
			'name'                        => _x( 'Writers', 'taxonomy general name' ),
			'singular_name'               => _x( 'Writer', 'taxonomy singular name' ),
			'search_items'                =>  __( 'Search Writers' ),
			'popular_items'               => __( 'Popular Writers' ),
			'all_items'                   => __( 'All Writers' ),
			'parent_item'                 => null,
			'parent_item_colon'           => null,
			'edit_item'                   => __( 'Edit Writer' ),
			'update_item'                 => __( 'Update Writer' ),
			'add_new_item'                => __( 'Add New Writer' ),
			'new_item_name'               => __( 'New Writer Name' ),
			'separate_items_with_commas'  => __( 'Separate writers with commas' ),
			'add_or_remove_items'         => __( 'Add or remove writers' ),
			'choose_from_most_used'       => __( 'Choose from the most used writers' ),
			'menu_name'                   => __( 'Writers' ),
		),
		'show_ui'       => true,
		'query_var'     => true,
		//'rewrite'       => array( 'slug' => 'the_writer' ), // your slug in the URL
	] );
}

#2 Example of Creating a Private Taxonomy

If you do not want your taxonomy to be exposed publicly, you can set the 'public' and 'rewrite' parameters to false. It will be available to use internally by your plugin or theme, but will not be visible in front end.

add_action( 'init', 'wpdocs_register_private_taxonomy', 0 );

/**
 * Register a private 'Genre' taxonomy for post type 'book'.
 *
 * @see register_post_type() for registering post types.
 */
function wpdocs_register_private_taxonomy() {
	$args = array(
		'label'        => __( 'Genre', 'textdomain' ),
		'public'       => false,
		'rewrite'      => false,
		'hierarchical' => true
	);

	register_taxonomy( 'genre', 'book', $args );
}

#3 Taxonomy with same URL hierarchy as CPT

In order to have a taxonomy appear in the URL hierarchy of the relevant CPT, you can rewrite the taxonomy slug to contain the CPT’s slug. But you must register the CPT after registering the taxonomy, otherwise the rewrite will not work, i.e. in this case, book CPT must be registered after genre taxonomy.

add_action( 'init', 'wpdocs_create_book_tax_rewrite', 0 );

/**
 * Register a 'genre' taxonomy for post type 'book', with a rewrite to match book CPT slug.
 *
 * @see register_post_type for registering post types.
 */
function wpdocs_create_book_tax_rewrite() {
	register_taxonomy( 'genre', 'book', [
		'rewrite' => [ 'slug' => 'books/genre' ],
	] );
}

#4 Custom capabilities for custom taxonomy

If you want to use the custom capabilities for taxonomy to be displayed and managed from frontend then append the ‘capabilities’ key with the array like below.

By default, users who can edit categories can edit custom taxonomies too. To disable such bihavior and add custom capability check for custom taxonomy you need specify that capabilities names as shown bellow. In this case to edit custom taxonomy user need to pass such check the check current_user_can( 'edit_genre' ).

add_action( 'init', 'wpdocs_create_book_tax', 0 );

/**
 * Register a 'genre' taxonomy for post type 'book'.
 *
 * Register custom capabilities for taxonomies.
 *
 * @see register_post_type for registering post types.
 */
function wpdocs_create_book_tax() {

	register_taxonomy( 'genre', 'book', array(
		'label'        => __( 'Genre', 'textdomain' ),
		'rewrite'      => array( 'slug' => 'genre' ),
		'hierarchical' => true,
		'capabilities' => array(
			'manage_terms'  => "manage_genre",
			'edit_terms'    => "edit_genre",
			'delete_terms'  => "delete_genre",
			'assign_terms'  => "assign_genre",
		),
	) );
}

#5 Renaming taxonomy names

The code below shows how to rename an existing category taxonomy labels.

// Let's rename the category taxonomy (titles)
add_action( 'init', function(){
	global $wp_taxonomies;

	$labels                     = & $wp_taxonomies['category']->labels;
	$labels->name               = 'Author';
	$labels->singular_name      = 'Author';
	$labels->add_new            = 'Add Author';
	$labels->add_new_item       = 'Add Author';
	$labels->edit_item          = 'Edit Author';
	$labels->new_item           = 'Author';
	$labels->view_item          = 'View Author';
	$labels->search_items       = 'Search Authors';
	$labels->not_found          = 'No Authors found';
	$labels->not_found_in_trash = 'No Authors found in Trash';
	$labels->all_items          = 'All Authors';
	$labels->menu_name          = 'Author';
	$labels->name_admin_bar     = 'Author';

} );

Notes

  • Global. WP_Taxonomy[]. $wp_taxonomies Registered taxonomies.

Changelog

Since 2.3.0 Introduced.
Since 4.2.0 Introduced show_in_quick_edit argument.
Since 4.4.0 The show_ui argument is now enforced on the term editing screen.
Since 4.4.0 The public argument now controls whether the taxonomy can be queried on the front end.
Since 4.5.0 Introduced publicly_queryable argument.
Since 4.7.0 Introduced show_in_rest, 'rest_base' and 'rest_controller_class' arguments to register the taxonomy in REST API.
Since 5.1.0 Introduced meta_box_sanitize_cb argument.
Since 5.4.0 Added the registered taxonomy object as a return value.
Since 5.5.0 Introduced default_term argument.
Since 5.9.0 Introduced rest_namespace argument.

Code of register_taxonomy() WP 6.0

function register_taxonomy( $taxonomy, $object_type, $args = array() ) {
	global $wp_taxonomies;

	if ( ! is_array( $wp_taxonomies ) ) {
		$wp_taxonomies = array();
	}

	$args = wp_parse_args( $args );

	if ( empty( $taxonomy ) || strlen( $taxonomy ) > 32 ) {
		_doing_it_wrong( __FUNCTION__, __( 'Taxonomy names must be between 1 and 32 characters in length.' ), '4.2.0' );
		return new WP_Error( 'taxonomy_length_invalid', __( 'Taxonomy names must be between 1 and 32 characters in length.' ) );
	}

	$taxonomy_object = new WP_Taxonomy( $taxonomy, $object_type, $args );
	$taxonomy_object->add_rewrite_rules();

	$wp_taxonomies[ $taxonomy ] = $taxonomy_object;

	$taxonomy_object->add_hooks();

	// Add default term.
	if ( ! empty( $taxonomy_object->default_term ) ) {
		$term = term_exists( $taxonomy_object->default_term['name'], $taxonomy );
		if ( $term ) {
			update_option( 'default_term_' . $taxonomy_object->name, $term['term_id'] );
		} else {
			$term = wp_insert_term(
				$taxonomy_object->default_term['name'],
				$taxonomy,
				array(
					'slug'        => sanitize_title( $taxonomy_object->default_term['slug'] ),
					'description' => $taxonomy_object->default_term['description'],
				)
			);

			// Update `term_id` in options.
			if ( ! is_wp_error( $term ) ) {
				update_option( 'default_term_' . $taxonomy_object->name, $term['term_id'] );
			}
		}
	}

	/**
	 * Fires after a taxonomy is registered.
	 *
	 * @since 3.3.0
	 *
	 * @param string       $taxonomy    Taxonomy slug.
	 * @param array|string $object_type Object type or array of object types.
	 * @param array        $args        Array of taxonomy registration arguments.
	 */
	do_action( 'registered_taxonomy', $taxonomy, $object_type, (array) $taxonomy_object );

	/**
	 * Fires after a specific taxonomy is registered.
	 *
	 * The dynamic portion of the filter name, `$taxonomy`, refers to the taxonomy key.
	 *
	 * Possible hook names include:
	 *
	 *  - `registered_taxonomy_category`
	 *  - `registered_taxonomy_post_tag`
	 *
	 * @since 6.0.0
	 *
	 * @param string       $taxonomy    Taxonomy slug.
	 * @param array|string $object_type Object type or array of object types.
	 * @param array        $args        Array of taxonomy registration arguments.
	 */
	do_action( "registered_taxonomy_{$taxonomy}", $taxonomy, $object_type, (array) $taxonomy_object );

	return $taxonomy_object;
}