Term Queries
Query WordPress taxonomy terms for category listings, tag clouds, and taxonomy-based navigation.
What This Guide Covers
Terms are WordPress taxonomy data - categories, tags, and custom taxonomies that organize your content. This guide shows you how to query WordPress terms using Builderius GraphQL for any taxonomy scenario.
You'll learn how to:
- Get current term data for archive templates
- Query multiple terms for category listings and tag clouds
- Filter terms by taxonomy, post counts, and custom criteria
- Access custom term fields from ACF and MetaBox
- Build taxonomy-based navigation and filtering
- Handle term pagination and hierarchical relationships
Query Single Term
- Current Term
- Specific Term
Get the term you're currently viewing (on category/tag archives):
{
archive {
term {
term_id
name
slug
description
count
}
}
}
Get a term by ID, name, or slug:
{
# Query by term ID (default identifier)
term(identifier: "term_id", value: 123, taxonomy: "category") { # Int - Term ID number
name
slug
description
}
# Query by term taxonomy ID
term(identifier: "term_taxonomy_id", value: 456, taxonomy: "category") { # Int - Term taxonomy ID
name
slug
description
}
# Query by term slug
term(identifier: "slug", value: "news", taxonomy: "category") { # String - Term slug
name
slug
description
}
# Query by term name
term(identifier: "name", value: "News", taxonomy: "category") { # String - Term name
name
slug
description
}
}
Term ID vs Term Taxonomy ID
- Normal Use
- Rare Case
- When Taxonomy ID Matters
Normal WordPress Behavior (Separate Terms): When you create terms through wp-admin, each taxonomy gets its own separate terms (php code serves as explanation):
// Create "News" as category - gets term_id = 5, term_taxonomy_id = 10
wp_insert_term('News', 'category');
// Create "News" as tag - gets term_id = 6, term_taxonomy_id = 11
wp_insert_term('News', 'post_tag');
In this normal case, term_id and term_taxonomy_id are functionally equivalent for querying.
Rare Case (Shared Terms Across Taxonomies): You can programmatically force the same term to exist in multiple taxonomies (php code serves as explanation):
// Create term in first taxonomy
$term = wp_insert_term('News', 'category');
$term_id = $term['term_id']; // Let's say this is 5
// Force same term into second taxonomy
wp_insert_term('News', 'post_tag', array(
'term_id' => $term_id // Same term_id = 5
));
Now you have:
term_id= 5 (same for both taxonomies)term_taxonomy_id= 10 (for category relationship)term_taxonomy_id= 11 (for tag relationship)
When term_taxonomy_id Matters:
Only in this rare shared-term scenario do you need term_taxonomy_id for precision:
# Gets the tag version of term_id 5
term(identifier: "term_taxonomy_id", value: 11, taxonomy: "post_tag")
# Gets the category version of term_id 5
term(identifier: "term_taxonomy_id", value: 10, taxonomy: "category")
Practical Usage:
Most of the time, use term_id or slug. Use term_taxonomy_id only when working with shared terms or when you have specific term_taxonomy_id values from database queries or API responses.
Complete Term Fields Reference
Here's what's available when querying a term - all standard WordPress taxonomy fields plus custom field integrations:
{
term {
# Basic Fields
term_id
name
slug
term_group
term_taxonomy_id
taxonomy
description
count
parent
# Children Terms (Hierarchy)
children {
term_id
name
slug
count
}
# Native WordPress Meta Fields
featured_color: meta_value(key: "_featured_color")
custom_icon: meta_value(key: "_custom_icon")
# All Meta Fields (returns array)
meta {
key
value
}
# ACF Fields
category_image: acf_value(name: "category_image")
featured_on_homepage: acf_value(name: "featured_on_homepage")
category_color: acf_value(name: "category_color")
description_extended: acf_value(name: "description_extended")
# ACF Repeater Fields
featured_posts: acf_repeater_value(name: "featured_posts") {
post_id: acf_value(name: "post_id")
featured_order: acf_value(name: "featured_order")
}
# MetaBox Fields (Term Level)
category_priority: metabox_value(field_id: "category_priority")
display_settings: metabox_value(field_id: "display_settings")
}
}
Basic Term Loop
{
terms_query(
arguments: {
taxonomy: "category"
hide_empty: true
number: 10
}
) {
terms {
term_id
name
slug
description
count
}
}
}
Custom Term Queries
Custom term queries Pro feature let you manually specify exactly what terms you want, with your own filtering and arguments. You have complete control over which terms to display and how they're organized.
- Categories
- Tags
- Custom Taxonomy
Category listings with post counts:
{
terms_query(
arguments: {
taxonomy: "category"
hide_empty: true
number: 15
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
description
count
parent
}
pagination {
links
}
}
}
Tag cloud ordered by popularity:
{
terms_query(
arguments: {
taxonomy: "post_tag"
hide_empty: true
number: 50
orderby: "count"
order: "DESC"
}
) {
terms {
term_id
name
slug
count
# Calculate tag weight for cloud display
tag_weight: expression_result(
expression: "min(count / 5, 10)"
)
}
}
}
Product categories with custom fields:
{
terms_query(
arguments: {
taxonomy: "product_category"
hide_empty: true
number: 20
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
description
count
category_image: acf_value(name: "category_image")
featured_on_homepage: acf_value(name: "featured_on_homepage")
category_color: acf_value(name: "category_color")
}
pagination {
links
}
}
}
Query Arguments
Complete Arguments Reference
All arguments available for terms_query shown in context:
{
terms_query(
arguments: {
# Taxonomy
taxonomy: "category" # String or Array of strings - Taxonomy name (category, post_tag, or custom taxonomy)
# Include/Exclude Terms
include: [1, 5, 12] # Array - Include only these term IDs
exclude: [3, 7] # Array - Exclude these term IDs
# Filter Options
hide_empty: true # Boolean - Hide terms with no posts (true, false)
object_ids: [10, 25, 30] # Array - Filter terms by object/post IDs
name: ["News", "Updates"] # Array - Filter by term names
slug: ["news", "updates"] # Array - Filter by term slugs
# Pagination & Limits
number: 20 # Int - Number of terms to return
offset: 5 # Int - Number of terms to skip from start
# Ordering
orderby: "name" # String - Sort field (name, slug, term_group, term_id, description, count)
order: "ASC" # String - Sort direction (ASC, DESC)
}
) {
terms {
name
slug
}
}
}
Basic Filtering
- By Taxonomy
- Show Empty
- Ordering
- Include/Exclude
Filter by specific taxonomy:
{
terms_query(
arguments: {
taxonomy: "category"
}
) {
terms {
name
slug
}
}
}
Show only terms with posts:
{
terms_query(
arguments: {
taxonomy: "category"
hide_empty: false
}
) {
terms {
name
slug
count
}
}
}
Control term order and sorting:
{
terms_query(
arguments: {
taxonomy: "category"
orderby: "count" # name, slug, count, term_id
order: "DESC" # DESC, ASC
}
) {
terms {
name
count
}
}
}
Include or exclude specific terms:
{
terms_query(
arguments: {
taxonomy: "category"
include: [1, 5, 12] # Only these terms
exclude: [3, 7] # Exclude these terms
}
) {
terms {
name
slug
}
}
}
Term to Term Relationships
Navigate between related terms using hierarchical and sibling relationships.
- Current Term Parent
- Sibling Terms
- Current Term Children
- Term Hierarchy
- Recursive Hierarchy
Get parent term of the current term:
{
archive {
term {
name
parent @private # Get parent ID for conditionals
parent_data: terms_query( # Fetch parent term details
arguments: {
taxonomy: "category"
include: "{{parent}}"
}
) @private {
terms {
term_id @private
name
}
}
parent_is: expression_result(
expression: "(parent > 0) ? parent_data.terms[0].name : null" # Show parent name or null
)
}
}
}
Get terms that share the same parent:
{
archive {
term {
parent @private
sibling_terms: terms_query(
arguments: {
taxonomy: "category"
parent: "{{parent}}" # Terms with same parent
hide_empty: true
}
) {
terms {
name
slug
count
}
}
}
}
}
Get child terms of the current term:
{
archive {
term {
term_id @private
name
description
child_categories: terms_query(
arguments: {
taxonomy: "category"
parent: "{{term_id}}" # Child terms of current term
hide_empty: true
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
count
}
}
}
}
}
Build a complete nested term hierarchy:
{
terms_query(
arguments: {
taxonomy: "category"
parent: 0 # Top-level terms only
hide_empty: true
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
count
# Get child terms for each parent
children: terms_query(
arguments: {
taxonomy: "category"
parent: "{{term_id}}" # Children of this term
hide_empty: true
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
count
}
}
}
}
}
Build complete term hierarchy with unlimited nesting levels:
{
terms_query(
arguments: {
taxonomy: "category"
parent: 0 # Top-level terms only
hide_empty: true
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
count
# Recursive children - gets all nested levels automatically
children @recursive {
term_id
name
slug
count
parent
}
}
}
}
Content Relationships
Connect terms with their associated posts and content.
- Current Post Terms
- Single Term Posts
- Terms With Posts
Get terms for the current post:
{
post {
ID @private
post_categories: terms_query(
arguments: {
taxonomy: "category"
object_ids: "{{ID}}" # Terms assigned to current post
}
) {
terms {
name
slug
count
}
}
}
}
Get posts assigned to a single and specific term:
{
term(identifier: "slug", value: "news", taxonomy: "category") {
term_id @private
name
slug
description
count
term_posts: posts_query(
arguments: {
tax_query: {
array: [
{
taxonomy: "category"
field: "term_id"
terms: "{{term_id}}"
operator: "IN"
}
]
}
posts_per_page: 10
orderby: "date"
order: "DESC"
}
) {
posts {
post_title
post_excerpt
post_date
permalink
}
}
}
}
Get posts assigned grouped by terms:
{
terms_query(
arguments: {
taxonomy: "category"
object_ids: [10, 25, 30] # Terms used by these specific posts
hide_empty: true
}
) {
terms {
name
slug
count
# Show posts within each term
posts_in_term: posts_query(
arguments: {
post__in: [10, 25, 30] # Only the specific posts we're filtering by
tax_query: {
array: [
{
taxonomy: "category"
field: "term_id"
terms: "{{term_id}}"
operator: "IN"
}
]
}
}
) {
posts {
post_title
permalink
}
}
}
}
}
Pagination
Handle pagination for term listings with custom URL parameters.
- Basic Pagination
- Advanced Pagination
Basic term pagination:
{
terms_query(
arguments: {
taxonomy: "category"
hide_empty: true
number: 20
orderby: "name"
order: "ASC"
}
) {
terms {
name
slug
count
}
pagination(
arguments: {
pagination_url_param_name: "category_page"
}
) {
links
current_page
total_pages
}
}
}
Advanced pagination with offset control and formatted page info display:
{
terms_query(
arguments: {
taxonomy: "product_category"
hide_empty: true
number: 15
offset: 0 # Starting point for pagination
orderby: "count"
order: "DESC"
}
) {
terms {
name
slug
count
category_image: acf_value(name: "category_image")
}
pagination(
arguments: {
pagination_url_param_name: "categories_page"
}
) {
current_page
total_pages
links
# Enhanced pagination info
page_info: expression_result(
expression: "current_page . ' of ' . total_pages . ' pages'"
)
}
}
}
Performance Tips
Optimize your term queries for better performance by using these techniques.
- Use @private Fields
- Limit Data
- Efficient Ordering
Use @private to query data you need for variables but don't want in output:
{
archive {
term {
term_id @private # Hidden from output, available for {{term_id}}
name
parent_category: terms_query(
arguments: {
taxonomy: "category"
include: "{{parent}}" # Uses private parent field
}
) {
terms {
name
slug
}
}
}
}
}
Only fetch the fields and terms you actually need:
{
terms_query(
arguments: {
taxonomy: "category"
number: 15 # Don't fetch more than needed
hide_empty: true # Only terms with posts
orderby: "name"
order: "ASC"
}
) {
terms {
name # Only fields you'll use
slug
count
}
}
}
Use efficient ordering methods:
{
terms_query(
arguments: {
taxonomy: "category"
orderby: "name" # Efficient: name, term_id, count
order: "ASC"
number: 20
}
) {
terms {
name
slug
}
}
}
Common Use Cases
Real-world examples of term queries you'll commonly need for category listings, tag clouds, and taxonomy navigation.
- Category Navigation
- Tag Cloud
- Terms with Posts
Category navigation with hierarchy and custom images:
{
terms_query(
arguments: {
taxonomy: "category"
parent: 0 # Top-level categories only
hide_empty: true # Only categories with posts
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
count
# ACF fields for navigation
category_image: acf_value(name: "category_image")
featured_on_nav: acf_value(name: "featured_on_nav")
nav_color: acf_value(name: "nav_color")
# Child categories
subcategories: terms_query(
arguments: {
taxonomy: "category"
parent: "{{term_id}}" # Children of this category
hide_empty: true
number: 10
orderby: "name"
order: "ASC"
}
) {
terms {
name
slug
count
}
}
}
}
}
Weighted tag cloud with size classes:
{
terms_query(
arguments: {
taxonomy: "post_tag"
hide_empty: true # Only tags with posts
number: 100 # Large number for full cloud
orderby: "count" # Most popular first
order: "DESC"
}
) {
terms {
name
slug
count
# Calculate tag weight for CSS sizing
tag_size_class: expression_result(
expression: "count <= 2 ? 'tag-small' : (count <= 10 ? 'tag-medium' : 'tag-large')"
)
# Alternative numerical weight
tag_weight: expression_result(
expression: "min(max(count, 1), 10)"
)
}
}
}
Terms with their recent posts nested inside:
{
terms_query(
arguments: {
taxonomy: "product_category" # Product categories
hide_empty: true # Only categories with products
orderby: "name"
order: "ASC"
}
) {
terms {
term_id
name
slug
description
count
# Custom fields for category display
category_image: acf_value(name: "category_image")
featured_on_homepage: acf_value(name: "featured_on_homepage")
# Recent products in this category
recent_products: posts_query(
arguments: {
post_type: "product" # Custom post type
posts_per_page: 6 # 6 products per category
tax_query: {
array: [
{
taxonomy: "product_category"
field: "term_id"
terms: ["{{term_id}}"] # Products in this category
operator: "IN"
}
]
}
orderby: "date"
order: "DESC"
}
) {
posts {
post_title
post_excerpt
permalink
featured_image {
file_url(size: THUMBNAIL) # Small thumbnails for category display
alt_text
}
# Product custom fields
price: acf_value(name: "price")
sale_price: acf_value(name: "sale_price")
}
}
}
}
}