Para mí, la mejor forma de definir un template tag, que se podría traducir como etiqueta de plantilla, es como una función PHP, normal y corriente, que se construye con el fin de mostrar información.

Por eso, los template tags tienen su lugar de uso perfecto, y de ahí su nombre, en los templates, estos son, las distintas plantillas de un theme (header.php, archive.php, single.php, etc). También se utilizan en cualquier otro lugar destinado a mostrar información en el documento, como puede ser un widget.

¿Cómo son y cómo se utilizan?

Cómo hemos dicho, son funciones PHP que muestran información. Por ejemplo, un template tag para mostrar la palabra HOLA:

function di_hola() {
    echo 'HOLA';
}

En cualquier parte del template dónde quieras que aparezca el mensaje HOLA, simplemente ejecutas la función;

di_hola();

Así de sencillo es el concepto de template tag.

Nomenclatura: muy simple y muy descriptiva

Por convención, los template tags en WordPress deberían mostrar sólo una cosa. Por ejemplo, el título de un artículo o su fecha de publicación. Y para denominar los template tags nada más sencillo que el nombre de la cosa que muestra, por ejemplo the_title() para mostrar el título del post.

Muchos template tags tienen la función equivalente para obtener algo, no para mostrarlo. Estas funciones reciben el mismo nombre pero con el prefijo get_. Así, si the_title() muestra el título, get_the_title() obtiene el título, lo que haría posible que se pueda asignar a una variable:

// muestra el título
the_title();

// obtiene el títtulo
$titulo = get_the_title();

// Se podría mosrar el título así también
echo get_the_title();

// pero esto no, ya que the_title() muestra el valor, no lo devuelve
$titutlo = the_title();

Los template tags del core y el Loop

WordPress viene con todo un set de template tags amplio y variado. Puedes ver la lista en esta entrada del Codex, aunque probablemente no exhaustiva. Lo más importante que un desarrollador debe tener claro es que los template tags del core, en su gran mayoría, están diseñadas para su uso dentro del Loop.

¿Por qué?

Para seguir con el ejemplo de the_title(), este template tag muestra el título del post, pero ¿de qué post? Del que ahí almacenado en el objeto global $post. Y este objeto es escrito en el loop. Por ejemplo. si estamos viendo un post, el template single.php podría ser:

<?php while ( have_posts() ) { the_post(); ?>
  <article>
    <h1><?php the_title(); ?></h1>
  </article>
<?php } ?>

O en un loop secundario, por ejemplo:

<?php
$args = array(
  // ...
);

$query = new WP_Query( $args );

while ( $query->have_posts() ) { $query->the_post(); ?>
  <article>
    <h1><?php the_title(); ?></h1>
  </article>
<?php
 }
 wp_reset_postdata();
?>

Fíjate en el uso de wp_reset_postdata(). Después de cada loop secundario hay que utilizar esta función para que el objeto global $post vuelva a su valor original, antes del loop secundario. La importancia de esto se puede ver en este ejemplo básico de un loop secundario dentro del loop principal:

<?php while ( have_posts() ) { the_post(); ?>
  <article>
    // Aquí estamos en el loop principal
    <h1><?php the_title(); ?></h1>
    
    <?php
      $query = new WP_Query( $args );
      while ( $query->have_posts() ) { $query->the_post(); ?>
          <article>
              // Se muestra el título de cada post
              // encontrado en $query
              <h1><?php the_title(); ?></h1>
          </article>
       <?php
       }
       wp_reset_postdata();
    ?>

    // Aquí estamos de nuevo en el loop principal
    <h1><?php the_title(); ?></h1>
  </article>
<?php } ?>

Pero recuerda que no todos los template tags tienen que utilizarse dentro del loop. Por ejemplo, wp_list_categories() es un template tag que muestra una lista de las categorías del sitio y no tiene nada que ver con el loop. Sin embargo, the_category() tiene que estar dentro del loop y muestra las categorías a las que pertence el post actual.

Template tags con parámetros

Algunos template tags admiten parámetros, por ejemplo the_terms(). Ese template tag muestra una lista de los términos de una taxonomía a los que ha sido asignado un post. Acepta como parámetros el ID del post del que se quiere mostrar la lista, la taxonomía en la que comprobar la clasificación del post y los caracteres de separación entre un término y otro en la lista mostrada:

// "Categoría: categoría A / categoría B / Cateogría C
the_terms( $post->ID, 'category', 'Categorías: ', ' / ' );

Cómo crear un template tag propio

Pues como un template tag es simplement una función PHP que muestra información, crear un template tag propio es tan fácil como definir una función que no devuelva ningún valor, que no haya un return, sino que lo muestre con echo:

function di_hola() {
  echo 'HOLA';
}

Por ejemplo, podríamos crear un template tag para mostrar el precio almacenado en el custom field price:

function cyb_the_price( $post_id ) {
  echo get_post_meta( $post_id, 'price', true );
}

O si lo queremos hacer que se pueda utilizar dentro del loop sin tener que pasar el $post_id:

function cyb_the_price( $post_id = 0 ) {
  if( ! $post_id ) {
    // Obtener el ID que
    // se haya establecido en el Loop
    $post_id = get_the_ID();
  }
  echo get_post_meta( $post_id, 'price', true );
}

Muchas veces veo template tags que devuelven bloques de HTML, por ejemplo una lista de posts relacionados. Aunque es completamente válido y funcional, este tipo de bloques HTML son más apropiados para template parts que para template tags. Eso ya para otro post.