
Alla fine della Parte 1 della guida il tema era attivo e mostrava i contenuti, ma era un tema di una pagina sola: qualunque URL aprissi, rispondeva sempre index.php. Nessuna intestazione, nessun menu, nessuna differenza tra un articolo e un archivio.
Questa seconda parte cambia proprio quello. Costruiamo i template file — header.php, footer.php, sidebar.php — e soprattutto entriamo nella gerarchia dei template: la regola con cui WordPress decide, a ogni richiesta, quale file del tuo tema caricare. È la parte della guida che una volta capita rende tutto il resto meccanico, e finché non è chiara fa sembrare WordPress imprevedibile.
Cosa sono i template file, e perché non è una questione di ordine
Un template file è un file PHP del tema che produce una parte della pagina. La tentazione è leggerli come una comodità organizzativa — spezzare un file lungo in file corti — ma la ragione vera è un’altra: WordPress li sceglie da solo, in base al tipo di contenuto richiesto, senza che tu scriva una sola condizione.
È la differenza tra queste due cose. Se metti tutto in index.php, prima o poi ti ritrovi a scrivere if ( is_single() ) … elseif ( is_page() ) … elseif ( is_archive() ), e quel file diventa illeggibile in un paio di settimane. Se invece crei single.php, page.php e archive.php, quelle condizioni spariscono: le ha già valutate WordPress prima di includere il file.
Questi stessi file uno starter theme te li consegna già scritti nella loro forma minima. È il compromesso della strada alternativa: partire da una base già impostata ti risparmia la scrittura, non la comprensione — perché sono i file che modificherai di più.
I template si dividono in due famiglie. Quelli parziali — header, footer, sidebar — vengono inclusi dagli altri e compaiono ovunque. Quelli di pagina — single.php, page.php, archive.php, 404.php — vengono scelti da WordPress uno per richiesta. In questa parte costruiamo i primi e impariamo la regola che governa i secondi; i template di pagina veri e propri li scriviamo nella Parte 3.
header.php: cosa ci va davvero
header.php apre il documento HTML e viene incluso da get_header(). Contiene il DOCTYPE, l’intero <head>, l’apertura del <body>, il logo e la navigazione principale. Il nome del sito che vedrai qui sotto non è testo del tema ma un’impostazione, e ha regole sue: cosa si può e cosa no con il titolo del sito.
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<a class="skip-link screen-reader-text" href="#primary">
<?php esc_html_e( 'Vai al contenuto', 'nome-tema' ); ?>
</a>
<header class="site-header">
<div class="site-branding">
<?php the_custom_logo(); ?>
<p class="site-title">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home">
<?php bloginfo( 'name' ); ?>
</a>
</p>
</div>
<nav class="site-navigation" aria-label="<?php esc_attr_e( 'Menu principale', 'nome-tema' ); ?>">
<?php
wp_nav_menu( array(
'theme_location' => 'primary',
'container' => false,
'menu_class' => 'nav-menu',
'fallback_cb' => false,
) );
?>
</nav>
</header>Cinque righe di questo file non sono opzionali, e sono quelle che i tutorial saltano più spesso.
wp_head()è la riga più importante del tema. Tutto ciò che WordPress e i plugin devono mettere nel<head>passa da lì: fogli di stile, script, tag SEO, canonical, dati strutturati, preload. Se manca, il tema sembra funzionare ma metà dei plugin non funziona, e la causa è difficilissima da individuare perché nessuno segnala un errore.wp_body_open(), introdotto in WordPress 5.2, è l’equivalente subito dopo l’apertura del<body>. È lì che finiscono, tra le altre cose, i frammentinoscriptdi Google Tag Manager, ed è il punto in cui si aggancia una barra di avviso in cima al sito.language_attributes()stampalang="it-IT"leggendo la lingua impostata nel sito. Scriverelang="it"a mano funziona finché il sito è monolingua e smette di funzionare il giorno dopo.body_class()stampa un elenco di classi che descrivono la pagina corrente — tipo di contenuto, categoria, stato di login, slug. Sono il modo più economico che hai per differenziare il CSS senza scrivere PHP.- Lo skip link in cima permette a chi naviga da tastiera di saltare il menu e andare al contenuto. Costa tre righe e sposta parecchio sull’accessibilità: il contesto completo è in accessibilità da tastiera.
Nota cosa non c’è: il tag <title>. Non va scritto a mano, perché add_theme_support( 'title-tag' ) — dichiarato in functions.php nella Parte 1 — lo fa generare a WordPress, ed è la condizione perché un plugin SEO possa riscriverlo.
Il menu: registrarlo e non farlo esplodere
wp_nav_menu() stampa un menu solo se esiste una posizione registrata. La registrazione è già in functions.php dalla Parte 1, dentro after_setup_theme:
register_nav_menus( array(
'primary' => __( 'Menu principale', 'nome-tema' ),
'footer' => __( 'Menu footer', 'nome-tema' ),
) );Il parametro che merita attenzione è fallback_cb. Se lo lasci al valore predefinito e nessun menu è ancora assegnato alla posizione, WordPress chiama wp_page_menu() e stampa l’elenco di tutte le pagine del sito. Su un sito nuovo con tre pagine non te ne accorgi; su un sito con cinquanta pagine ti ritrovi un menu chilometrico e passi mezz’ora a cercare da dove esce. Impostarlo a false significa: se non c’è un menu, non stampare niente.
footer.php: chiudere il documento nel punto giusto
footer.php chiude quello che header.php ha aperto. Il contenuto varia molto da progetto a progetto, ma due righe sono sempre le stesse.
<footer class="site-footer">
<p class="site-copyright">
© <?php echo esc_html( date_i18n( 'Y' ) ); ?>
<?php bloginfo( 'name' ); ?>
</p>
<?php
wp_nav_menu( array(
'theme_location' => 'footer',
'container' => false,
'depth' => 1,
'fallback_cb' => false,
) );
?>
</footer>
<?php wp_footer(); ?>
</body>
</html>wp_footer() è il gemello di wp_head(): è lì che vengono stampati gli script caricati in fondo alla pagina, ed è anche il punto in cui WordPress inserisce la barra di amministrazione per gli utenti collegati. Se la admin bar non compare, nel 99% dei casi manca questa riga.
Un dettaglio piccolo ma reale: per l’anno usa date_i18n( 'Y' ) e non date( 'Y' ). La seconda legge il fuso orario del server, che quasi mai coincide con quello impostato nel sito. Per un anno nel copyright il danno è trascurabile — per una data di pubblicazione stampata in un template, no.
sidebar.php e le aree widget
La sidebar non è obbligatoria e su molti progetti non serve affatto. Quando serve, la cosa da capire è che il file sidebar.php e l’area widget sono due cose distinte: il file è il contenitore nel tema, l’area widget è ciò che l’utente riempie da Aspetto > Widget. L’area va registrata in functions.php:
function nometema_widget_areas() {
register_sidebar( array(
'name' => __( 'Barra laterale', 'nome-tema' ),
'id' => 'sidebar-1',
'description' => __( 'Widget mostrati nella colonna laterale.', 'nome-tema' ),
'before_widget' => '<section id="%1$s" class="widget %2$s">',
'after_widget' => '</section>',
'before_title' => '<h2 class="widget-title">',
'after_title' => '</h2>',
) );
}
add_action( 'widgets_init', 'nometema_widget_areas' );I quattro parametri before_ e after_ decidono il markup che avvolge ogni widget e il suo titolo. Non è un dettaglio estetico: è lì che stabilisci il livello di intestazione dei titoli dei widget. Impostarli su h2 tiene la gerarchia coerente con il resto della pagina, e la gerarchia dei titoli è uno dei controlli della checklist SEO on-page.
Il file, invece, è questo:
<?php
if ( ! is_active_sidebar( 'sidebar-1' ) ) {
return;
}
?>
<aside id="secondary" class="widget-area"
aria-label="<?php esc_attr_e( 'Barra laterale', 'nome-tema' ); ?>">
<?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside>Il return anticipato in cima è la riga che fa la differenza. Senza, quando l’utente non ha inserito nessun widget il tema stampa comunque un <aside> vuoto: su un layout a griglia significa una colonna larga trecento pixel che occupa spazio e non contiene niente. Con il return, se l’area è vuota il file non produce nulla e il contenuto principale può occupare tutta la larghezza.
Per farla comparire, richiama get_sidebar() nei template in cui la vuoi. Non ovunque per abitudine: una sidebar su un articolo lungo ha senso, sulla pagina contatti quasi mai.
La gerarchia dei template: la regola che decide tutto
Ogni volta che arriva una richiesta, WordPress capisce che tipo di contenuto deve mostrare e poi cerca nel tuo tema una lista ordinata di nomi di file, dal più specifico al più generico. Carica il primo che trova e ignora gli altri. Se non trova niente, arriva a index.php, che è la ragione per cui index.php è obbligatorio.
Detta così sembra astratta. In pratica significa questo: per personalizzare qualcosa non devi scrivere logica, devi creare un file con il nome giusto. Vuoi un layout diverso per gli articoli? Crei single.php. Solo per la categoria “guide”? Crei category-guide.php. WordPress lo trova e lo usa, senza che tu abbia registrato niente da nessuna parte.
Come verificare quale template è stato usato
Questa è l’informazione che serve più spesso e che nessuno ti dà a schermo. La soluzione è Query Monitor, il plugin già consigliato nella Parte 1: nel pannello Template mostra il file effettivamente caricato e, sotto, l’intera lista dei candidati nell’ordine in cui sono stati cercati, con quelli esistenti evidenziati.
Tenerlo aperto mentre navighi il sito è il modo più veloce per interiorizzare la gerarchia: in dieci minuti capisci più di quanto capiresti leggendo il diagramma ufficiale. Il diagramma — il Template Hierarchy del Developer Handbook — resta il riferimento da consultare per i casi che non ti capitano tutti i giorni, come le tassonomie personalizzate.
Quattro casi concreti di gerarchia
Le liste che seguono si leggono dall’alto: WordPress prende il primo file che esiste nel tema.
Un articolo del blog
URL di esempio: /2025/05/12/guida-wordpress/, tipo di contenuto post, slug guida-wordpress.
single-post-guida-wordpress.php single-post.php single.php singular.php index.php
Il primo nome, con lo slug incluso, esiste da WordPress 4.7 e serve raramente: è utile quando un singolo contenuto ha bisogno di un trattamento tutto suo — una landing page che vive dentro il blog, per esempio. singular.php è il livello che accomuna articoli e pagine, comodo quando il layout dei due coincide.
Una pagina statica
URL di esempio: /chi-sono/.
il template assegnato dall'editor (qualsiasi nome)
page-chi-sono.php
page-{ID}.php
page.php
singular.php
index.phpLa prima voce è quella che quasi tutte le spiegazioni dimenticano, ed è la più importante in un tema consegnato a un cliente: se alla pagina è stato assegnato un template personalizzato dal menu a tendina dell’editor, quello vince su tutto il resto. È il meccanismo che permette di offrire “Pagina a larghezza piena” o “Pagina contatti” come opzione selezionabile. Come si dichiara un template di pagina lo vediamo nella Parte 3.
L’archivio di una categoria
URL di esempio: /category/tutorial/.
category-tutorial.php
category-{ID}.php
category.php
archive.php
index.phpFra i due nomi specifici preferisci sempre quello con lo slug: l’ID è un numero che non dice niente a chi legge il codice, e se la categoria viene ricreata cambia.
Una pagina non trovata
404.php index.php
La lista più corta della gerarchia, e quella che vale di più rispetto allo sforzo: bastano due file di differenza tra un vicolo cieco e una pagina che rimette l’utente in carreggiata. Il template 404 lo scriviamo nella Parte 3.
get_template_part() e il layout dinamico
get_header(), get_footer() e get_sidebar() includono file con un nome fisso. Per tutto il resto c’è get_template_part(), che include un frammento riutilizzabile e — questa è la parte interessante — applica la stessa logica di fallback della gerarchia.
get_template_part( 'template-parts/content', get_post_type() );
Su un articolo questa riga cerca template-parts/content-post.php; se non esiste ricade su template-parts/content.php. Su un custom post type evento cerca content-evento.php e ricade sullo stesso file generico. Un’unica riga in index.php gestisce così tutti i tipi di contenuto, presenti e futuri: quando servirà un layout diverso per gli eventi, creerai il file e basta.
Da WordPress 5.5 la funzione accetta un terzo parametro, un array di dati da passare al frammento:
get_template_part( 'template-parts/card', null, array(
'dimensione' => 'grande',
'mostra_data' => false,
) );Dentro card.php quei valori arrivano nella variabile $args. Prima del 5.5 l’unico modo era passare per le variabili globali, con tutti i problemi che comporta: se hai visto codice WordPress che usa global $qualcosa per comunicare tra template, era questo il motivo, e oggi non serve più.
Il criterio per decidere cosa diventa un frammento è semplice: appena un blocco di markup compare in due template diversi, spostalo. Non prima — un frammento usato una volta sola è solo un file in più da aprire. L’uso avanzato di get_template_part(), con la struttura completa delle cartelle, è il punto di partenza della Parte 4.
Gli errori tipici di questa fase
I problemi che nascono qui hanno una caratteristica comune: non producono errori PHP. Il sito funziona, sembra a posto, e qualcos’altro non va.
- Manca
wp_head()owp_footer(). Sintomo: un plugin “non funziona”, il CSS non si carica, la barra di amministrazione è sparita, il consenso cookie non appare. Si perdono ore a controllare il plugin, e il problema è nel tema. È il primo controllo da fare quando qualcosa non compare in pagina. fallback_cblasciato al valore predefinito. Il menu mostra tutte le pagine del sito finché non ne assegni uno.- Sidebar senza
is_active_sidebar(). Contenitore vuoto che occupa spazio nel layout. get_header()chiamato dentro un frammento. L’intestazione compare due volte e il documento HTML non è più valido. I frammenti inclusi conget_template_part()devono produrre solo la porzione di contenuto che gli compete.- I tag
</body>e</html>chiusi altrove. Vanno infooter.php, dopowp_footer(). Se un template chiude il documento per conto suo, gli script di fine pagina finiscono fuori dalbody. - Aver dimenticato di aggiungere
get_header()eget_footer()ai nuovi template. Ogni template di pagina che crei deve includerli: la gerarchia sceglie il file, non ne completa il contenuto.
Il passo successivo: la Parte 3
Adesso il tema ha un’intestazione, un piè di pagina, un menu funzionante e, soprattutto, sai come WordPress sceglie i file. Manca la parte che sfrutta quella regola: i template specifici.
Nella Parte 3 — single, page, archive, 404 e loop avanzato scriviamo single.php, page.php, archive.php e 404.php, vediamo come si dichiara un template di pagina selezionabile dall’editor e affrontiamo WP_Query — con la trappola più comune di tutto lo sviluppo WordPress, quella di usarlo dove non andrebbe usato.
Domande frequenti
Devo per forza creare sidebar.php?
No. Non è un file obbligatorio e molti temi moderni non ne hanno nessuno: se il progetto non prevede una colonna laterale, saltarlo è la scelta giusta. Vale però la pena registrare almeno un’area widget anche senza sidebar visibile — il footer a colonne, per esempio, di solito è un’area widget — perché è ciò che permette a chi gestisce il sito di aggiungere contenuti senza chiamarti.
Perché il mio menu non compare?
Tre cause, in ordine di frequenza. La posizione non è registrata con register_nav_menus() in functions.php. Oppure è registrata ma nessun menu le è stato assegnato da Aspetto > Menu, e con fallback_cb a false il risultato corretto è proprio non vedere nulla. Oppure il nome della posizione in theme_location non coincide con quello registrato: è una stringa, e un refuso non genera errori.
Che differenza c’è tra get_template_part() e include?
Tre differenze pratiche. get_template_part() gestisce il fallback sul nome del file, quindi puoi avere una versione specifica e una generica. Cerca il file prima nel tema figlio e poi nel tema padre, che è ciò che rende un tema estendibile. E non produce un errore fatale se il file non esiste: la pagina resta incompleta ma il sito non va giù. Con include perdi tutte e tre le cose.
Devo creare tutti i file della gerarchia?
No, e crearli tutti è controproducente. La gerarchia è un sistema di ripiego: crea un file solo quando quel tipo di contenuto ha davvero bisogno di un layout diverso, altrimenti lascia che ricada sul livello superiore. Un tema con quattro template ben scritti è più facile da mantenere di uno con quindici file quasi identici, e i quindici file quasi identici sono il modo più affidabile per ritrovarsi con una modifica applicata in dieci posti su undici.
Dove sei arrivato
Il tema ha una struttura vera: intestazione e piè di pagina condivisi, un menu gestibile dal backend, un’area widget opzionale e una regola chiara che decide quale file rispondere a ogni richiesta. Da qui in avanti aggiungere un layout non significa più modificare codice esistente, ma creare un file con il nome giusto — ed è la proprietà che rende un tema custom sostenibile nel tempo.
Se stai costruendo un tema per un progetto reale e vuoi un confronto su come impostare la struttura prima di andare troppo avanti, sviluppo temi e plugin WordPress su misura: posso occuparmene io o affiancarti sull’architettura.

Lascia un commento