ENbext.dev
DocumentationPRISM (natif bext)

Traduction automatique depuis l’anglais. Consulter l’original.

Sur cette page

JSX intégré

bext inclut un runtime JSX intégré qui compile JSX en chaînes HTML. Il n'y a pas de DOM virtuel, pas de différence, pas de réconciliation. La fonction h() concatène les chaînes — la même chose que vous feriez avec les littéraux de modèle, mais avec la syntaxe JSX.

tsx
// This JSX:
<div className="card">
  <h2>{title}</h2>
  <p>{description}</p>
</div>

// Becomes this string:
// <div class="card"><h2>My Title</h2><p>My description</p></div>

Comment ça marche#

Le runtime JSX de bext exporte h() (hyperscript), jsx, jsxs et Fragment. Le mode react-jsx de TypeScript appelle jsx / jsxs ; les appelants directs peuvent utiliser h(). Au lieu de créer des nœuds DOM virtuels, les éléments intrinsèques renvoient SafeHtml, une sous-classe de marque String dont la valeur primitive est le fil HTML.

  • Les composants de fonction reçoivent des accessoires et renvoient une chaîne
  • Les balises HTML sont rendues sous forme de concaténation de chaînes
  • Les enfants sont aplatis et joints
  • Il n'y a pas de cycle de vie des composants, pas d'état, pas de hooks

Le runtime automatique réutilise l'objet props créé par le compilateur pour les éléments intrinsèques, car formatAttrs ignore déjà children. Pour les composants, il contourne la normalisation uniquement lorsque les enfants sont absents, scalaires ou dans un tableau dense déjà plat ; Les tableaux imbriqués, les valeurs nulles et les booléens conservent la sémantique d'aplatissement héritée.

Cela le rend idéal pour les pages rendues par le serveur où vous souhaitez bénéficier de l'ergonomie de JSX sans envoyer de code de framework au navigateur.

Configuration de tsconfig.json#

Configurez TypeScript pour utiliser le runtime JSX de bext :

json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@bext-stack/framework",
    "paths": {
      "@bext-stack/framework/*": ["../shared/framework/*"],
      "@bext-stack/framework": ["../shared/framework/src/index.ts"]
    }
  }
}

Avec cette configuration, TypeScript importe automatiquement le runtime JSX à partir de @bext-stack/framework/jsx-runtime. Aucune importation manuelle nécessaire.

Exemples de composants#

Composant simple#

tsx
function Header({ title, subtitle }: { title: string; subtitle?: string }) {
  return (
    <header>
      <h1>{title}</h1>
      {subtitle && <p className="subtitle">{subtitle}</p>}
    </header>
  );
}

// Returns: <header><h1>My Site</h1><p class="subtitle">Welcome</p></header>
const html = <Header title="My Site" subtitle="Welcome" />;

Composant de carte#

tsx
interface CardProps {
  title: string;
  body: string;
  href?: string;
  children?: string[];
}

function Card({ title, body, href, children }: CardProps) {
  const Tag = href ? "a" : "div";
  return (
    <Tag className="card" href={href}>
      <h3>{title}</h3>
      <p>{body}</p>
      {children}
    </Tag>
  );
}

const html = <Card title="Feature" body="Lightning fast" href="/features" />;
// <a class="card" href="/features"><h3>Feature</h3><p>Lightning fast</p></a>

Composant de mise en page#

tsx
function Layout({ title, children }: { title: string; children?: string[] }) {
  return (
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>{title}</title>
        <link rel="stylesheet" href="/styles.css" />
      </head>
      <body>
        <nav>
          <a href="/">Home</a>
          <a href="/about">About</a>
        </nav>
        <main>{children}</main>
        <footer>&copy; 2026</footer>
      </body>
    </html>
  );
}
tsx
interface NavItem {
  href: string;
  label: string;
}

function Nav({ items, currentPath }: { items: NavItem[]; currentPath: string }) {
  return (
    <nav className="sidebar">
      {items.map((item) => (
        <a
          href={item.href}
          className={currentPath === item.href ? "active" : ""}
        >
          {item.label}
        </a>
      ))}
    </nav>
  );
}

Mélange avec des chaînes de modèles#

bext JSX renvoie des chaînes simples, vous pouvez donc librement mélanger JSX et les littéraux de modèle :

tsx
import { h } from "@bext-stack/framework";

function renderPage(page: Page, ctx: RenderContext): string {
  const nav = (
    <nav>
      <a href="/">Home</a>
      <a href="/docs">Docs</a>
    </nav>
  );

  // Mix JSX output with template strings
  return `<!DOCTYPE html>
<html lang="en">
<head>
  <title>${page.title}</title>
</head>
<body>
  ${nav}
  <main>${page.html}</main>
  <footer>Built with bext</footer>
</body>
</html>`;
}

Ceci est utile lorsque votre shell HTML externe est statique et que vous souhaitez uniquement JSX pour les composants dynamiques.

Prise en charge des fragments#

Utilisez Fragment (ou le raccourci <>...</>) pour renvoyer plusieurs éléments sans wrapper :

tsx
import { Fragment } from "@bext-stack/framework";

function MetaTags({ title, description }: { title: string; description: string }) {
  return (
    <>
      <title>{title}</title>
      <meta name="description" content={description} />
      <meta property="og:title" content={title} />
    </>
  );
}

// Returns: <title>Hello</title><meta name="description" content="World"><meta property="og:title" content="Hello">

dangereusementSetInnerHTML#

Pour injecter du HTML brut sans s'échapper (pour une sortie markdown, un contenu utilisateur nettoyé, etc.) :

tsx
function MarkdownContent({ html }: { html: string }) {
  return (
    <article
      className="prose"
      dangerouslySetInnerHTML={{ __html: html }}
    />
  );
}

// The html string is inserted directly without escaping
const output = <MarkdownContent html="<h1>Hello</h1><p>World</p>" />;
// <article class="prose"><h1>Hello</h1><p>World</p></article>

Avertissement : Utilisez uniquement dangerouslySetInnerHTML avec du contenu fiable. Ne transmettez jamais d’entrées utilisateur non nettoyées.

Objets de style#

Transmettez un objet JavaScript à style et il sera converti en chaîne CSS. Les propriétés camelCase sont converties en kebab-case :

tsx
function Badge({ color, label }: { color: string; label: string }) {
  return (
    <span
      style={{
        backgroundColor: color,
        padding: "2px 8px",
        borderRadius: "4px",
        fontSize: "12px",
        fontWeight: 600,
      }}
    >
      {label}
    </span>
  );
}

// <span style="background-color:red;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:600">New</span>

className vers l'alias de classe#

Le runtime JSX de bext convertit automatiquement les noms d'attributs de style React en leurs équivalents HTML :

Attribut JSX Sortie HTML
className class
htmlFor for
httpEquiv http-equiv
tabIndex tabindex
crossOrigin crossorigin
autoComplete autocomplete
autoFocus autofocus
tsx
<label htmlFor="email" className="form-label">Email</label>
// <label for="email" class="form-label">Email</label>

<meta httpEquiv="refresh" content="5" />
// <meta http-equiv="refresh" content="5">

Attributs booléens#

La valeur booléenne true restitue l'attribut sans valeur. false ou null l'omet :

tsx
<input type="checkbox" checked={true} disabled={false} />
// <input type="checkbox" checked>

<details open={isOpen}>
  <summary>Click me</summary>
  <p>Content</p>
</details>

Éléments vides#

Les éléments HTML à fermeture automatique ( <img>, <br>, <input>, <meta>, <link>, etc.) sont rendus correctement sans balises de fermeture :

tsx
<img src="/photo.jpg" alt="A photo" />
// <img src="/photo.jpg" alt="A photo">

<br />
// <br>

<link rel="stylesheet" href="/styles.css" />
// <link rel="stylesheet" href="/styles.css">

Comparaison avec React#

Fonctionnalité bext JSX React
Sortir Chaîne HTML DOM virtuel
Taille du paquet 0 Ko (chaînes) ~140 Ko ( react + react -dom)
Gestion de l'État Aucun (serveur uniquement) useState, useReducer
Crochets de cycle de vie Aucun useEffect, useMemo, etc.
Hydratation des clients Aucun (zéro JS) Hydratation complète requise
Vitesse de rendu (appareil de 38,3 Ko) 69,3 µs direct / 75,3 µs automatique 1 823 µs
Cas d'utilisation Pages rendues par le serveur Applications client interactives

Utiliser bext JSX lorsque :

  • Vos pages sont rendues par le serveur sans interactivité client
  • Vous souhaitez que zéro JavaScript soit envoyé au navigateur
  • Vous avez besoin de performances de rendu maximales (au niveau de la microseconde)
  • Vous créez des sites de contenu, des documents, des blogs, des pages marketing

Utiliser React lorsque :

  • Vous avez besoin d'interactivité côté client (formulaires, animations, mises à jour en temps réel)
  • Vous disposez d'une bibliothèque de composants React existante.
  • Vous avez besoin de hooks, de contexte ou de gestion d'état

Vous pouvez également mélanger les deux : voir React, Preact et solide pour savoir comment intégrer des composants React dans les mises en page JSX bext.

En rapport#

  • Cadre PRISM — le framework de serveur construit sur ce runtime JSX
  • Passe de compilation PRISM — comment les appels JSX sont repliés en concats de chaînes au moment de la construction
  • Architecture des îles — envoi de composants côté client aux côtés du serveur JSX
  • Signaux — îlots réactifs sans React, utilisant la même syntaxe JSX
  • CSS vent arrière — générer du CSS à partir de l'utilisation de JSX className

Échappement HTML : ce qui est échappé et ce qui ne l'est pas#

Il s'agit de la note de sécurité la plus importante,, de ce document. Le texte et les attributs dynamiques sont sécurisés par défaut ; Le HTML brut reste un opt-in explicite.

Position Échappé? Exemple
Texte statique dans JSX Oui (temps de compilation) <p>5 < 10</p> → <p>5 &lt; 10</p>
Valeurs d'attribut Oui (environnement d'exécution formatAttrs ) <a title={user.bio}>
Enfants de chaîne d'expression {value} Oui (assistant d'exécution ou de compilation) <p>{userInput}</p>
JSX rendu imbriqué Transmis via SafeHtml <div>{<strong>safe</strong>}</div>
dangerouslySetInnerHTML Non (intentionnel) comme dans React

Lorsque vous écrivez <p>{userInput}</p>, une chaîne simple passe par escapeHtml. Lorsque l'enfant est sorti d'un autre élément PRISM, il transporte le signal hors bande. Marque SafeHtml et traverse sans double fuite. Les bundles compilés hérités qui portent toujours le marqueur réservé \x01 restent pris en charge, mais la nouvelle sortie du runtime ne met pas ce marqueur sur le fil.

Le texte dynamique normal ne nécessite aucun wrapper :

tsx
<p>{userInput}</p>
<input value={userInput} readOnly />

Si vous avez besoin d'une sortie HTML brute (markdown, texte riche épuré), utilisez dangerouslySetInnerHTML={{ __html: ... }} exactement comme React. Le nom de l'accessoire est l'avertissement : utilisez-le uniquement avec du contenu fiable ou nettoyé.

Attention

dangerouslySetInnerHTML contourne la fuite. Transmettez uniquement du HTML fiable ou nettoyé séparément à __html ; ne l'utilisez pas pour une saisie utilisateur ordinaire.

escapeHtml est toujours exporté pour les contextes non-JSX et pour les appels d'aide à la compilation. Il utilise des expressions régulières sur des chaînes sans &<>". Les attributs de classe ajoutent un cache de valeurs exactes limité à deux entrées pour les lignes répétées du tableau ; le cache stocke le résultat déjà échappé, de sorte qu'une valeur malveillante répétée reste échappée.

Modifier cette page ↗Besoin d’aide ? ↗
Rechercher dans la documentation

Saisissez un sujet, une commande ou une question.