
🔧 Cómo crear una plantilla para un tipo de contenido personalizado en WordPress
Agregó un tipo de contenido personalizado a su sitio, por ejemplo «Promociones», «Portafolio» o «Testimonios». Todo funciona: las entradas se crean, los metacampos se completan. Pero cuando abre una de estas entradas en el front-end, se ve como una entrada de blog común. El mismo diseño, las mismas barras laterales, ninguna diferencia. Sin embargo, usted creó un tipo de contenido personalizado precisamente para que el contenido se presentara de forma distinta.
El problema es que WordPress renderiza cualquier entrada personalizada a través de single.php por defecto, la plantilla universal para todas las entradas individuales. Para obtener un diseño único para un tipo específico, necesita su propio archivo de plantilla. Y resulta ser sorprendentemente sencillo.
A continuación, un desglose paso a paso: desde la jerarquía de plantillas hasta dos métodos de creación (mediante un archivo del tema y mediante el filtro template_include), con ejemplos de código funcionales.
💡 Resumen rápido:
- Comprender cómo WordPress busca una plantilla para un tipo de contenido personalizado y la prioridad de
single-{post_type}.php - Copiar el archivo
single.phpde su tema, renombrarlo para su CPT y personalizarlo - Explorar un método alternativo mediante el filtro
template_include(útil si no quiere modificar el tema) - Crear el tipo de contenido personalizado en sí usando el plugin Custom Post Type UI, de forma rápida y sin código
1. Cómo elige WordPress una plantilla para un tipo de contenido personalizado
La jerarquía de plantillas de WordPress es una cadena de archivos que el núcleo verifica para encontrar la plantilla adecuada para renderizar una página. Para las entradas estándar, la cadena es larga: single-post.php → single.php → singular.php → index.php. Para los tipos de contenido personalizados es más corta, pero la lógica es la misma.
Cuando un visitante abre una entrada personalizada del tipo aktsii, WordPress verifica los archivos en este orden:
single-aktsii.php, la plantilla específica para este tipo de contenido personalizadosingle.php, la plantilla genérica para entradas individualessingular.php, la plantilla para cualquier contenido singular (entrada, página, CPT)index.php, el recurso final de respaldo
El primer archivo que se encuentra en la cadena es el que se usa para la renderización. Si single-aktsii.php existe, WordPress lo toma y se detiene. Si no se encuentra, el núcleo pasa a single.php. Esa es exactamente la razón por la que su tipo de contenido personalizado se ve como una entrada común: no existe el archivo single-aktsii.php, por lo que el núcleo recurre al single.php genérico.
La solución se deriva directamente de esto: cree single-{post_type}.php y WordPress lo tomará automáticamente.
2. Crear un archivo de plantilla: copiar single.php y renombrarlo
Este es el enfoque más simple y fiable. Funciona con cualquier tema clásico y no requiere plugins ni filtros.
Paso 1: encuentre el archivo single.php de su tema. Se encuentra en la raíz de la carpeta del tema: /wp-content/themes/your-theme/single.php. Si el tema usa FSE (Full Site Editing) y está construido sobre bloques, es posible que este archivo no exista, y el método del filtro (template_include) será más conveniente (sección 4).
Paso 2: copie y renombre. Copie single.php y asígnele un nombre siguiendo el patrón single-{slug}.php: reemplace {slug} con el slug de su tipo de contenido personalizado (el especificado durante el registro, en caracteres latinos). Por ejemplo, para un tipo con el slug aktsii:
1 single-aktsii.php
Para un tipo con el slug portfolio, el archivo se llamaría single-portfolio.php. Para testimonials, se convierte en single-testimonials.php. Importante: el slug debe coincidir con el especificado en el parámetro 'rewrite' => array('slug' => '...') durante el registro.
Paso 3: coloque el archivo de vuelta en la carpeta del tema. WordPress lo tomará automáticamente para todas las entradas de este tipo. No se necesita configuración adicional; la jerarquía de plantillas lo maneja automáticamente.
Paso 4: verifique. Abra cualquier entrada del tipo personalizado en el front-end. Si ve la misma página que antes, revise el nombre del archivo (distingue mayúsculas de minúsculas, guiones en lugar de guiones bajos) y limpie la caché. Si el archivo se creó en un tema hijo y el tema padre también contiene single-{post_type}.php, el tema hijo tiene prioridad.
3. Personalizar el contenido de la plantilla
El archivo single.php copiado sigue siendo idéntico al original; simplemente renderiza el mismo diseño genérico. Ahora lo llenamos con contenido específico del tipo de contenido personalizado.
3.1. Estructura básica: mostrar campos personalizados
Suponga que el tipo de contenido personalizado aktsii tiene los campos aktsiya_data_start, aktsiya_data_end y aktsiya_skidka (creados mediante ACF, Meta Box o manualmente). Aquí tiene una plantilla mínima que los muestra:
1 <?php 2 /** 3 * Template for custom type "Promotions" (single-aktsii.php) 4 */ 5 6 get_header(); 7 ?> 8 9 <main id="main" class="site-main" role="main"> 10 11 <?php while ( have_posts() ) : the_post(); ?> 12 13 <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> 14 15 <header class="entry-header"> 16 <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?> 17 </header> 18 19 <?php if ( has_post_thumbnail() ) : ?> 20 <div class="post-thumbnail"> 21 <?php the_post_thumbnail( 'large' ); ?> 22 </div> 23 <?php endif; ?> 24 25 <div class="entry-content"> 26 <?php the_content(); ?> 27 28 <div class="custom-fields"> 29 <?php 30 $data_start = get_post_meta( get_the_ID(), 'aktsiya_data_start', true ); 31 $data_end = get_post_meta( get_the_ID(), 'aktsiya_data_end', true ); 32 $skidka = get_post_meta( get_the_ID(), 'aktsiya_skidka', true ); 33 34 if ( $data_start ) { 35 echo '<p><strong>Start Date:</strong> ' . esc_html( $data_start ) . '</p>'; 36 } 37 if ( $data_end ) { 38 echo '<p><strong>End Date:</strong> ' . esc_html( $data_end ) . '</p>'; 39 } 40 if ( $skidka ) { 41 echo '<p><strong>Discount:</strong> ' . esc_html( $skidka ) . '%</p>'; 42 } 43 ?> 44 </div> 45 </div> 46 47 </article> 48 49 <?php 50 if ( comments_open() || get_comments_number() ) : 51 comments_template(); 52 endif; 53 ?> 54 55 <?php endwhile; ?> 56 57 </main> 58 59 <?php 60 get_sidebar(); 61 get_footer();
Esta es una plantilla completamente funcional que puede colocar en su tema tal cual. Muestra el título, la imagen destacada, el contenido y tres campos personalizados en un bloque separado.
3.2. Qué más puede hacer con la plantilla
A partir de aquí, las posibilidades de personalización son prácticamente ilimitadas:
- Eliminar la barra lateral: simplemente elimine la llamada a
get_sidebar(). - Cambiar el contenedor HTML: reemplace
<article>por un<div>con la clase que necesite, agregue su propia cuadrícula. - Llamar a una parte de plantilla específica: por ejemplo,
get_template_part( 'template-parts/content', 'aktsii' )y mantenga la lógica en un archivo separado. - Encolar estilos personalizados: mediante
wp_enqueue_style()dentro defunctions.phpcon una verificaciónis_singular( 'aktsii' ).
Una nota importante: si el tema se actualiza, un archivo en el tema padre puede sobrescribirse. Por lo tanto, trabaje en un tema hijo o use el método del filtro de la siguiente sección.
4. Enfoque alternativo: el filtro template_include
Si prefiere no modificar los archivos del tema (o el tema está basado en bloques y carece de un single.php clásico), puede asignar una plantilla mediante el hook template_include. Este se ejecuta antes de que WordPress incluya el archivo de plantilla y le permite sobrescribir la ruta.
Desventaja: necesitará almacenar el archivo de plantilla ya sea en un plugin o en un tema hijo, y el código debe apuntar a la ruta correcta. Ventaja: la lógica no se pierde cuando el tema padre se actualiza.
4.1. Ejemplo: un CPT, una plantilla
Agregue esto al archivo functions.php del tema hijo (o a un MU-plugin):
1 add_filter( 'template_include', 'techblog_cpt_template', 99 ); 2 3 function techblog_cpt_template( $template ) { 4 if ( is_singular( 'aktsii' ) ) { 5 $custom_template = get_stylesheet_directory() . '/single-aktsii.php'; 6 if ( file_exists( $custom_template ) ) { 7 return $custom_template; 8 } 9 } 10 return $template; 11 }
El código verifica si la página actual pertenece al tipo de contenido personalizado aktsii. Si es así, busca el archivo single-aktsii.php en la carpeta del tema hijo (get_stylesheet_directory()) y lo devuelve. Si no se encuentra el archivo, devuelve el $template predeterminado sin cambios.
4.2. Ejemplo: múltiples CPT con un solo manejador
Si tiene varios tipos de contenido personalizados, es conveniente consolidar la lógica en un solo filtro:
1 add_filter( 'template_include', 'techblog_cpt_templates', 99 ); 2 3 function techblog_cpt_templates( $template ) { 4 $cpt_templates = array( 5 'aktsii' => 'single-aktsii.php', 6 'portfolio' => 'single-portfolio.php', 7 'testimonials' => 'single-testimonials.php', 8 ); 9 10 foreach ( $cpt_templates as $cpt => $template_file ) { 11 if ( is_singular( $cpt ) ) { 12 $custom_template = get_stylesheet_directory() . '/' . $template_file; 13 if ( file_exists( $custom_template ) ) { 14 return $custom_template; 15 } 16 } 17 } 18 19 return $template; 20 }
Ahora cada CPT obtiene su propia plantilla y toda la lógica reside en una sola función.
4.3. Almacenar plantillas en un plugin
Si distribuye el tipo de contenido personalizado como un plugin (en lugar de como parte de un tema), tiene sentido almacenar la plantilla en la carpeta del plugin. En ese caso, especifique la ruta mediante plugin_dir_path( __FILE__ ):
1 add_filter( 'template_include', 'myplugin_cpt_template', 99 ); 2 3 function myplugin_cpt_template( $template ) { 4 if ( is_singular( 'aktsii' ) ) { 5 $custom_template = plugin_dir_path( __FILE__ ) . 'templates/single-aktsii.php'; 6 if ( file_exists( $custom_template ) ) { 7 return $custom_template; 8 } 9 } 10 return $template; 11 }
Este patrón es utilizado por muchos plugins populares (WooCommerce, Easy Digital Downloads, The Events Calendar): incluyen plantillas internamente y las incorporan mediante el filtro.
5. Crear el tipo de contenido personalizado
Tenemos la plantilla lista, pero para ser exhaustivos, unas palabras sobre de dónde proviene el tipo de contenido personalizado en sí.
5.1. Registro mediante código
Un registro mínimo de CPT en functions.php o un MU-plugin:
1 add_action( 'init', 'techblog_register_cpt_aktsii' ); 2 3 function techblog_register_cpt_aktsii() { 4 $labels = array( 5 'name' => 'Promotions', 6 'singular_name' => 'Promotion', 7 'menu_name' => 'Promotions', 8 ); 9 10 $args = array( 11 'labels' => $labels, 12 'public' => true, 13 'has_archive' => true, 14 'supports' => array( 'title', 'editor', 'thumbnail' ), 15 'rewrite' => array( 'slug' => 'aktsii' ), 16 'show_in_rest' => true, 17 ); 18 19 register_post_type( 'aktsii', $args ); 20 }
El detalle clave aquí es que el parámetro 'rewrite' => array( 'slug' => 'aktsii' ) establece el slug utilizado tanto en la URL como en el nombre del archivo de plantilla. Si el slug es aktsii, el archivo de plantilla debe llamarse single-aktsii.php.
5.2. Usar el plugin Custom Post Type UI
Si prefiere no editar functions.php, puede crear un CPT visualmente usando el plugin gratuito Custom Post Type UI en WordPress.org. Después de la instalación:
- En el panel de administración, vaya a CPT UI → Add/Edit Post Types
- Complete los campos: Post Type Slug (caracteres latinos), Plural Label, Singular Label
- En la pestaña Settings, seleccione qué características del editor necesita (título, editor, miniatura)
- Haga clic en Add Post Type
El plugin genera el código PHP de registro, que puede exportar y pegar en functions.php. Esto es conveniente si más adelante quiere eliminar la dependencia del plugin. El slug que especificó en CPT UI es el mismo que va en el nombre del archivo de plantilla.
⁉️🤔 Preguntas frecuentes
¿Qué debo hacer si la plantilla no se toma después de crear el archivo?
Regenere los enlaces permanentes: vaya a Settings → Permalinks y haga clic en «Save Changes» (no necesita cambiar nada). WordPress reconstruirá sus reglas de enrutamiento. Si eso no ayuda, verifique el nombre del archivo: el slug del tipo de contenido personalizado y el nombre del archivo deben coincidir (mayúsculas/minúsculas, guiones y guiones bajos). Como tercer paso, limpie la caché de su plugin de caché, si hay uno instalado. Los enlaces permanentes deben regenerarse después de registrar cualquier nuevo tipo de contenido personalizado, no solo cuando tenga problemas con la plantilla. Esta es una práctica estándar: vaya a Settings → Permalinks → Save, y las nuevas URL comenzarán a funcionar de inmediato.
¿Puedo usar una plantilla para múltiples tipos de contenido personalizados?
Sí, de dos maneras. Primero, cree un archivo físico
single-aktsii.php, y para el segundo tipo (digamos,portfolio) cópielo comosingle-portfolio.php. Segundo, use el filtrotemplate_include(sección 4): en el array$cpt_templates, apunte diferentes tipos al mismo archivo de plantilla. En la práctica, sin embargo, diferentes CPT casi siempre tienen diseños diferentes; copiar y editar es más simple que construir lógica condicional dentro de una sola plantilla. Si realmente tiene muchos tipos con lógica idéntica, cree unsingle-cpt.phpcompartido, verifiqueget_post_type()dentro de él e incluya la parte de plantilla correspondiente medianteget_template_part( 'template-parts/content', get_post_type() ). Este es un enfoque limpio y escalable.
¿Necesito crear archive-{post_type}.php** para la página de archivo?**
Se recomienda pero no es obligatorio. Sin él, WordPress usa
archive.phpoindex.php. Si el CPT tiene'has_archive' => truey el archivo se abre en/aktsii/pero no hay un archivoarchive-aktsii.php, se usará elarchive.phpgenérico. Crear una plantilla de archivo separada tiene sentido cuando la cuadrícula de entradas, el diseño de columnas o la barra lateral deben diferir del archivo del blog.
¿Funciona esto con temas de bloques (FSE)?
En los temas de bloques, los archivos de plantilla PHP clásicos no son el enfoque principal. Sin embargo, el filtro
template_include(sección 4) funciona también en FSE: puede colocarsingle-aktsii.phpen un tema hijo o plugin e incluirlo mediante el filtro. Alternativamente, en el Editor del Sitio puede crear una plantilla para un tipo de contenido personalizado específico a través de la interfaz: Appearance → Editor → Templates → Add New Template → Single Item: Aktsii. WordPress la guardará como una plantilla HTML en la base de datos y funcionará sin ningún archivo en el tema.
¿Qué elegir en 2026: archivo o filtro?
Una matriz de decisión rápida para elegir el enfoque:
Escenario | Qué usar |
|---|---|
Tema clásico, uno o dos CPT | Archivo |
Tema hijo, está agregando el CPT | Archivo en el tema hijo, sobrevive a las actualizaciones del tema padre |
Tema de bloques (FSE), CPT mediante un plugin | Filtro |
CPT distribuido como un plugin | Filtro + plantilla dentro de la carpeta del plugin |
Muchos CPT con una estructura similar | Filtro + |
En la práctica, en techblog.sdstudio.top usamos con mayor frecuencia una combinación: registramos el CPT mediante código en un MU-plugin y colocamos las plantillas como archivos en un tema hijo. Esto proporciona selección automática sin filtros adicionales, y las actualizaciones del tema padre no rompen nada.
Si recién está comenzando a aprender sobre tipos de contenido personalizados, empiece con el archivo single-{post_type}.php. Toma cinco minutos y ofrece resultados instantáneos. Cuando sienta que el enfoque basado en archivos ya no es suficiente (necesita cargar plantillas desde un plugin o intercambiarlas sobre la marcha), cambie a template_include. Ambos métodos son completamente legítimos y están soportados por el núcleo de WordPress.



