SkillAgentSearch skills...

Jsxte

A JSX based html templating engine for browsers or Node environments.

Install / Use

/learn @ncpa0/Jsxte
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

JSX Template Engine

NPM npm Libraries.io dependency status for latest release GitHub last commit

A JSX based html templating engine for browsers or Node environments.

  1. Getting started
    1. Installation
    2. Building
  2. Examples
  3. Asynchronous Components
  4. Context
    1. Example
    2. Provider/Consumer Pattern
  5. Error Boundaries
    1. Example
  6. toHtmlTag symbol
  7. DomRenderer
  8. JsxteRenderer
  9. Extending the typings
    1. Adding custom web component tags
    2. Adding a global html attribute
  10. Express JS View Engine
  11. Monkey-Patching type definitions
  12. Contributing

Getting started

Installation

npm i jsxte

or

yarn add jsxte

Building

To use the jsxte you will have to set up your transpiler to use this package for transforming the JSX syntax, if you use typescript for transpiling all you have to do is set these options in the tsconfig:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "jsxte"
  }
}

If you use something else, like babel you will also need to adapt the configuration of that, for example: https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#with-a-configuration-file-recommended

(See example configurations here).

Once you are done with that you can start writing your templates and rendering them.

import { createElement, renderToHtml } from "jsxte";

const Header: JSXTE.Component<{ label: string }> = (props) => {
  return <h1>{props.label}</h1>;
};

const App: JSXTE.Component<{ label: string }> = (props) => {
  return (
    <html>
      <head>
        <meta charset="utf-8" />
        <meta
          http-equiv="X-UA-Compatible"
          content="IE=edge"
        />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
      </head>
      <body>
        <Header label={props.label} />
      </body>
    </html>
  );
};

const html = renderToHtml(<App label="Hello World!" />);
// OR
const html = renderToHtml(createElement(App, { label: "Hello World!" }));

Examples

Check out these example repositories:

Asynchronous Components

In case you use the templates in a server app in a Node environment you might want to include some data from the database in the html you serve to the client. To make it easier to fetch what's needed and marry it with the templates you can make your components asynchronous and send async requests from within them.

import { renderToHtmlAsync } from "jsxte";

const Header: JSXTE.Component = () => {
  return <h1>Hello World</h1>;
};

const ToDoList: JSXTE.Component = async () => {
  const todos = await fetchMyTodosFromDB();

  return (
    <table>
      <thead>
        <tr>
          <th>Label</th>
          <th>Is Done?</th>
        </tr>
      </thead>
      <tbody>
        {todos.map((todo) => (
          <tr>
            <td>{todo.label}</td>
            <td>{todo.isDone ? "yes" : "no"}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

const App: JSXTE.Component = () => {
  return (
    <html>
      <head>
        <meta charset="utf-8" />
        <meta
          http-equiv="X-UA-Compatible"
          content="IE=edge"
        />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
      </head>
      <body>
        <Header />
        <h3>ToDo's:</h3>
        <ToDoList />
      </body>
    </html>
  );
};

// If your component contains an asynchronous component at any point, `renderToHtmlAsync` needs to be used instead of `renderToHtml`
const html = await renderToHtmlAsync(<App label="Hello World!" />);

Context

Context Map is a interface provided to each functional component that provides a mechanism for providing any arbitrary data to it's descendant. This is primarily to avoid the prop-drilling.

Example

import { defineContext } from "jsxte";

const myContext = defineContext<{ label: string }>();

const App: JSXTE.Component = (props, componentApi) => {
  // Set the context to a new value, all descendants of this component will have access to it
  componentApi.ctx.set(myContext, { label: "Hello" });

  return <Foo />;
};

const Foo: JSXTE.Component = (props, componentApi) => {
  let label = "";

  // Check if `myContext` is being provided by any of the ancestors
  if (componentApi.ctx.has(myContext)) {
    // Retrieve the context data
    label = componentApi.ctx.getOrFail(myContext).label;
  }

  return <p>{label}</p>;
};

Provider/Consumer Pattern

Context also provides a Provider and a Consumer components.

const MyContext = defineContext<string>();

const App: JSXTE.Component = () => {
  return (
    <MyContext.Provider value={"Hello World!"}>
      <div>
        <MyContext.Consumer
          render={(providedValue) => <h1>{providedValue ?? ""}</h1>}
        />
      </div>
    </MyContext.Provider>
  );
};

Error Boundaries

Error boundaries are components that catch errors thrown by their children and allow you to display a fallback UI instead of having the rendering outright fail.

Error boundaries work with both synchronous and asynchronous components. But the onError handler should never return an asynchronous component.

Example

import { ErrorBoundary, renderToHtml } from "jsxte";

class Boundary extends ErrorBoundary {
  render(props: JSXTE.ElementProps, componentApi: ComponentApi) {
    return <>{props.children}</>;
  }

  onError(
    error: unknown,
    originalProps: JSXTE.ElementProps,
    componentApi: ComponentApi,
  ) {
    return <h1>Something went wrong!</h1>;
  }
}

const FailingComponent: JSXTE.Component = () => {
  throw new Error("Unexpected failure!");
};

const html = renderToHtml(
  <div>
    <Boundary>
      <FailingComponent />
    </Boundary>
  </div>,
);

// html:
// <div>
//   <h1>Something went wrong!</h1>
// </div>

toHtmlTag symbol

Symobl.toHtmlTag is a special symbol that allows to determine how an object should be stringified when used as a child of a JSX element.

Example

class User {
  constructor(
    public id: string,
    public username: string,
    public email: string,
  ) {}

  [Symbol.toHtmlTag]() {
    return `User: ${this.username}`;
  }
}

const user = new User("001", "Johny", "johny0169@gmail.com");

renderToHtml(<div>{user}</div>);

Result:

<div>User: Johny</div>

DomRenderer

DomRenderer renders given JSX into a DOM object. It requires a window object to be passed to the constructor.

import { DomRenderer } from "jsxte";

const renderer = new DomRenderer(window);
const divElement = renderer.render(<div>Hello World!</div>);

divElement.outerHTML; // <div>Hello World!</div>
window.document.body.appendChild(divElement);

JsxteRenderer

JsxteRenderer is a base class around which HTML and JSON renderer are built upon. This renderer requires a specific interface that provides methods for creating the final output format:

// T is the type of the renderer return value
export interface ElementGenerator<T> {
  createElement(
    type: string,
    attributes: Array<[attributeName: string, attributeValue: any]>,
    children: Array<T>,
  ): T;
  createTextNode(text: string | number | bigint): T;
  createFragment(children: Array<T>): T;
}

It is possible to render to other formats than HTML or JSON by providing a custom ElementGenerator implementation to the renderer.

Example

import { JsxteRenderer } from "jsxte";

class DomGenerator
  implements ElementGenerator<HTMLElement | Text | DocumentFragment>
{
  createElement(
    type: string,
    attributes: Array<[attributeName: string, attributeValue: any]>,
    children: Array<HTMLElement | Text | DocumentFragment>,
  ): HTMLElement | Text | DocumentFragment {
    const element = document.createElement(type);
    for (const [name, value] of attributes) {
      element.setAttribute(name, value);
    }
    for (const child of children) {
      element.appendChild(child);
    }
    return element;
  }

  createTextNode(
    text: string | number | bigint,
  ): HTMLElement | Text | DocumentFragment {
    return document.createTextNode(String(text));
  }

  createFragment(
    children: Array<HTMLElement | Text | DocumentFragment>,
  ): HTMLElement | Text | DocumentFragment {
    const fragment = document.createDocumentFragment();
    for (const child of children) {
      fragment.appendChild(child);
    }
    return fragment;
  }
}

const renderer = new JsxteRenderer(new DomGenerator());
const divElement = renderer.render(<div>Hello World!</div>);

Extending the typings

JSXTE should be able to parse any html attributes you put in, as well as custom web component tags, although you may see type errors if you use anything that is not defined in the library typings. If you wish to use them it is recommended you extend the typings to disable said errors.

Adding custom web component tags

To add a typing for a custom web component simply add a declare block in one of your project

View on GitHub
GitHub Stars69
CategoryDevelopment
Updated3d ago
Forks3

Languages

TypeScript

Security Score

100/100

Audited on Apr 6, 2026

No findings