Templates et props
Chacun des six composants de template reçoit un objet de props typé plus un qui porte l'état global. Cette page documente ce que reçoit chaque template et comment utiliser les props.
Chacun des six composants de template reçoit un objet de props typé plus un site: SiteContext qui porte l'état global. Cette page documente ce que reçoit chaque template et comment utiliser les props.
Commun : SiteContext
Chaque template (et le BaseLayout) reçoit site: SiteContext :
interface SiteContext {
settings: SiteSettings; // titre, description, langue, baseUrl, …
resolvedMenus: {
header: ResolvedMenuItem[]; // déjà résolus (pas de refs raw post/term)
footer: ResolvedMenuItem[];
};
themeCssPath: string; // ex. "theme-assets/default.css"
themeConfig?: TConfig; // votre config thème mergée
homePath?: string; // pour le lien du logo (utile en multilang)
}
Accès dans le template :
function HomeTemplate({ site, posts }) {
return (
<main>
<h1>{site.settings.title}</h1>
<p>{site.settings.description}</p>
{/* … */}
</main>
);
}
BaseLayout
interface BaseLayoutProps {
site: SiteContext;
pageTitle: string; // calculé par le publisher
pageDescription?: string;
ogImage?: string;
currentPath: string; // chemin de la page courante
currentLocale?: string; // langue courante (multilang)
children: React.ReactNode; // le template enfant
}
C'est le wrapper de toutes les pages. Voir Vue d'ensemble pour la structure minimale incluant les sentinels obligatoires.
HomeTemplate
interface HomeTemplateProps {
site: SiteContext;
posts: CardPost[]; // posts récents (par défaut ~20)
pinnedPost?: CardPost; // si vous voulez featurer un post
}
interface CardPost extends Post {
url: string; // URL relative résolue
hero?: MediaView;
category?: { name: string; url: string };
}
Rendu pour /index.html quand settings.homeMode === "latest". Pas utilisé en mode static-page (le SingleTemplate est utilisé à la place).
SingleTemplate
interface SingleTemplateProps {
site: SiteContext;
post: CardPost; // forme enrichie de Post
bodyHtml: string; // HTML rendu du markdown
author?: AuthorView; // résolu depuis users
relatedPosts?: CardPost[]; // si le thème en surface
}
interface AuthorView {
uid: string;
displayName: string; // "Prénom Nom"
bio?: string;
avatarUrl?: string;
}
Rendu pour chaque post + page. bodyHtml est déjà passé par les filtres post.markdown.before et post.html.body — vous pouvez l'insérer tel quel via dangerouslySetInnerHTML.
function SingleTemplate({ site, post, bodyHtml, author }) {
return (
<article>
<header>
<h1>{post.title}</h1>
{author && <p>by {author.displayName}</p>}
</header>
<div dangerouslySetInnerHTML={{ __html: bodyHtml }} />
</article>
);
}
CategoryTemplate
interface CategoryTemplateProps {
site: SiteContext;
term: Term; // la catégorie (avec seo)
posts: CardPost[]; // posts dans cette catégorie
categoryRssUrl?: string; // si flexweg-rss a un feed pour ce term
archivesLink?: string;
allCategories?: Term[]; // autres catégories pour navigation
popularTags?: Term[];
}
Rendu pour chaque archive de catégorie. Le term.seo doit être utilisé pour <title> / <meta description> via le BaseLayout.
AuthorTemplate
interface AuthorTemplateProps {
site: SiteContext;
author: AuthorView;
posts: CardPost[]; // posts de cet auteur
}
Rendu pour /authors/<uid>.html. Beaucoup de thèmes n'exposent pas d'archives d'auteur — si c'est votre cas, mettez un composant minimaliste ou rendez un 404.
NotFoundTemplate
interface NotFoundTemplateProps {
site: SiteContext;
currentPath: string;
}
Rendu pour /404.html. Page statique unique qui s'affiche pour toute URL non-trouvée (à condition que votre hébergement la serve comme fallback 404).
Le bodyHtml est déjà transformé
Pour SingleTemplate, bodyHtml est passé par :
core/markdown.renderMarkdown(marked + DOMPurify)- Le filtre async
post.html.body(vos blocs de thème, embeds, etc.)
Donc quand vous l'inserrer via dangerouslySetInnerHTML, le contenu est complet et sûr.
Astuce : helper localePrefix
Pour préfixer les URLs avec la locale courante (utile en multilang) :
function localePrefix(homePath?: string): string {
if (!homePath || !homePath.startsWith("/")) return "";
const m = homePath.match(/^\/([a-z]{2,3})\//);
return m ? `/${m[1]}` : "";
}
// Dans le template :
const prefix = localePrefix(site.homePath);
const href = `${prefix}/about.html`;
Le marketplace-core utilise ce pattern dans son Sidebar.tsx pour que les liens restent dans la langue courante.
Astuce : canonicalUrl
import { canonicalUrl } from "@flexweg/cms-runtime";
const canonical = canonicalUrl(site.settings.baseUrl, currentPath);
// "/index.html" → "https://monsite.flexweg.com/"
// "/news/launch.html" → "https://monsite.flexweg.com/news/launch.html"
return (
<html>
<head>
<link rel="canonical" href={canonical} />
</head>
{/* ... */}
</html>
);
Toujours utiliser canonicalUrl pour <link rel="canonical"> et les sitemap entries — sinon vous obtenez les URLs sales en .html.