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 :

TS
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 :

TSX
function HomeTemplate({ site, posts }) {
  return (
    <main>
      <h1>{site.settings.title}</h1>
      <p>{site.settings.description}</p>
      {/* … */}
    </main>
  );
}

BaseLayout

TS
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

TS
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

TS
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.

TSX
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

TS
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

TS
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

TS
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 :

  1. core/markdown.renderMarkdown (marked + DOMPurify)
  2. 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) :

TSX
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

TSX
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.