hast utility to transform a tree to preact, react, solid, svelte, vue, etcetera, with an automatic JSX runtime.
- What is this?
- When should I use this?
- Install
- Use
- API
- Errors
- Examples
- Syntax
- Compatibility
- Security
- Related
- Contribute
- License
This package is a utility that takes a hast tree and an automatic JSX runtime and turns the tree into anything you wish.
You can use this package when you have a hast syntax tree and want to use it with whatever framework.
This package uses an automatic JSX runtime, which is a sort of lingua franca for frameworks to support JSX.
Notably, automatic runtimes have support for passing extra information in development, and have guaranteed support for fragments.
This package is ESM only. In Node.js (version 16+), install with npm:
npm install hast-util-to-jsx-runtime
In Deno with esm.sh
:
import {toJsxRuntime} from 'https://esm.sh/hast-util-to-jsx-runtime@2'
In browsers with esm.sh
:
<script type="module">
import {toJsxRuntime} from 'https://esm.sh/hast-util-to-jsx-runtime@2?bundle'
</script>
import {h} from 'hastscript'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {Fragment, jsxs, jsx} from 'react/jsx-runtime'
import {renderToStaticMarkup} from 'react-dom/server'
const tree = h('h1', 'Hello, world!')
const doc = renderToStaticMarkup(toJsxRuntime(tree, {Fragment, jsxs, jsx}))
console.log(doc)
Yields:
<h1>Hello, world!</h1>
Note: to add better type support, register a global JSX namespace:
import type {JSX as Jsx} from 'react/jsx-runtime' declare global { namespace JSX { type ElementClass = Jsx.ElementClass type Element = Jsx.Element type IntrinsicElements = Jsx.IntrinsicElements } }
This package exports the identifier toJsxRuntime
.
It exports the TypeScript types
Components
,
CreateEvaluater
,
ElementAttributeNameCase
,
EvaluateExpression
,
EvaluateProgram
,
Evaluater
,
ExtraProps
,
Fragment
,
Jsx
,
JsxDev
,
Options
,
Props
,
Source
,
Space
,
and
StylePropertyNameCase
.
There is no default export.
Transform a hast tree to preact, react, solid, svelte, vue, etcetera, with an automatic JSX runtime.
Result from your configured JSX runtime
(JSX.Element
if defined,
otherwise unknown
which you can cast yourself).
Possible components to use (TypeScript type).
Each key is a tag name typed in JSX.IntrinsicElements
,
if defined.
Each value is either a different tag name
or a component accepting the corresponding props
(and an optional node
prop if passNode
is on).
You can access props at JSX.IntrinsicElements
.
For example,
to find props for a
,
use JSX.IntrinsicElements['a']
.
import type {Element} from 'hast'
type ExtraProps = {node?: Element | undefined}
type Components = {
[TagName in keyof JSX.IntrinsicElements]:
| Component<JSX.IntrinsicElements[TagName] & ExtraProps>
| keyof JSX.IntrinsicElements
}
type Component<ComponentProps> =
// Class component:
| (new (props: ComponentProps) => JSX.ElementClass)
// Function component:
| ((props: ComponentProps) => JSX.Element | string | null | undefined)
Create an evaluator that turns ESTree ASTs from embedded MDX into values (TypeScript type).
There are no parameters.
Evaluater (Evaluater
).
Casing to use for attribute names (TypeScript type).
HTML casing is for example
class
, stroke-linecap
, xml:lang
.
React casing is for example
className
, strokeLinecap
, xmlLang
.
type ElementAttributeNameCase = 'html' | 'react'
Turn an MDX expression into a value (TypeScript type).
expression
(Expression
from@types/estree
) — estree expression
Result of expression (unknown
).
Turn an MDX program (export/import statements) into a value (TypeScript type).
program
(Program
from@types/estree
) — estree program
Result of program (unknown
);
should likely be undefined
as ESM changes the scope but doesn’t yield
something.
Evaluator that turns ESTree ASTs from embedded MDX into values (TypeScript type).
evaluateExpression
(EvaluateExpression
) — evaluate an expressionevaluateProgram
(EvaluateProgram
) — evaluate a program
Extra fields we pass (TypeScript type).
type ExtraProps = {node?: Element | undefined}
Represent the children, typically a symbol (TypeScript type).
type Fragment = unknown
Create a production element (TypeScript type).
type
(unknown
) — element type:Fragment
symbol, tag name (string
), componentprops
(Props
) — element props,children
, and maybenode
key
(string
orundefined
) — dynamicly generated key to use
Element from your framework
(JSX.Element
if defined,
otherwise unknown
which you can cast yourself).
Create a development element (TypeScript type).
type
(unknown
) — element type:Fragment
symbol, tag name (string
), componentprops
(Props
) — element props,children
, and maybenode
key
(string
orundefined
) — dynamicly generated key to useisStaticChildren
(boolean
) — whether two or more children are passed (in an array), which is whetherjsxs
orjsx
would be usedsource
(Source
) — info about sourceself
(undefined
) — nothing (this is used by frameworks that have components, we don’t)
Element from your framework
(JSX.Element
if defined,
otherwise unknown
which you can cast yourself).
Configuration (TypeScript type).
Fragment
(Fragment
, required) — fragmentjsxDEV
(JsxDev
, required in development) — development JSXjsxs
(Jsx
, required in production) — static JSXjsx
(Jsx
, required in production) — dynamic JSXcomponents
(Partial<Components>
, optional) — components to usecreateEvaluater
(CreateEvaluater
, optional) — create an evaluator that turns ESTree ASTs into valuesdevelopment
(boolean
, default:false
) — whether to usejsxDEV
when on orjsx
andjsxs
when offelementAttributeNameCase
(ElementAttributeNameCase
, default:'react'
) — specify casing to use for attribute namesfilePath
(string
, optional) — file path to the original source file, passed in source info tojsxDEV
when using the automatic runtime withdevelopment: true
passNode
(boolean
, default:false
) — pass the hast element node to componentsspace
(Space
, default:'html'
) — whethertree
is in the'html'
or'svg'
space, when an<svg>
element is found in the HTML space, this package already automatically switches to and from the SVG space when entering and exiting itstylePropertyNameCase
(StylePropertyNameCase
, default:'dom'
) — specify casing to use for property names instyle
objectstableCellAlignToStyle
(boolean
, default:true
) — turn obsoletealign
props ontd
andth
into CSSstyle
props
Properties and children (TypeScript type).
import type {Element} from 'hast'
type Props = {
[prop: string]:
| Array<JSX.Element | string | null | undefined> // For `children`.
| Record<string, string> // For `style`.
| Element // For `node`.
| boolean
| number
| string
| undefined
children: Array<JSX.Element | string | null | undefined> | undefined
node?: Element | undefined
}
Info about source (TypeScript type).
columnNumber
(number
orundefined
) — column where thing starts (0-indexed)fileName
(string
orundefined
) — name of source filelineNumber
(number
orundefined
) — line where thing starts (1-indexed)
Namespace (TypeScript type).
👉 Note: hast is not XML; it supports SVG as embedded in HTML; it does not support the features available in XML; passing SVG might break but fragments of modern SVG should be fine; use
xast
if you need to support SVG as XML.
type Space = 'html' | 'svg'
Casing to use for property names in style
objects (TypeScript type).
CSS casing is for example background-color
and -webkit-line-clamp
.
DOM casing is for example backgroundColor
and WebkitLineClamp
.
type StylePropertyNameCase = 'css' | 'dom'
The following errors are thrown:
This error is thrown when either options
is not passed at all or
when options.Fragment
is undefined
.
The automatic JSX runtime needs a symbol for a fragment to work.
To solve the error, make sure you are passing the correct fragment symbol from your framework.
This error is thrown when options.development
is turned on (true
),
but when options.jsxDEV
is not a function.
The automatic JSX runtime, in development, needs this function.
To solve the error,
make sure you are importing the correct runtime functions
(for example, 'react/jsx-dev-runtime'
),
and pass jsxDEV
.
These errors are thrown when options.development
is not turned on
(false
or not defined),
and when options.jsx
or options.jsxs
are not functions.
The automatic JSX runtime, in production, needs these functions.
To solve the error,
make sure you are importing the correct runtime functions
(for example, 'react/jsx-runtime'
),
and pass jsx
and jsxs
.
This error is thrown when MDX nodes are passed that represent JavaScript programs or expressions.
Supporting JavaScript can be unsafe and requires a different project.
To support JavaScript,
pass a createEvaluater
function in options
.
This error is thrown when a style
attribute is found on an element,
which cannot be parsed as CSS.
Most frameworks don’t accept style
as a string,
so we need to parse it as CSS,
and pass it as an object.
But when broken CSS is used,
such as style="color:red; /*"
,
we crash.
To solve the error,
make sure authors write valid CSS.
Alternatively,
pass options.ignoreInvalidStyle: true
to swallow these errors.
👉 Note: you must set
elementAttributeNameCase: 'html'
for preact.
In Node.js, do:
import {h} from 'hastscript'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {Fragment, jsx, jsxs} from 'preact/jsx-runtime'
import {render} from 'preact-render-to-string'
const result = render(
toJsxRuntime(h('h1', 'hi!'), {
Fragment,
jsx,
jsxs,
elementAttributeNameCase: 'html'
})
)
console.log(result)
Yields:
<h1>hi!</h1>
In a browser, do:
import {h} from 'https://esm.sh/hastscript@9'
import {toJsxRuntime} from 'https://esm.sh/hast-util-to-jsx-runtime@2'
import {Fragment, jsx, jsxs} from 'https://esm.sh/preact@10/jsx-runtime'
import {render} from 'https://esm.sh/preact@10'
render(
toJsxRuntime(h('h1', 'hi!'), {
Fragment,
jsx,
jsxs,
elementAttributeNameCase: 'html'
}),
document.getElementById('root')
)
To add better type support, register a global JSX namespace:
import type {JSX as Jsx} from 'preact/jsx-runtime'
declare global {
namespace JSX {
type ElementClass = Jsx.ElementClass
type Element = Jsx.Element
type IntrinsicElements = Jsx.IntrinsicElements
}
}
👉 Note: you must set
elementAttributeNameCase: 'html'
andstylePropertyNameCase: 'css'
for Solid.
In Node.js, do:
import {h} from 'hastscript'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {Fragment, jsx, jsxs} from 'solid-jsx/jsx-runtime'
console.log(
toJsxRuntime(h('h1', 'hi!'), {
Fragment,
jsx,
jsxs,
elementAttributeNameCase: 'html',
stylePropertyNameCase: 'css'
}).t
)
Yields:
<h1 >hi!</h1>
In a browser, do:
import {h} from 'https://esm.sh/hastscript@9'
import {toJsxRuntime} from 'https://esm.sh/hast-util-to-jsx-runtime@2'
import {Fragment, jsx, jsxs} from 'https://esm.sh/solid-js@1/h/jsx-runtime'
import {render} from 'https://esm.sh/solid-js@1/web'
render(Component, document.getElementById('root'))
function Component() {
return toJsxRuntime(h('h1', 'hi!'), {
Fragment,
jsx,
jsxs,
elementAttributeNameCase: 'html',
stylePropertyNameCase: 'css'
})
}
To add better type support, register a global JSX namespace:
import type {JSX as Jsx} from 'solid-js/jsx-runtime'
declare global {
namespace JSX {
type ElementClass = Jsx.ElementClass
type Element = Jsx.Element
type IntrinsicElements = Jsx.IntrinsicElements
}
}
I have no clue how to render a Svelte component in Node, but you can get that component with:
import {h} from 'hastscript'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {Fragment, jsx, jsxs} from 'svelte-jsx'
const svelteComponent = toJsxRuntime(h('h1', 'hi!'), {Fragment, jsx, jsxs})
console.log(svelteComponent)
Yields:
[class Component extends SvelteComponent]
Types for Svelte are broken. Raise it with Svelte.
👉 Note: you must set
elementAttributeNameCase: 'html'
for Vue.
In Node.js, do:
import serverRenderer from '@vue/server-renderer'
import {h} from 'hastscript'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {Fragment, jsx, jsxs} from 'vue/jsx-runtime' // Available since `vue@3.3`.
console.log(
await serverRenderer.renderToString(
toJsxRuntime(h('h1', 'hi!'), {
Fragment,
jsx,
jsxs,
elementAttributeNameCase: 'html'
})
)
)
Yields:
<h1>hi!</h1>
In a browser, do:
import {h} from 'https://esm.sh/hastscript@9'
import {toJsxRuntime} from 'https://esm.sh/hast-util-to-jsx-runtime@2'
import {createApp} from 'https://esm.sh/vue@3'
import {Fragment, jsx, jsxs} from 'https://esm.sh/vue@3/jsx-runtime'
createApp(Component).mount('#root')
function Component() {
return toJsxRuntime(h('h1', 'hi!'), {
Fragment,
jsx,
jsxs,
elementAttributeNameCase: 'html'
})
}
To add better type support, register a global JSX namespace:
import type {JSX as Jsx} from 'vue/jsx-runtime'
declare global {
namespace JSX {
type ElementClass = Jsx.ElementClass
type Element = Jsx.Element
type IntrinsicElements = Jsx.IntrinsicElements
}
}
HTML is parsed according to WHATWG HTML (the living standard), which is also followed by browsers such as Chrome, Firefox, and Safari.
Projects maintained by the unified collective are compatible with maintained versions of Node.js.
When we cut a new major release,
we drop support for unmaintained versions of Node.
This means we try to keep the current release line,
hast-util-to-jsx-runtime@2
,
compatible with Node.js 16.
Be careful with user input in your hast tree.
Use hast-util-santize
to make hast trees safe.
hastscript
— build hast treeshast-util-to-html
— serialize hast as HTMLhast-util-sanitize
— sanitize hast
See contributing.md
in
syntax-tree/.github
for ways to get started.
See support.md
for ways to get help.
This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.