hast-util-to-jsx-runtime
hast utility to transform a tree to preact, react, solid, svelte, vue, etc.,
with an automatic JSX runtime.
Contents
What is this?
This package is a utility that takes a hast tree and an
automatic JSX runtime and turns the tree into anything you
wish.
When should I use this?
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.
Install
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>
Use
import {h} from 'hastscript'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {Fragment, jsx, jsxs} from 'react/jsx-runtime'
import {renderToStaticMarkup} from 'react-dom/server'
const tree = h('h1', 'Hello, world!')
const doc = renderToStaticMarkup(toJsxRuntime(tree, {Fragment, jsx, jsxs}))
console.log(doc)
Yields:
<h1>Hello, world!</h1>
API
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.
toJsxRuntime(tree, options)
Transform a hast tree to preact, react, solid, svelte, vue, etc., with an
automatic JSX runtime.
Parameters
tree
(Node
)
— tree to transformoptions
(Options
, required)
— configuration
Returns
Result from your configured JSX runtime (JSX.Element
).
Components
Possible components to use (TypeScript type).
Each key is a tag name typed in JSX.IntrinsicElements
.
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']
.
Type
import type {Element} from 'hast'
type Components = {
[TagName in keyof JSX.IntrinsicElements]:
| Component<JSX.IntrinsicElements[TagName] & ExtraProps>
| keyof JSX.IntrinsicElements
}
type ExtraProps = {node?: Element | undefined}
type Component<ComponentProps> =
| (new (props: ComponentProps) => JSX.ElementClass)
| ((props: ComponentProps) => JSX.Element | string | null | undefined)
CreateEvaluater
Create an evaluator that turns ESTree ASTs from embedded MDX into values
(TypeScript type).
Parameters
There are no parameters.
Returns
Evaluater (Evaluater
).
ElementAttributeNameCase
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
type ElementAttributeNameCase = 'html' | 'react'
EvaluateExpression
Turn an MDX expression into a value (TypeScript type).
Parameters
expression
(Expression
from @types/estree
)
— estree expression
Returns
Result of expression (unknown
).
EvaluateProgram
Turn an MDX program (export/import statements) into a value (TypeScript type).
Parameters
program
(Program
from @types/estree
)
— estree program
Returns
Result of program (unknown
);
should likely be undefined
as ESM changes the scope but doesn’t yield
something.
Evaluater
Evaluator that turns ESTree ASTs from embedded MDX into values (TypeScript
type).
Fields
Extra fields we pass (TypeScript type).
Type
type ExtraProps = {node?: Element | undefined}
Fragment
Represent the children, typically a symbol (TypeScript type).
Type
type Fragment = unknown
Jsx
Create a production element (TypeScript type).
Parameters
type
(unknown
)
— element type: Fragment
symbol, tag name (string
), componentprops
(Props
)
— element props, children
, and maybe node
key
(string
or undefined
)
— dynamicly generated key to use
Returns
Element from your framework (JSX.Element
).
JsxDev
Create a development element (TypeScript type).
Parameters
type
(unknown
)
— element type: Fragment
symbol, tag name (string
), componentprops
(Props
)
— element props, children
, and maybe node
key
(string
or undefined
)
— dynamicly generated key to useisStaticChildren
(boolean
)
— whether two or more children are passed (in an array), which is whether
jsxs
or jsx
would be usedsource
(Source
)
— info about sourceself
(undefined
)
— nothing (this is used by frameworks that have components, we don’t)
Returns
Element from your framework (JSX.Element
).
Options
Configuration (TypeScript type).
Fields
Fragment
(Fragment
, required)
— fragmentjsx
(Jsx
, required in production)
— dynamic JSXjsxs
(Jsx
, required in production)
— static JSXjsxDEV
(JsxDev
, required in development)
— development JSXcomponents
(Partial<Components>
, optional)
— components to usecreateEvaluater
(CreateEvaluater
, optional)
— create an evaluator that turns ESTree ASTs into valuesdevelopment
(boolean
, default: false
)
— whether to use jsxDEV
when on or jsx
and jsxs
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 to jsxDEV
when using the automatic runtime with development: true
passNode
(boolean
, default: false
)
— pass the hast element node to componentsspace
(Space
, default: 'html'
)
— whether tree
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 in style
objectstableCellAlignToStyle
(boolean
, default: true
)
— turn obsolete align
props on td
and th
into CSS style
props
Props
Properties and children (TypeScript type).
Type
import type {Element} from 'hast'
type Props = {
[prop: string]:
| Array<JSX.Element | string | null | undefined>
| Record<string, string>
| Element
| boolean
| number
| string
| undefined
children: Array<JSX.Element | string | null | undefined> | undefined
node?: Element | undefined
}
Source
Info about source (TypeScript type).
Fields
columnNumber
(number
or undefined
)
— column where thing starts (0-indexed)fileName
(string
or undefined
)
— name of source filelineNumber
(number
or undefined
)
— line where thing starts (1-indexed)
Space
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
type Space = 'html' | 'svg'
StylePropertyNameCase
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
type StylePropertyNameCase = 'css' | 'dom'
Errors
The following errors are thrown:
Expected `Fragment` in options
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.
Expected `jsxDEV` in options when `development: true`
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
.
Expected `jsx` in production options
Expected `jsxs` in production options
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
.
Cannot handle MDX estrees without `createEvaluater`
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
.
Cannot parse `style` attribute
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.
Examples
Example: Preact
👉 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@8'
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')
)
Example: Solid
👉 Note: you must set elementAttributeNameCase: 'html'
and
stylePropertyNameCase: '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@8'
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'
})
}
Example: Svelte
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]
Example: Vue
👉 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'
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@8'
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'
})
}
Syntax
HTML is parsed according to WHATWG HTML (the living standard), which is also
followed by browsers such as Chrome, Firefox, and Safari.
Compatibility
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.
Security
Be careful with user input in your hast tree.
Use hast-util-santize
to make hast trees safe.
Related
Contribute
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.
License
MIT © Titus Wormer