{"id":22521796,"url":"https://github.com/ncpa0/jsxte","last_synced_at":"2025-04-05T10:07:40.314Z","repository":{"id":39710035,"uuid":"491692141","full_name":"ncpa0/jsxte","owner":"ncpa0","description":"A JSX based html templating engine for browsers or Node environments.","archived":false,"fork":false,"pushed_at":"2025-03-10T16:46:34.000Z","size":3714,"stargazers_count":67,"open_issues_count":9,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-29T09:10:37.412Z","etag":null,"topics":["html","javascript","jsx","template-engine"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ncpa0.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-12T23:03:23.000Z","updated_at":"2025-03-21T06:38:19.000Z","dependencies_parsed_at":"2023-10-10T16:14:48.326Z","dependency_job_id":"633e5f12-66a3-462b-831e-8a7cb8f84330","html_url":"https://github.com/ncpa0/jsxte","commit_stats":null,"previous_names":["ncpa0cpl/jsxte"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncpa0%2Fjsxte","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncpa0%2Fjsxte/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncpa0%2Fjsxte/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ncpa0%2Fjsxte/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ncpa0","download_url":"https://codeload.github.com/ncpa0/jsxte/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247318744,"owners_count":20919484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["html","javascript","jsx","template-engine"],"created_at":"2024-12-07T05:13:00.364Z","updated_at":"2025-04-05T10:07:40.299Z","avatar_url":"https://github.com/ncpa0.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JSX Template Engine\n\n![NPM](https://img.shields.io/npm/l/jsxte?style=for-the-badge) [![npm](https://img.shields.io/npm/v/jsxte?style=for-the-badge)](https://www.npmjs.com/package/jsxte) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/jsxte?style=for-the-badge) ![GitHub last commit](https://img.shields.io/github/last-commit/ncpa0cpl/jsxte?style=for-the-badge)\n\nA JSX based html templating engine for browsers or Node environments.\n\n1. [Getting started](#getting-started)\n   1. [Installation](#installation)\n   2. [Building](#building)\n2. [Examples](#examples)\n3. [Asynchronous Components](#asynchronous-components)\n4. [Context](#context)\n   1. [Example](#example)\n   2. [Provider/Consumer Pattern](#providerconsumer-pattern)\n5. [Error Boundaries](#error-boundaries)\n   1. [Example](#example-1)\n6. [toHtmlTag symbol](#tohtmltag-symbol)\n7. [DomRenderer](#domrenderer)\n8. [JsxteRenderer](#jsxterenderer)\n9. [Extending the typings](#extending-the-typings)\n   1. [Adding custom web component tags](#adding-custom-web-component-tags)\n   2. [Adding a global html attribute](#adding-a-global-html-attribute)\n10. [Express JS View Engine](#express-js-view-engine)\n11. [Monkey-Patching type definitions](#monkey-patching-type-definitions)\n12. [Contributing](#contributing)\n\n## Getting started\n\n### Installation\n\n```bash\nnpm i jsxte\n```\n\nor\n\n```bash\nyarn add jsxte\n```\n\n### Building\n\nTo 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:\n\n```json\n{\n  \"compilerOptions\": {\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"jsxte\"\n  }\n}\n```\n\nIf 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\n\n(See example configurations [here](./docs/config-examples/)).\n\nOnce you are done with that you can start writing your templates and rendering them.\n\n```tsx\nimport { createElement, renderToHtml } from \"jsxte\";\n\nconst Header: JSXTE.Component\u003c{ label: string }\u003e = (props) =\u003e {\n  return \u003ch1\u003e{props.label}\u003c/h1\u003e;\n};\n\nconst App: JSXTE.Component\u003c{ label: string }\u003e = (props) =\u003e {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cmeta charset=\"utf-8\" /\u003e\n        \u003cmeta\n          http-equiv=\"X-UA-Compatible\"\n          content=\"IE=edge\"\n        /\u003e\n        \u003cmeta\n          name=\"viewport\"\n          content=\"width=device-width, initial-scale=1\"\n        /\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        \u003cHeader label={props.label} /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  );\n};\n\nconst html = renderToHtml(\u003cApp label=\"Hello World!\" /\u003e);\n// OR\nconst html = renderToHtml(createElement(App, { label: \"Hello World!\" }));\n```\n\n## Examples\n\nCheck out these example repositories:\n\n- [(Express + TypeScript) TODO App](https://github.com/ncpa0/jsxte-with-typescript-example)\n- [(Express + Babel) TODO App](https://github.com/ncpa0/jsxte-with-babel-example)\n\n## Asynchronous Components\n\nIn 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.\n\n```tsx\nimport { renderToHtmlAsync } from \"jsxte\";\n\nconst Header: JSXTE.Component = () =\u003e {\n  return \u003ch1\u003eHello World\u003c/h1\u003e;\n};\n\nconst ToDoList: JSXTE.Component = async () =\u003e {\n  const todos = await fetchMyTodosFromDB();\n\n  return (\n    \u003ctable\u003e\n      \u003cthead\u003e\n        \u003ctr\u003e\n          \u003cth\u003eLabel\u003c/th\u003e\n          \u003cth\u003eIs Done?\u003c/th\u003e\n        \u003c/tr\u003e\n      \u003c/thead\u003e\n      \u003ctbody\u003e\n        {todos.map((todo) =\u003e (\n          \u003ctr\u003e\n            \u003ctd\u003e{todo.label}\u003c/td\u003e\n            \u003ctd\u003e{todo.isDone ? \"yes\" : \"no\"}\u003c/td\u003e\n          \u003c/tr\u003e\n        ))}\n      \u003c/tbody\u003e\n    \u003c/table\u003e\n  );\n};\n\nconst App: JSXTE.Component = () =\u003e {\n  return (\n    \u003chtml\u003e\n      \u003chead\u003e\n        \u003cmeta charset=\"utf-8\" /\u003e\n        \u003cmeta\n          http-equiv=\"X-UA-Compatible\"\n          content=\"IE=edge\"\n        /\u003e\n        \u003cmeta\n          name=\"viewport\"\n          content=\"width=device-width, initial-scale=1\"\n        /\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        \u003cHeader /\u003e\n        \u003ch3\u003eToDo's:\u003c/h3\u003e\n        \u003cToDoList /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  );\n};\n\n// If your component contains an asynchronous component at any point, `renderToHtmlAsync` needs to be used instead of `renderToHtml`\nconst html = await renderToHtmlAsync(\u003cApp label=\"Hello World!\" /\u003e);\n```\n\n## Context\n\nContext 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.\n\n### Example\n\n```tsx\nimport { defineContext } from \"jsxte\";\n\nconst myContext = defineContext\u003c{ label: string }\u003e();\n\nconst App: JSXTE.Component = (props, componentApi) =\u003e {\n  // Set the context to a new value, all descendants of this component will have access to it\n  componentApi.ctx.set(myContext, { label: \"Hello\" });\n\n  return \u003cFoo /\u003e;\n};\n\nconst Foo: JSXTE.Component = (props, componentApi) =\u003e {\n  let label = \"\";\n\n  // Check if `myContext` is being provided by any of the ancestors\n  if (componentApi.ctx.has(myContext)) {\n    // Retrieve the context data\n    label = componentApi.ctx.getOrFail(myContext).label;\n  }\n\n  return \u003cp\u003e{label}\u003c/p\u003e;\n};\n```\n\n### Provider/Consumer Pattern\n\nContext also provides a Provider and a Consumer components.\n\n```tsx\nconst MyContext = defineContext\u003cstring\u003e();\n\nconst App: JSXTE.Component = () =\u003e {\n  return (\n    \u003cMyContext.Provider value={\"Hello World!\"}\u003e\n      \u003cdiv\u003e\n        \u003cMyContext.Consumer\n          render={(providedValue) =\u003e \u003ch1\u003e{providedValue ?? \"\"}\u003c/h1\u003e}\n        /\u003e\n      \u003c/div\u003e\n    \u003c/MyContext.Provider\u003e\n  );\n};\n```\n\n## Error Boundaries\n\nError 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.\n\nError boundaries work with both synchronous and asynchronous components. But the `onError` handler should never return an asynchronous component.\n\n### Example\n\n```tsx\nimport { ErrorBoundary, renderToHtml } from \"jsxte\";\n\nclass Boundary extends ErrorBoundary {\n  render(props: JSXTE.ElementProps, componentApi: ComponentApi) {\n    return \u003c\u003e{props.children}\u003c/\u003e;\n  }\n\n  onError(\n    error: unknown,\n    originalProps: JSXTE.ElementProps,\n    componentApi: ComponentApi,\n  ) {\n    return \u003ch1\u003eSomething went wrong!\u003c/h1\u003e;\n  }\n}\n\nconst FailingComponent: JSXTE.Component = () =\u003e {\n  throw new Error(\"Unexpected failure!\");\n};\n\nconst html = renderToHtml(\n  \u003cdiv\u003e\n    \u003cBoundary\u003e\n      \u003cFailingComponent /\u003e\n    \u003c/Boundary\u003e\n  \u003c/div\u003e,\n);\n\n// html:\n// \u003cdiv\u003e\n//   \u003ch1\u003eSomething went wrong!\u003c/h1\u003e\n// \u003c/div\u003e\n```\n\n## toHtmlTag symbol\n\n`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.\n\n### Example\n\n```tsx\nclass User {\n  constructor(\n    public id: string,\n    public username: string,\n    public email: string,\n  ) {}\n\n  [Symbol.toHtmlTag]() {\n    return `User: ${this.username}`;\n  }\n}\n\nconst user = new User(\"001\", \"Johny\", \"johny0169@gmail.com\");\n\nrenderToHtml(\u003cdiv\u003e{user}\u003c/div\u003e);\n```\n\nResult:\n\n```html\n\u003cdiv\u003eUser: Johny\u003c/div\u003e\n```\n\n## DomRenderer\n\n`DomRenderer` renders given JSX into a DOM object. It requires a window object to be passed to the constructor.\n\n```tsx\nimport { DomRenderer } from \"jsxte\";\n\nconst renderer = new DomRenderer(window);\nconst divElement = renderer.render(\u003cdiv\u003eHello World!\u003c/div\u003e);\n\ndivElement.outerHTML; // \u003cdiv\u003eHello World!\u003c/div\u003e\nwindow.document.body.appendChild(divElement);\n```\n\n## JsxteRenderer\n\n`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:\n\n```ts\n// T is the type of the renderer return value\nexport interface ElementGenerator\u003cT\u003e {\n  createElement(\n    type: string,\n    attributes: Array\u003c[attributeName: string, attributeValue: any]\u003e,\n    children: Array\u003cT\u003e,\n  ): T;\n  createTextNode(text: string | number | bigint): T;\n  createFragment(children: Array\u003cT\u003e): T;\n}\n```\n\nIt is possible to render to other formats than HTML or JSON by providing a custom `ElementGenerator` implementation to the renderer.\n\n### Example\n\n```tsx\nimport { JsxteRenderer } from \"jsxte\";\n\nclass DomGenerator\n  implements ElementGenerator\u003cHTMLElement | Text | DocumentFragment\u003e\n{\n  createElement(\n    type: string,\n    attributes: Array\u003c[attributeName: string, attributeValue: any]\u003e,\n    children: Array\u003cHTMLElement | Text | DocumentFragment\u003e,\n  ): HTMLElement | Text | DocumentFragment {\n    const element = document.createElement(type);\n    for (const [name, value] of attributes) {\n      element.setAttribute(name, value);\n    }\n    for (const child of children) {\n      element.appendChild(child);\n    }\n    return element;\n  }\n\n  createTextNode(\n    text: string | number | bigint,\n  ): HTMLElement | Text | DocumentFragment {\n    return document.createTextNode(String(text));\n  }\n\n  createFragment(\n    children: Array\u003cHTMLElement | Text | DocumentFragment\u003e,\n  ): HTMLElement | Text | DocumentFragment {\n    const fragment = document.createDocumentFragment();\n    for (const child of children) {\n      fragment.appendChild(child);\n    }\n    return fragment;\n  }\n}\n\nconst renderer = new JsxteRenderer(new DomGenerator());\nconst divElement = renderer.render(\u003cdiv\u003eHello World!\u003c/div\u003e);\n```\n\n## Extending the typings\n\nJSXTE 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.\n\n### Adding custom web component tags\n\nTo add a typing for a custom web component simply add a declare block in one of your project `.ts` or `.tsx` files, like this one:\n\n```tsx\ndeclare global {\n  namespace JSX {\n    interface IntrinsicElements {\n      \"my-custom-web-component\": {\n        /* here include the attributes your component can take */\n        \"data-example-attribute\"?: string;\n      };\n    }\n  }\n}\n\n// with it it's possible to use this without type errors:\nconst MyComponent: JSXTE.Component = () =\u003e (\n  \u003cmy-custom-web-component data-example-attribute=\"Hello\"\u003e\n  \u003c/my-custom-web-component\u003e\n);\n```\n\n### Adding a global html attribute\n\nThere is a dictionary of html attributes that are available for every default html tag, that dictionary can be extended like so:\n\n```tsx\ndeclare global {\n  namespace JSXTE {\n    interface BaseHTMLTagProps {\n      \"new-attribute\"?: string;\n    }\n  }\n}\n\n// with it it's possible to use this without type errors:\nconst MyComponent = () =\u003e \u003cdiv new-attribute=\"Hello\"\u003e\u003c/div\u003e;\n```\n\n## Express JS View Engine\n\nYou can also use `jsxte` with the Express View Engine. To do that, use the `expressExtend` to add the engine support, specify the views directory and then use the express response method `.render()`. The `.render()` method takes the component props as it's second argument.\n\n```ts\nimport express from \"express\";\nimport { expressExtend } from \"jsxte\";\n\nconst app = express();\nexpressExtend(app);\n\napp.set(\"views\", path.resolve(__dirname, \"views\"));\n\napp.get(\"/\", (_, resp) =\u003e {\n  const indexProps = {\n    /* ... */\n  };\n  resp.render(\"index\", indexProps); // will render the `index.js` component located in the ./views file\n});\n```\n\nFor this approach to work, the JSX Components must be exported as defaults (ex. `export default () =\u003e \u003cdiv\u003e\u003c/div\u003e` or `exports.default = () =\u003e \u003cdiv\u003e\u003c/div\u003e`) and the views must be transpiled to `.js` files.\n\n## Monkey-Patching type definitions\n\nIt is possible to monkey-patch type definition of all HTML tags and add new attributes to them.\n\n#### Extend prop types of a specific tag\n\nThe following adds a new attribute to the `\u003cdiv /\u003e` tag - `data-my-attr`:\n\n```tsx\ndeclare global {\n  namespace JSXTE {\n    interface DivTagProps {\n      \"data-my-attr\"?: string;\n    }\n  }\n}\n```\n\n#### Extends prop of all html tags\n\nThe following adds a new attribute to all html tags - `hx-post`:\n\n```tsx\ndeclare global {\n  namespace JSXTE {\n    interface BaseHTMLTagProps {\n      \"hx-post\"?: string;\n    }\n  }\n}\n```\n\n#### Change the accepted type for a specific attribute\n\nThe following adds a `Function` type to the `onclick` attribute of all html tags:\n\n```tsx\ndeclare global {\n  namespace JSXTE {\n    interface AttributeAcceptedTypes {\n      onclick?: Function;\n    }\n  }\n}\n```\n\n## Contributing\n\nIf you want to contribute please See [CONTRIBUTING.md](./CONTRIBUTING.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fncpa0%2Fjsxte","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fncpa0%2Fjsxte","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fncpa0%2Fjsxte/lists"}