Categories and tags are themselves just taxonomies — a general WordPress system for grouping content — built in and pre-attached to posts. A custom post type doesn't get either by default, but CPT UI can attach a brand new taxonomy to it just as easily as it registered the post type itself.
Every taxonomy is one of two shapes, chosen when it's created:
| Type | Behaves like | Good for |
|---|---|---|
| Hierarchical | Categories — can have parent/child terms, shown as checkboxes in the editor | Nested groupings — "Web Design" under "Services" |
| Non-hierarchical | Tags — flat, no parent/child, shown as a free-form comma-separated field | Loose labels — skills, keywords, anything without a natural nesting |
From CPT UI → Add/Edit Taxonomies, the process mirrors the post type screen: a taxonomy slug (e.g. project_type), plural and singular labels, the hierarchical/non-hierarchical choice above, and — the one setting that actually connects it to anything — an Attached to Post Types list, where you check off project (or whichever custom post type it should organize).
Attachments aren't one-to-one
A single taxonomy can attach to more than one post type at once, and a single post type can have more than one taxonomy attached — a project post type might reasonably have both a hierarchical project_type taxonomy and a non-hierarchical skill taxonomy at the same time.
Once attached, a custom taxonomy works with the same functions categories and tags already use — get_the_terms() to list a post's terms, has_term() to check for a specific one:
<?php
$terms = get_the_terms( get_the_ID(), 'project_type' );
if ( $terms && ! is_wp_error( $terms ) ) {
foreach ( $terms as $term ) {
echo esc_html( $term->name ) . ' ';
}
}
?>With both a post type and a taxonomy registered, the next lesson builds the template files that actually display them — following the exact same template hierarchy pattern from earlier, extended with a custom post type's own naming.