{"id":15413418,"url":"https://github.com/i-like-robots/hyperons","last_synced_at":"2025-04-14T10:06:10.967Z","repository":{"id":44164840,"uuid":"128735449","full_name":"i-like-robots/hyperons","owner":"i-like-robots","description":"🔥 The fastest JSX to string renderer on the server and in the browser.","archived":false,"fork":false,"pushed_at":"2024-12-09T21:30:55.000Z","size":721,"stargazers_count":48,"open_issues_count":2,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-14T10:06:06.691Z","etag":null,"topics":["html","hyperscript","jsx"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/hyperons","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/i-like-robots.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2018-04-09T07:56:31.000Z","updated_at":"2025-01-16T06:32:55.000Z","dependencies_parsed_at":"2023-02-01T01:01:00.803Z","dependency_job_id":"fd969db5-dc98-4778-b8b3-a0ac9c51ea9a","html_url":"https://github.com/i-like-robots/hyperons","commit_stats":{"total_commits":212,"total_committers":7,"mean_commits":"30.285714285714285","dds":0.25,"last_synced_commit":"51a0dd56b5767203a143506e220466a34aac9c7c"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-like-robots%2Fhyperons","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-like-robots%2Fhyperons/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-like-robots%2Fhyperons/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/i-like-robots%2Fhyperons/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/i-like-robots","download_url":"https://codeload.github.com/i-like-robots/hyperons/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248860285,"owners_count":21173342,"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","hyperscript","jsx"],"created_at":"2024-10-01T16:56:59.072Z","updated_at":"2025-04-14T10:06:10.941Z","avatar_url":"https://github.com/i-like-robots.png","language":"JavaScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Hyperons\" src=\"https://cdn.rawgit.com/i-like-robots/hyperons/4b788429/assets/hyperons.svg\" width=\"400\"\u003e\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/i-like-robots/hyperons/blob/main/LICENSE) ![build status](https://github.com/i-like-robots/hyperons/actions/workflows/test.yml/badge.svg?branch=main) [![Coverage Status](https://coveralls.io/repos/github/i-like-robots/hyperons/badge.svg?branch=main)](https://coveralls.io/github/i-like-robots/hyperons?branch=main) [![npm version](https://img.shields.io/npm/v/hyperons.svg?style=flat)](https://www.npmjs.com/package/hyperons)\n\n\u003c/div\u003e\n\nThe fastest and smallest JSX to string renderer on the server and in the browser.\n\n## Installation\n\nThis is a [Node.js][node] module available through the [npm][npm] registry. Before installing, download and install Node.js. Node.js 14 or higher is required.\n\nInstallation is done using the [npm install][install] command:\n\n```sh\n$ npm install -S hyperons\n```\n\n[node]: https://nodejs.org/en/\n[npm]: https://www.npmjs.com/\n[install]: https://docs.npmjs.com/getting-started/installing-npm-packages-locally\n\n## Features\n\n- [The fastest](#benchmarks) JSX to string renderer and tiny code size (1.2kb gzipped)\n- Render components on the server and in the browser\n- Works with class components and functional components, even ones that use [hooks](#hooks)\n- Support for context, CSS stringification, boolean attributes, void elements, fragments, and more\n\n## Usage\n\nThis module provides two functions; one to create elements and one to render them. If you've worked with [React][react] or React-like libraries before then they're the equivalent to `React.createElement()` and `ReactDOM.renderToString()`.\n\nThe example below shows how to render a simple component using Hyperons with vanilla JavaScript:\n\n[react]: https://reactjs.org/\n\n```js\nimport { h, render } from 'hyperons'\n\nconst Welcome = () =\u003e\n  h(\n    'div',\n    { className: 'welcome-banner' },\n    h('h1', null, 'Hello World!'),\n    h('p', null, 'This component was rendered with Hyperons')\n  )\n\nrender(Welcome())\n```\n\nAlthough you can use Hyperons without a compilation step, I'd recommend using [JSX](#jsx) to more succinctly describe your markup. Here is the same component as before but rewritten to use JSX syntax:\n\n```jsx\nimport { h, render } from 'hyperons'\n\nconst Welcome = () =\u003e (\n  \u003cdiv className=\"welcome-banner\"\u003e\n    \u003ch1\u003eHyperons\u003c/h1\u003e\n    \u003cp\u003eThis component was rendered with Hyperons\u003c/p\u003e\n  \u003c/div\u003e\n)\n\nrender(\u003cWelcome /\u003e)\n```\n\n## API\n\n### `Hyperons.h()` / `Hyperons.createElement()`\n\n```js\nHyperons.h(type[, props][, ...children])\n```\n\nReturns an element with the given `props`. It accepts the following arguments:\n\n- `type` The type of element to create which can be the name of an HTML element (such as `\"div\"`), a [component](#components), or a [fragment](#fragments).\n- `props` An object containing data to pass to a component or HTML attributes to render. See the [props documentation](#props) for more information.\n- `...children` Any number of child elements which may be simple values or other elements. See the [children documentation](#children) for more information.\n\n### `Hyperons.render()` / `Hyperons.renderToString()`\n\n```js\nHyperons.render(element)\n```\n\nReturns the rendered `element` as a string. It accepts the following arguments:\n\n- `element` An element created with `Hyperons.h()`\n\n### `Hyperons.Component`\n\nComponents can be defined as classes or functions. Components written as classes should extend `Hyperons.Component`:\n\n```jsx\nimport { h, Component } from 'hyperons'\n\nclass Welcome extends Component {\n  render() {\n    return \u003ch1\u003eHello, {this.props.name}\u003c/h1\u003e\n  }\n}\n```\n\nThe only method you must define for a class component is `render()`. See the [component syntax](#components) documentation for more information.\n\n### `Hyperons.Fragment`\n\nA `Fragment` is a special component which enables multiple elements to be rendered without a wrapper. See the [using fragments](#fragments) documentation for more information.\n\n```jsx\nimport { h, Fragment } from 'hyperons'\n\nconst DescriptionList = () =\u003e {\n  return (\n    \u003cdl\u003e\n      {props.definitions.map((item) =\u003e (\n        \u003cFragment\u003e\n          \u003cdt\u003e{item.title}\u003c/dt\u003e\n          \u003cdd\u003e{item.description}\u003c/dd\u003e\n        \u003c/Fragment\u003e\n      ))}\n    \u003c/dl\u003e\n  )\n}\n```\n\n### `Hyperons.Suspense`\n\n`Suspense` is a special component which renders a fallback for lazy-loaded or async children. Hyperons only renders static HTML so this component exists only for compatibility purposes and will not render its children.\n\n```jsx\nimport { h, Suspense } from 'hyperons'\n\nconst AsyncComponent = () =\u003e {\n  return (\n    \u003cSuspense fallback={\u003cLoading /\u003e}\u003e\n      \u003cSomeComponent /\u003e\n    \u003c/Suspense\u003e\n  )\n}\n```\n\n### `Hyperons.createContext`\n\nCreates a new [context](#context) object. Components which subscribe to this context will read the current context value from the closest matching context provider above it in the tree. Hyperons largely supports the same [context API](https://reactjs.org/docs/context.html) as React including accessing context via the `Class.contextType` property and `useContext()` [hook](#hooks).\n\n```jsx\nimport { h, createContext } from 'hyperons'\n\nconst MyContext = createContext()\n\nconst ChildComponent = () =\u003e {\n  return \u003cMyContext.Consumer\u003e{(ctx) =\u003e \u003cp\u003e{ctx.text}\u003c/p\u003e}\u003c/MyContext.Consumer\u003e\n}\n\nconst ParentComponent = () =\u003e {\n  return (\n    \u003cMyContext.Provider value={{ text: 'Hello, World!' }}\u003e\n      \u003cChildComponent /\u003e\n    \u003c/MyContext.Provider\u003e\n  )\n}\n```\n\n### Hooks\n\nReact v16.8 introduced hooks which enable developers to add state, persistent data, and hook into lifecycle events from functional components. Hyperons renders static HTML so there is no state nor lifecycle methods but shims for the following hooks are currently supported:\n\n| Hook            | Behavior                                                             |\n| --------------- | -------------------------------------------------------------------- |\n| useCallback     | Returns the given function                                           |\n| useContext      | Fully functional, see [context](#context)                            |\n| useEffect       | No op                                                                |\n| useLayoutEffect | No op                                                                |\n| useMemo         | Invokes the given function and returns the value                     |\n| useReducer      | Returns the given value and a no op function, calls init if provided |\n| useRef          | Returns the given value wrapped in an object                         |\n| useState        | Returns the given value and a no op function                         |\n\n```jsx\nimport { h, useCallback, useState } from 'hyperons'\n\nfunction Counter() {\n  const [count, setCount] = useState(0)\n  const onIncrement = useCallback(() =\u003e setCount(count + 1), [count])\n  const onDecrement = useCallback(() =\u003e setCount(count - 1), [count])\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={onDecrement}\u003e-\u003c/button\u003e\n      \u003cdiv\u003e{count}\u003c/div\u003e\n      \u003cbutton onClick={onIncrement}\u003e+\u003c/button\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\n## Syntax\n\n### Components\n\nComponents are reusable pieces of UI which can be composed in useful ways. There are two types of components supported by Hyperons:\n\n- Functional components which are regular JavaScript functions which accept `props` and return elements.\n- Class components, which are ES6 classes extending `Hyperons.Component` and have a `render()` method which returns elements.\n\nHere is an example showing the same component written using a function and as a class:\n\n```jsx\nimport { h, Component } from 'hyperons'\n\n// Functional component\nfunction SubmitButtonFn(props) {\n  return \u003cbutton type=\"submit\"\u003e{props.text}\u003c/button\u003e\n}\n\n// Class component\nclass SubmitButtonClass extends Component {\n  render() {\n    return \u003cbutton type=\"submit\"\u003e{this.props.text}\u003c/button\u003e\n  }\n}\n```\n\nWhen using React or React-like libraries class components are usually used to add extra functionality such as hooking into lifecycle methods and maintain state. Hyperons renders static HTML so there is no state nor lifecycle methods.\n\n### Props\n\nProps are objects either containing data to share with components or [HTML attributes](#html-attributes) for a HTML element. A component should never modify the props it receives.\n\n```js\n// Pass data to a component as props\nHyperons.createElement(SubmitButton, { text: 'Submit' })\n\n// Render props as HTML attributes\nHyperons.createElement('button', { type: 'submit' })\n```\n\nDefault prop values can be defined on components by adding a `defaultProps` property. These will be combined with any props received by the component:\n\n```jsx\n// Functional component\nfunction SubmitButtonFn(props) {\n  // ...\n}\n\nSubmitButtonFn.defaultProps = {\n  text: 'Submit'\n}\n\n// Class component\nclass SubmitButtonClass extends Hyperons.Component {\n  // ...\n\n  static get defaultProps() {\n    return {\n      text: 'Submit'\n    }\n  }\n}\n```\n\n### HTML Attributes\n\nWhen props are used to render [attributes] some property names and values will be treated differently by Hyperons:\n\n- Because `class` and `for` are [reserved words][words] in JavaScript you may use the aliases `className` and `htmlFor` instead.\n\n- Boolean attributes, such as `hidden` or `checked`, will only be rendered if assigned a [truthy][truthy] value.\n\n- Enumerated attributes which accept the values `\"true\"` or `\"false\"`, such as `contenteditable`, will be rendered with their assigned value.\n\n- Any attributes requiring hyphens, such as `aria-*` and `data-*` should be written with hyphens.\n\n- Framework specific props such as `key` and `ref` will not be rendered.\n\n- Attributes with a function value, such as event handlers, will not be rendered.\n\n[attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes\n[words]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords\n[truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy\n\n### Styles\n\nThe `style` attribute accepts a JavaScript object containing CSS properties and values.\n\nCSS Properties may be written in camelCase for consistency with accessing the properties with JavaScript in the browser (e.g. `element.style.marginBottom`). Vendor prefixes other than `ms` should always begin with a capital letter (e.g. `WebkitHyphens`).\n\nHyperons will automatically append a `px` suffix to number values but certain properties will remain unit-less (e.g. `z-index` and `order`). If you want to use units other than `px`, you should specify the value as a string with the desired unit. For example:\n\n```jsx\n// Input:\nconst styles = {\n  display: 'flex',\n  order: 2,\n  width: '50%',\n  marginBottom: 20,\n  WebkitHyphens: 'auto',\n}\n\nHyperons.render(\u003cdiv style={styles}\u003e\u003c/div\u003e)\n\n// Output:\n\u003cdiv style=\"display:flex;order:2;width:50%;margin-bottom:20px;-webkit-hyphens:auto;\u003e\u003c/div\u003e\n```\n\n### HTML entities\n\nHyperons will escape all string values so if you need to output a HTML entity you can run into issues with double escaping. The simplest way to work-around this issue is to write the unicode character directly in your code (and use UTF-8 encoding for you source files). Otherwise, you can find the [unicode number][charcode] for the required character. For example:\n\n```jsx\n// Incorrect. Outputs: \u003ch1\u003eMac \u0026amp;amp; Cheese\u003c/h1\u003e\n\u003ch1\u003eMac \u0026amp; Cheese\u003c/h1\u003e\n// Correct. Outputs: \u003ch1\u003eMac \u0026amp; Cheese\u003c/h1\u003e\n\u003ch1\u003eMac \u0026 Cheese\u003c/h1\u003e\n// Correct. Outputs: \u003ch1\u003eMac \u0026amp; Cheese\u003c/h1\u003e\n\u003ch1\u003e{`Mac ${String.fromCharCode(38)} Cheese`}\u003c/h1\u003e\n```\n\n[charcode]: https://www.fileformat.info/info/charset/UTF-8/list.htm\n\n### Inner HTML\n\nHyperons supports the `dangerouslySetInnerHTML` property to inject unescaped HTML code. This is potentially dangerous and should never be used around any user input, but it can be useful as a last resort.\n\n```jsx\nconst html = { __html: '\u003ci\u003eMac \u0026amp; Cheese\u003c/i\u003e' }\nHyperons.render(\u003cdiv dangerouslySetInnerHTML={html}\u003e\u003c/div\u003e)\n\n// Output:\n\u003cdiv\u003e\u003ci\u003eMac \u0026amp; Cheese\u003c/i\u003e\u003c/div\u003e\n```\n\n### Children\n\nComponents can render any number of child elements. Children can be strings, numbers, or other components. Components will receive references to any children via a `children` prop which enables components to be composed in useful ways.\n\n```jsx\nconst Wrapper = (props) =\u003e \u003cp\u003e{props.children}\u003c/p\u003e\nconst html = \u003cWrapper\u003eHello\u003c/Wrapper\u003e // Outputs: \u003cp\u003eHello\u003c/p\u003e\n```\n\n_Please note_ that child elements will not be rendered for [void elements][void].\n\n[void]: https://www.w3.org/TR/html/syntax.html#void-elements\n\n### Fragments\n\nIn React and React-like frameworks components must always return a single enclosing element. But sometimes it is required to return a list of elements, either because you don't need the extra elements or the extra elements would create invalid HTML output. For example, when rendering a description list the title and detail (`\u003cdt\u003e` and `\u003cdd\u003e`) elements are usually grouped in pairs:\n\n```jsx\nfunction DescriptionList(props) {\n  return (\n    \u003cdl\u003e\n      {props.definitions.map((item) =\u003e (\n        \u003cdt\u003e{item.title}\u003c/dt\u003e\n        \u003cdd\u003e{item.description}\u003c/dd\u003e\n      ))}\n    \u003c/dl\u003e\n  )\n}\n```\n\nHowever, most tools will throw an error when evaluating the code above because the title and description elements are not wrapped in an single parent element but wrapping them would result in invalid HTML.\n\nTo solve this [React 16.2][react-16] introduced the concept of [fragments] which enable a list of elements to be wrapped inside an enclosing element without rendering anything extra. To use fragments in your JSX code Hyperons provides a special `Fragment` component:\n\n```jsx\nfunction DescriptionList(props) {\n  return (\n    \u003cdl\u003e\n      {props.definitions.map((item) =\u003e (\n        \u003cHyperons.Fragment\u003e\n          \u003cdt\u003e{item.title}\u003c/dt\u003e\n          \u003cdd\u003e{item.description}\u003c/dd\u003e\n        \u003c/Hyperons.Fragment\u003e\n      ))}\n    \u003c/dl\u003e\n  )\n}\n```\n\n[react-16]: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html\n[fragments]: https://reactjs.org/docs/fragments.html\n\n### Context\n\nIn React and React-like frameworks context provides a way to share values between components without using props to pass it down through every level of the component tree. Hyperons largely supports the same [context API](https://reactjs.org/docs/context.html) as React - contexts can be created with a default value, values updated with a `\u003cProvider\u003e`, and context consumed via `Class.contextType`, `\u003cConsumer\u003e`, or `useContext` [hook](#hooks).\n\n```jsx\nimport { h, createContext, useContext } from 'hyperons'\n\nconst Context = createContext({ text: 'Default value' })\n\n// Functional component using a context consumer\nfunction ComponentWithConsumer() {\n  return \u003cContext.Consumer\u003e{(ctx) =\u003e \u003cp\u003e{ctx.text}\u003c/p\u003e}\u003c/Context.Consumer\u003e\n}\n\n// Functional component using the context hook\nfunction ComponentWithHook() {\n  const ctx = useContext(Context)\n  return \u003cp\u003e{ctx.text}\u003c/p\u003e\n}\n\n// Class component subscribing by contextType\nclass ComponentClass extends Component {\n  render() {\n    return \u003cp\u003e{this.context.text}\u003c/p\u003e\n  }\n}\n\nComponentClass.contextType = Context\n\n// Replacing the default value with a provider\nfunction ComponentWithProvide() {\n  return (\n    \u003cContext.Provider value={{ text: 'Updated value' }}\u003e\n      \u003cComponentWithConsumer /\u003e\n      \u003cComponentWithHook /\u003e\n    \u003c/Context.Provider\u003e\n  )\n}\n```\n\n### JSX\n\n_Not familiar with JSX? Check out [WTF is JSX][wtf] and [JSX in Depth][in-depth] first._\n\nIf you're authoring your components with JSX syntax you will need to transpile your code into plain JavaScript in order to run them. Depending on the toolchain you're using there will be different plugins available. Some popular tools to transpile JavaScript are [Babel] (with [the React preset][babel-react]), [ESBuild] and [Sucrase].\n\n[wtf]: https://jasonformat.com/wtf-is-jsx/\n[in-depth]: https://reactjs.org/docs/jsx-in-depth.html\n[babel]: https://babeljs.io/\n[babel-react]: https://babeljs.io/docs/en/babel-preset-react\n[esbuild]: https://esbuild.github.io/\n[sucrase]: https://github.com/alangpierce/sucrase\n\n## Project information\n\n### Development\n\nThis project is written using ES2020 syntax, [Prettier] for code formatting, [ESLint] for static analysis, is tested with [Vitest], and bundled using [Vite].\n\n[prettier]: https://prettier.io/\n[eslint]: https://eslint.org/\n[vitest]: https://vitest.dev/\n[vite]: https://vite.dev\n\n### Benchmarks\n\nThis repository contains benchmarking and profiling tools in the `/benchmark` directory. The current results for server-side rendering are below:\n\n```\nBenchmark run on Sun  1 Jan 2023 20:49:00 GMT with Node v16.13.2\n\nUsing:\n - hyperapp@2.0.22\n - hyperons@2.0.0\n - inferno@8.0.5\n - nervjs@1.5.7\n - preact@10.11.3\n - rax@1.2.2\n - react@18.2.0\n - vdo@4.2.0\n\nResults:\n - Hyperapp x 13,592 ops/sec ±0.39% (95 runs sampled)\n - Hyperons x 21,145 ops/sec ±0.32% (99 runs sampled)\n - Inferno x 17,764 ops/sec ±0.30% (98 runs sampled)\n - Nerv x 10,485 ops/sec ±0.78% (93 runs sampled)\n - Preact x 16,010 ops/sec ±0.33% (95 runs sampled)\n - Rax x 12,282 ops/sec ±0.35% (96 runs sampled)\n - React x 14,212 ops/sec ±0.32% (97 runs sampled)\n - vdo x 12,765 ops/sec ±0.54% (97 runs sampled)\n\nThe fastest is: [ 'Hyperons' ]\n```\n\n### Name\n\n\u003e In particle physics, a hyperon is any baryon containing one or more strange quarks, but no charm\n\n— [Wikipedia](https://simple.wikipedia.org/wiki/Hyperon)\n\nIn keeping with React and the wider ecosystem I wanted to give this project a science-related name but also something that implies being small and light. Thus, Hyperons.\n\n### Prior art\n\nThis module was originally inspired by the [vhtml] package and also borrows from a few other JSX to string implementations:\n\n- [Hyperapp Render] (style stringification)\n- [React DOM] (boolean attributes)\n- [Rax] (performance improvements)\n\n[vhtml]: https://github.com/developit/vhtml\n[hyperapp render]: https://github.com/hyperapp/render\n[react dom]: https://github.com/facebook/react/tree/main/packages/react-dom\n[rax]: https://github.com/alibaba/rax\n\n### License\n\nHyperons is MIT licensed.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi-like-robots%2Fhyperons","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fi-like-robots%2Fhyperons","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fi-like-robots%2Fhyperons/lists"}