{"id":13451880,"url":"https://github.com/esxjs/esx","last_synced_at":"2025-05-16T15:09:46.593Z","repository":{"id":55888428,"uuid":"168709768","full_name":"esxjs/esx","owner":"esxjs","description":"Like JSX, but native and fast","archived":false,"fork":false,"pushed_at":"2020-08-15T16:41:15.000Z","size":711,"stargazers_count":662,"open_issues_count":9,"forks_count":11,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-15T21:47:56.870Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/esxjs.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-01T14:30:46.000Z","updated_at":"2025-05-11T21:05:57.000Z","dependencies_parsed_at":"2022-08-15T08:40:39.001Z","dependency_job_id":null,"html_url":"https://github.com/esxjs/esx","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esxjs%2Fesx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esxjs%2Fesx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esxjs%2Fesx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esxjs%2Fesx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/esxjs","download_url":"https://codeload.github.com/esxjs/esx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254553959,"owners_count":22090417,"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":[],"created_at":"2024-07-31T07:01:05.579Z","updated_at":"2025-05-16T15:09:41.585Z","avatar_url":"https://github.com/esxjs.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# esx \n\n[![Build Status](https://img.shields.io/travis/esxjs/esx.svg)](https://travis-ci.org/esxjs/esx)\n[![Coverage](https://img.shields.io/codecov/c/github/esxjs/esx.svg)](https://codecov.io/gh/esxjs/esx)\n[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/)\n\nHigh throughput React Server Side Rendering\n\n\u003cimg src=\"https://avatars1.githubusercontent.com/u/45238803?s=400\u0026u=83a23d46289a2f151a58f18001458f1494174853\u0026v=4\" width=\"150\" alt=\"esx demo\"\u003e\n\n\nFor a simplified example of `esx` in action, check out [esx-demo](https://github.com/esxjs/esx-demo).\n\n`esx` is designed to be a **high speed SSR template engine for React**.\n\nIt can be used with **absolutely no code base changes**. \n\nUse it with a [preloader flag](https://nodejs.org/api/cli.html#cli_r_require_module) like so:\n\n```sh\nnode -r esx/optimize my-app.js\n```\n\n*Note: transpiling is still experimental*.\n\nAlternatively [babel-plugin-esx-ssr](https://github.com/esxjs/babel-plugin-esx-ssr) can be used to transpile for the same performance gains. The babel plugin would be a preferred option where speed of process initialization is important (such as serverless).\n\nOptionally, `esx` is also a universal **JSX-like syntax in plain JavaScript** that allows for the elimination of transpilation in development environments.\n\n* For the server side, using `esx` syntax  will yield the same high speed results as the optimizing preloader\n* For client side development, using `esx` syntax can enhance development workflow by removing the need for browser transpilation when developing in modern browsers\n* For client side production `esx` can be compiled away for production with [babel-plugin-esx-browser](https://github.com/esxjs/babel-plugin-esx-browser), resulting in zero-byte payload overhead. \n\nIt uses native [Tagged Templates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates) and works in all modern browsers.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/jsx-vs-esx.png\" width=\"800\" alt=\"esx demo\"\u003e\n\u003c/p\u003e\n\n## Status\n\nNot only is this project on-going, it's also following a moving\ntarget (the React implementation). \n\nThis should only be used in production when:\n\n* It has been verified to yield significant enough performance gains\n* It has been thoroughly verified against your current implementation\n\n`esx` needs use cases and battle testing. All issues are very welcome,\nPR's are extremely welcome and Collaborators are exceptionally, extraordinarily, exceedingly welcome.\n\n## Install\n\n```js\nnpm i esx\n```\n\n## Tests\n\nThere are close to 3000 passing tests.\n\n```sh\ngit clone https://github.com/esxjs/esx\ncd esx\nnpm i\nnpm test\nnpm run test:client # test client-side implementation in node\nnpm tun test:browser # test client-side implementation in browser\n```\n\n## Syntax\n\nCreating HTML with `esx` syntax is as close as possible to JSX:\n\n- Spread props: `\u003cdiv ...${props}\u003e`\n- Self-closing tags: `\u003cdiv /\u003e`\n- Attributes: `\u003cimg src=\"https://example.com/img.png\"/\u003e` and `\u003cimg src=${src}/\u003e`\n- Boolean attributes: `\u003cdiv draggable /\u003e`\n- Components: `\u003cFoo/\u003e`\n  - Components must be registered with `esx`: `esx.register({Foo})`\n\n## Compatibility\n\n* `react` v16.8+ is required as a peer dependency\n* `react-dom` v16.8+ is required as a peer dependency\n* `esx` is built for Node 10+\n* Supported Operating Systems: Windows, Linux, macOS\n\n## Limitations\n\n`esx` should cover the API surface of all *non-deprecated* React features.\n\nNotably, `esx` will not work with the [Legacy Context API](https://reactjs.org/docs/legacy-context.html),\nbut it will work with the [New Context API](https://reactjs.org/docs/context.html).\n\nWhile the legacy API is being phased out, there still may be modules in a \nprojects depedency tree that rely on the legacy API. If you desperately need\nsupport for the legacy API, \u003ca href=\"https://twitter.com/messages/compose?recipient_id=323355503\"\u003econtact me\u003c/a\u003e..\n\n## Usage\n\n### As an optimizer \n\nPreload `esx/optimize` like so:\n\n```sh\nnode -r esx/optimize my-app.js\n```\n\nThat's it. This will convert all `JSX` and `createElement` calls to ESX format, \nunlocking the throughput benefits of SSR template rendering.\n\n### As a JSX replacement\n\nAdditionally, `esx` can be written by hand for great ergonomic benefit\nin both server and client development contexts. Here's the example\nfrom the [`htm`](https://github.com/developit/htm) readme converted\nto `esx` (`htm` is discussed at the bottom of this readme):\n\n```js\n// using require instead of import allows for no server transpilation\nconst { Component } = require('react') \nconst esx = require('esx')()\nclass App extends Component {\n  addTodo() {\n    const { todos = [] } = this.state;\n    this.setState({ todos: todos.concat(`Item ${todos.length}`) });\n  }\n  render({ page }, { todos = [] }) {\n    return esx`\n      \u003cdiv class=\"app\"\u003e\n        \u003cHeader name=\"ToDo's (${page})\" /\u003e\n        \u003cul\u003e\n          ${todos.map(todo =\u003e esx`\n            \u003cli\u003e${todo}\u003c/li\u003e\n          `)}\n        \u003c/ul\u003e\n        \u003cbutton onClick=${() =\u003e this.addTodo()}\u003eAdd Todo\u003c/button\u003e\n        \u003cFooter\u003efooter content here\u003c/Footer\u003e\n      \u003c/div\u003e\n    `\n  }\n}\nconst Header = ({ name }) =\u003e esx`\u003ch1\u003e${name} List\u003c/h1\u003e`\nconst Footer = props =\u003e esx`\u003cfooter ...${props} /\u003e`\n\nesx.register({ Header, Footer })\n\nmodule.exports = App\n```\n\nIn a client entry point this can be rendered the usual way:\n\n```js\nconst App = require('./App')\nconst container = document.getElementById('app')\nconst { hydrate } = require('react-dom') // using hydrate because we have SSR\nconst esx = require('esx')({ App })\nhydrate(esx `\u003cApp page=\"All\"/\u003e`, container)\n```\n\nAnd the server entry point can use `esx.renderToToString` for high speed\nserver-side rendering:\n\n```js\nconst { createServer } = require('http')\nconst App = require('./App')\ncreateServer((req, res) =\u003e {\n  res.end(`\n    \u003chtml\u003e\n      \u003chead\u003e\u003ctitle\u003eTodo\u003c/title\u003e\u003c/head\u003e\n      \u003cbody\u003e\n        \u003cdiv id=\"app\"\u003e\n        ${esx.renderToString `\u003cApp page=\"All\"/\u003e`}\n        \u003c/div\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  `)\n}).listen(3000)\n```\n\n## API\n\nThe `esx` module exports an initializer function, which \nreturns a template string tag function.\n\n### Initializer: `createEsx(components = {}) =\u003e esx`\n\nThe default export is a function that when called initializes an \ninstance of `esx`. \n\n```js\nimport createEsx from 'esx'\n```\n\n```js\nconst createEsx = require('esx')\n```\n\nThe initializer takes an object of component mappings which \nit then uses to look up component references within the template.\n\nWhen called, the Initializer returns a Template Engine instance.\n\n### Template Engine: ``esx`\u003cmarkup/\u003e` =\u003e React Element``\n\nThe result of the Initializer is a Template Engine which \nshould always be assigned to `esx`. This is important\nfor editor syntax support. The Template Engine instance\nis a [template tag function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates).\n\n```js\nimport createEsx from 'esx'\nimport App from 'components/App'\nconst esx = createEsx({ App }) // same as {App: App}\n// `esx` is the Template Engine\nconsole.log(esx `\u003cApp/\u003e`) // exactly same result as React.createElement(App)\n```\n\n### Component Registration\n\nA component must be one of the following\n\n* function\n* class\n* symbol\n* object with a $$typeof key\n* string representing an element (e.g. 'div')\n\n#### `createEsx(components = {})`\n\nComponents passed to the Initializer are registered\nand validated at initialization time. Each key in the\n`components` object should correspond to the name of \na component referenced within an ESX template literal.\n\n#### `esx.register(components = {})`\n\nComponents can also be registered after initialization with the\n`esx.register` method:\n\n```js\nimport createEsx from 'esx'\nimport App from 'components/App'\nconst esx = createEsx()\nesx.register({ App })\n// exactly same result as React.createElement(App)\nconsole.log(esx `\u003cApp/\u003e`) \n```\n\nEach key in the `components` object should correspond to the name of a component as referenced within an ESX template literal.\n\n#### `esx.register.one(name, component)`\n\nA single component can be registered with the `esx.register.one` method.\nThe supplied `name` parameter must correspond to the name of a component \nreferenced within an ESX template literal and the `component` parameter\nwill be validated.\n\n#### `esx.register.lax(components = {})`\n\n**Advanced use only**. Use with care. This is a performance escape hatch. \nThis method will register components without validating. This may be used \nfor performance reasons such as when needing to register a component within a function. It is recommended to use the non-lax methods unless component validation\nin a specific scenario is measured as a bottleneck.\n\n#### `esx.register.lax.one(name, component)`\n\n**Advanced use only**. Use with care. This is a performance escape hatch. \nWill register one component without validating.\n\n### Server-Side Rendering: ``esx.renderToString`\u003cmarkup/\u003e` =\u003e String``\n\nOn the server side every Template Engine instance also has a\n`renderToString` method. The `esx.renderToString` method is\nalso a template literal tag function.\n\nThis **must** be used in place of the `react-dom/server` packages \n`renderToString` method in order to obtain the speed benefits.\n\n```js\nimport createEsx from 'esx'\nimport App from 'components/App'\nconst esx = createEsx()\nesx.register({ App })\n// same, but faster, result as ReactDomServer.renderToString(\u003cApp/\u003e)\nconsole.log(esx.renderToString `\u003cApp/\u003e`)\n```\n\n**Alias**: `esx.ssr`\n\n#### `esx.renderToString(EsxElement) =\u003e String`\n\nThe `esx.renderToString` method can also accept an element as its only\nparameter. \n\n```js\nimport createEsx from 'esx'\nimport App from 'components/App'\nconst esx = createEsx()\nesx.register({ App })\nconst app = esx `\u003cApp/\u003e`\n// same, but faster, result as ReactDomServer.renderToString(app)\nconsole.log(esx.renderToString(app))\n```\n\nElements created with `esx` contain template information and can\nbe used for high performance rendering, whereas a plain React element \nat the root could only ever be rendered with `ReactDomServer.renderToString`.\n\nThat is why `esx.renderToString` *will throw* if passed a plain \nReact element: \n\n```js\n// * DON'T DO THIS!: *\nesx.renderToString(React.createElement('div')) // =\u003e throws Error\n// instead do this:\nesx.renderToString `\u003cdiv/\u003e`\n// or this: \nesx.renderToString(esx `\u003cdiv/\u003e`)\n```\n\n### Plugins\n\nPre and Post plugins are also provided to allow for additional manipulation of templates and output. A Post plugin\ncould be used to write output directly to a stream, or inject additional\nHTML.\n\n#### `esx.plugins.pre((strings, ...values) =\u003e [strings, values])`\n\nThe `esx.plugins.pre` method registers a Pre plugin. Plugins \nwill be executed in the order that there are registered.\n\nA Pre plugin should be passed a function that has the same signature\nas a [tagged template function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates).\n\nIt must return an array containing two arrays. The first is an array of\nstrings, the second is an array of values.\n\nThe pre plugin can be used to reshape the strings array and/or apply mutations to the interpolated values. An example of a Pre plugin\ncould be to apply a transform, turning an alternative template syntax\n(such as Pug) into ESX syntax.\n\n#### `esx.plugins.post((htmlString) =\u003e htmlString))`\n\nThe `esx.plugins.post` method registers a Post plugin. Plugins \nwill be executed in the order that there are registered. Unlike\nPre plugins, Post plugins can only be used Server Side and will\nonly be invoked for components that are rendered via `esx.renderToString`.\n\nA Post plugin is passed the HTML string output of a component\nat the time it is rendered. A Post plugin can be used to inject\nextra HTML, apply additional transforms to the HTML, or capture\nthe HTML as each component is being rendered.\n\n\n### SSR Options\n\nOn the server side the Initializer has an `ssr` property, which\nhas an `options` method. The follow options are supported:\n\n#### `createEsx.ssr.option('hooks-mode', 'compatible'|'stateful')`\n\nBy default the `hooks-mode` option is `compatible` with React\nserver side rendering. This means that any stateful hooks, \ne.g. `useState` and `useReducer` do not actually retain state\nbetween renders. \n\nThe following will set `hooks-mode` to `stateful`:\n\n```js\ncreateEsx.ssr.option('hooks-mode', 'stateful')\n```\n\nThis means that `useState`, `useReducer`, `useMemo` and\n`useCallback` have the same stateful behaviour as their\nclient-side counterpart hooks. The state is retained\nbetween `renderToString` calls, instead of always returning\nthe initial state as with `compatible` mode. This can be useful \nwhere a server-side render-to-hydrate strategy is employed and\na great fit with rendering on server initialize.\n\n\n## Contributions\n\n`esx` is an **OPEN Open Source Project**. This means that:\n\n\u003e Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.\n\nSee the [CONTRIBUTING.md](https://github.com/esxjs/esx/blob/master/CONTRIBUTING.md) file for more details.\n\n## The Team\n\n### David Mark Clements\n\n\u003chttps://github.com/davidmarkclements\u003e\n\n\u003chttps://www.npmjs.com/~davidmarkclements\u003e\n\n\u003chttps://twitter.com/davidmarkclem\u003e\n\n## Prior Art\n\n### ESX\n`esx` was preceded by... `esx`. The `esx` namespace was registered four years ago \nby a prior author [Mattéo Delabre ](https://github.com/matteodelabre), with a similar \nidea. He kindly donated the namespace to this particular manifestation of the idea. \nFor this reason, `esx` versioning begins at v2.x.x.  Versions 0.x.x and 1.x.x are deprecated.\n\n### Hyperx\n\n`esx` is directly inspired by [`hyperx`](https://npm.im/hyperx), which\nwas the first known library to this authors knowledge to make the point\nthat template strings are perfect for generating both virtual doms \nand server side rendering. What `hyperx` lacks, however, is a way \nto represent React components within its template syntax. It is *only* \nfor generating HTML nodes.\n\n### HTM\n\nIt's not uncommon for similar ideas to be had and implemented concurrently\nwithout either party knowing of the other.\nWhile [`htm`](https://github.com/developit/htm) was first released early 2019, \nwork on `esx` had already been on-going some months prior. However the \nmission of `esx` is slightly broader, with a primary objective being to speed up \nserver side rendering, so it took longer to release.\n\n## License\n\n[MIT](./LICENSE)\n\n## Sponsors\n\n* [nearForm](https://nearform.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesxjs%2Fesx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fesxjs%2Fesx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesxjs%2Fesx/lists"}