get_template_part() no es un include: es tu cadena de suministro
get_template_part es una de las funciones que nos permiten avanzar en el desarrollo de temas complejos en WordPress

Foto de Rick Mason en Unsplash
Todo lenguaje tiene sus ritos menores. Pequeñas funciones que parecen inocentes, atajos sin pretensión de grandeza. Pero a veces en esos pliegues se esconde la diferencia entre un sistema y un amasijo. WordPress decidió llamarlo get_template_part(). Tres palabras que, mal usadas, te condenan a la repetición infinita; bien entendidas, te obligan a pensar en términos de piezas, no de páginas. Ahí comienza la frontera: entre quien arma una catedral y quien improvisa chabolas.
Acto I — La mentira de la medianía
Te cuentan que get_template_part() es “un include() simpático” para no repetir HTML. Copias header.php, footer.php, unos cuantos content.php y, cuando algo no encaja, lo parcheas con condicionales y a tirar. Resultado: arquitectura Frankenstein. Duplicas el mismo bloque en seis plantillas, cada una con su CSS mutante. Cambiar un icono implica un safari por veinte archivos. Y cuando metes un tema hijo, nada respeta nada.
La medianía usa get_template_part() como cinta americana. Mete el partial, confía en los globals (hola, $post) y reza para que el loop no se rompa. ¿Pasar datos? “Bah, ya se apaña con lo que haya en el aire.” Eso es rogar por side effects. Eso es deuda técnica con intereses.
No. get_template_part() no es un comodín. Es logística de componentes. Si lo tratas como un include, construyes un vertedero. Si lo tratas como doctrina de despliegue, construyes un sistema.
Acto II — La verdad: La hostia de realidad
get_template_part() es el contrato entre tu sistema de diseño y tus plantillas. Define qué se renderiza, dónde, con qué datos y con qué prioridad (padre/hijo). No improvisa: orquesta.
1) Cadena de mando: convención > ocurrencia
Estructura mínima decente:
FILES
theme/
template-parts/
card/
card.php
card-product.php
card-post.php
media/
picture.php
layout/
grid.php
Regla: un partial = una responsabilidad. Nada de “card-que-a-veces-hace-slider-y-a-veces-muestra-un-form”.
Invocación limpia:
PHP
get_template_part(
'template-parts/card/card',
'product'
);
Negocio: con una convención clara, un junior puede tocar una tarjeta sin prender fuego al resto. Onboarding y time-to-change bajan en picado.
2) Datos declarativos: argumentos o no hay trato
Desde WP 5.5, get_template_part() acepta args. Úsalos o sufrirás fantasmas globales:
PHP
get_template_part(
'template-parts/card/card',
'product'
);
Dentro del partial (puro, sin efectos colaterales):
PHP
<!--?php // template-parts/card/card-product.php
$args = wp_parse_args($args ?? [], [
'title' => '',
'price' => null,
'url' => '',
'badge' => null,
]);
?>
HTML + PHP
<article class="card card--product">
<!--?php if ($args['badge']) : ?-->
<span class="card__badge">
<!--?php echo esc_html($args['badge']); ?-->
</span>
<!--?php endif; ?-->
<h3 class="card__title">
<a href="<?php echo esc_url($args['url']); ?>">
<!--?php echo esc_html($args['title']); ?-->
</a>
</h3>
<!--?php endif; ?-->
</article>
Prohibido: tocar el loop, consultar la base de datos, depender de $post “porque sí”. El partial renderiza; la plantilla decide y le pasa datos.
Negocio: predecible = testeable = mantenible. Puedes A/B testear el markup sin mover media web.
3) Fallbacks que trabajan por ti (no contra ti)
La pareja slug + name te da una estrategia de variantes controladas:
PHP
// En el loop estándar:
get_template_part(
'template-parts/content/content',
get_post_type()
);
// Cae en content-{tipo}.php o content.php
Evita inventos tipo .single .loop .post-type .quiz …. La variante es de archivo, no de selector monstruoso. Si mañana no hay content-ebook.php, no pasa nada: cae en content.php. Cadena de mando, no ruleta rusa.
4) Tema hijo: el override que quieres (no el que temes)
get_template_part() respeta la jerarquía child > parent. Si tu tema padre declara:
parent/template-parts/card/card-product.php
y el hijo necesita ajustar branding o markup:
child/template-parts/card/card-product.php
No toques el padre, no rompas nada. Sustituyes el módulo, no haces cirugías a corazón abierto.
Negocio: múltiples marcas/clientes con un core común. Mantienes coherencia y personalización sin forks infernales.
5) Un wrapper para pensar en sistema
Crea un wrapper que centralice rutas y políticas. Menos errores, más intención.
PHP
<!--?php
// functions/theme-parts.php
function theme_part(string $group, string $name = null, array $args = []): void {
$base = "template-parts/{$group}/{$group}";
get_template_part($base, $name, $args);
}
// Uso
theme_part(
'card',
'product',
['title' => 'Café', 'price' => 8.9, 'url' => '/cafe']
);
?-->
theme_part(
'media',
null,
['src' => $src, 'alt' => $alt]
);
Mañana cambias el layout de carpetas o añades trazas/logging en el wrapper y todo el sistema obedece.
6) Seguridad y saneamiento: cero excusas
Partial = última milla antes del usuario. Aquí escapas todo:
esc_html() para texto
esc_attr() para atributos
esc_url() para URLs
wp_kses_post() si recibes HTML controlado
Y valida args con wp_parse_args() + tipos. Nada de echo $args[‘title’]; a pelo. La seguridad no es “bonita”; es barata comparada con un susto.
7) Rendimiento sin paranoia
“Es que muchos partials ralentizan”. Con OPcache y una jerarquía clara, el costo de locate_template() es despreciable frente al de pedir 20 recursos de terceros o renderizar 1000 nodos. Lo que sí mata el rendimiento es repetir consultas y lógica en cada partial. Recuerda: markup aquí, datos fuera.
8) Bloque clásico vs tema de bloques: convivencia, no guerra
Temas clásicos: get_template_part() es tu bisturí diario.
Temas de bloques (FSE): usa template parts de bloques (wp:template-part) para cabeceras, pies, etc. Aun así, get_template_part() sigue siendo útil para piezas PHP (lógica, compatibilidad, email templates, shortcodes complejos, SSR de widgets) o en temas híbridos.
Traducción: elige la herramienta según la naturaleza del componente. Si es puramente estructural/visual y editable por el editor de sitios, bloque. Si hay PHP o control fino, partial. Estrategia, no dogma.
Acto III — El manifiesto: La nueva ley)
get_template_part() es contrato, no comodín.
Un partial recibe args y devuelve markup. Sin globals mágicos.
Una responsabilidad por partial.
Card, media, layout. Si hace dos cosas, son dos archivos.
Convención antes que creatividad.
template-parts/{grupo}/{grupo}-{variante}.php. Fallbacks previsibles.
Tema hijo sin drama.
Overrides quirúrgicos. Nada de copiar todo el tema.
Wrapper obligatorio.
Centraliza rutas, tracing, políticas. El sistema obedece a una sola puerta.
Datos fuera, presentación dentro.
Consulta y negocio en la plantilla/controlador; partial = render.
Escapa todo.
Cada salida saneada. La seguridad es condición de aceptación.
Mide el cambio, no la línea.
Cambiar una tarjeta global < 15 min, sin buscar en 12 plantillas.
FSE y PHP pueden convivir.
Bloques para estructura editable; partials para piezas con cerebro.
Tolerancia cero a los side effects.
Si un partial necesita el loop, no es un partial: es una plantilla mal bautizada.
Guía de batalla: Para mañana por la mañana
- Inventaria tus duplicados y conviértelos en template-parts/ con naming consistente.
- Crea un wrapper theme_part() y migra llamadas.
- Elimina dependencias de $post: pasa ID, WP_Post o campos por args.
- Escribe tests manuales: una vista por variante; verifica fallbacks.
- Documenta en README: estructura, reglas de args, política de overrides en tema hijo.
- Prohíbe merges que añadan partials sin args o sin escapado.
Epílogo
get_template_part() no está para “reutilizar cachos” como quien pega post-its. Está para garantizar una cadena de suministro de interfaz: componentes previsibles, datos declarados, overrides seguros y velocidad de cambio obscena. Si eso no te mueve, es que buscas recetas, no criterio.
Desbroza. Establece la doctrina. Y que cada partial entre en formación.