@aerys/ui
v0.1.1
Published
Native Aerys UI — write React components in pure Aerys with no JSX. Provides h(), view(), and lowercase HTML-tag wrappers (button, div, h1, …).
Downloads
226
Maintainers
Readme
@aerys/ui
Native UI primitives for Aerys. Write React components in pure Aerys with zero JSX.
// Counter.aerx — pure Aerys, no '<' anywhere
let { button, div, h1 } = require("@aerys/ui/tags")
component Counter() {
let count = signal(0)
return div({ className = "counter" },
h1({}, `Count: @{count:get()}`),
button({ onClick = fn() { count:set(count:get() + 1) } }, "+1"),
)
}The .aerx file above compiles to standard React. Tailwind, Radix,
shadcn-ui, framer-motion, every other React-ecosystem library still
works — they're just functions you call in Aerys style.
Install
npm install @aerys/uiYou also need @aerys/aerysx
v0.2.0+ wired into your Vite config so .aerx files are compiled, and
react 17/18/19 as a peer dependency.
What you get
Three subpath entry points so bundlers tree-shake naturally:
@aerys/ui/runtime
The minimum surface for spelling React without JSX.
let { h, view, Fragment } = require("@aerys/ui/runtime")| Export | Behaviour |
| --- | --- |
| h(tag, props, ...children) | Universal element constructor. tag is a string ("div") or a component reference. props may be null/undefined. |
| view(...children) | Fragment shortcut. Returns a single child unwrapped, multiple children inside a Fragment, zero children as null. |
| Fragment | Re-export of React.Fragment for users who prefer to spell it. |
In practice you rarely call h directly — the AerysX compiler
auto-imports it whenever it sees h(...) or view(...) in your
file. Calling them in code "just works."
@aerys/ui/tags
Lowercase wrappers for 130+ HTML and SVG elements. Each one is a thin
(props, ...children) => React.createElement(name, props, ...children)
function so it composes naturally with everything else.
Three patterns, pick whichever feels right:
1) Destructure — most explicit, best tree-shaking:
let { div, h1, p, button, ul, li, form, input, table, svg, path } =
require("@aerys/ui/tags")2) tags proxy — one import, every tag, no upfront list:
let { tags } = require("@aerys/ui/tags")
tags.div({}, tags.h1({}, "Title"), tags.button({}, "Click"))
// Hyphenated / web-component names work via bracket access:
tags["my-counter"]({ count = 1 })The proxy caches each wrapper after first access, so repeated reads
return the same function reference — tags.section === tags.section
is true — keeping React's referential-equality optimizations
(memo, useCallback deps) intact.
3) Universal h() — for fully dynamic tag names:
let { h } = require("@aerys/ui/runtime")
let level = 2
let heading = h(`h@{level}`, {}, "Dynamic")The full named-export list lives in
src/tags.ts.
@aerys/ui (barrel)
Imports everything from both modules. Convenient for quick scripts:
let { h, view, button, div } = require("@aerys/ui")For app code prefer the subpath imports — they're explicit about what you're using and produce smaller bundles.
How the compiler hooks in
@aerys/aerysx v0.2.0 added three transforms specifically for this
package:
component NAME(...)is treated like a top-levelfn— it compiles tofunction NAME(...)with auto-export defaultfor the first one in a file.- Top-level
let { a, b } = require("path")is rewritten toimport { a, b } from "path".let X = require("path")becomes a default import. h(...)andview(...)auto-import from@aerys/ui/runtimewhen used and not already imported.
You don't have to opt in — write Aerys, the compiler handles the rest.
Why no JSX?
Aerys is a complete language with its own syntax for tables,
templates, signals, and lambdas. Writing <button onClick={...}> in a
.aerx file means switching dialects mid-line. With @aerys/ui the
file is Aerys all the way down, but the output is still React — so
every component you've already written and every library you depend on
keeps working.
License
Proprietary. See LICENSE.
