@miqro/jsx
v1.0.2
Published
JSX vDOM with hooks. runs server-side and in the browser via runtime adapters.
Readme
@miqro/jsx
JSX vDOM with hooks. runs server-side and in the browser via runtime adapters.
runtimes: browser → @miqro/jsx-dom · server → @miqro/jsx-node
tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "JSX.createElement",
"jsxFragmentFactory": "JSX.Fragment"
}
}usage
import JSX in every .tsx file:
import JSX from "@miqro/jsx";
function Hello() {
return <p>Hello</p>;
}hooks
useState
import JSX, { useState } from "@miqro/jsx";
function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{n}</button>;
}useEffect
import JSX, { useState, useEffect } from "@miqro/jsx";
function Timer() {
const [n, setN] = useState(0);
useEffect(() => {
const t = setTimeout(() => setN(n + 1), 1000);
return () => clearTimeout(t);
}, [n]);
return <p>{n}</p>;
}effects are disabled during SSR by default. but you can enable them with runtime options.
useContext
import JSX, { createContext, useContext } from "@miqro/jsx";
const ThemeContext = createContext();
function App() {
return <ThemeContext.provider value="dark">
<Child />
</ThemeContext.provider>;
}
function Child() {
const theme = useContext(ThemeContext);
return <p>{theme}</p>;
}useQuery
syncs state with URL query string.
import JSX, { useQuery } from "@miqro/jsx";
function Page() {
const [page, setPage] = useQuery("page", 1);
return <button onClick={() => setPage(page + 1)}>page {page}</button>;
}usePathname
import JSX, { usePathname } from "@miqro/jsx";
function Nav() {
const path = usePathname();
return <p>current: {path}</p>;
}useRef
import JSX, { useRef, useEffect } from "@miqro/jsx";
function Input() {
const ref = useRef(null);
useEffect(() => {
if (ref.current) ref.current.focus();
}, []);
return <input ref={ref} />;
}useElement
access the underlying DOM element the component renders into.
import JSX, { useElement } from "@miqro/jsx";
function Component() {
const el = useElement();
// el is the RuntimeHTMLElement
}useRefresh
force re-render.
import JSX, { useRefresh } from "@miqro/jsx";
function Component() {
const refresh = useRefresh();
// refresh() forces re-render
}useRuntime
import JSX, { useRuntime } from "@miqro/jsx";
function Component() {
const runtime = useRuntime();
// runtime.name === "dom" | "node"
}Router / Link
client-side routing.
import JSX, { Router, Link, Route } from "@miqro/jsx";
function App() {
return <Router>
<nav>
<Link href="/home">Home</Link>
<Link href="/about">About</Link>
</nav>
<Route path="/home"><Home /></Route>
<Route path="/about"><About /></Route>
</Router>;
}runtimes
same component code runs on both runtimes.
server (Node.js)
import { createNodeRuntime } from "@miqro/jsx-node";
import { createContainer, createElement } from "@miqro/jsx";
import { MyComponent } from "./component.js";
const runtime = createNodeRuntime({ url: { pathname: "/", searchParams: new URLSearchParams(), toString: () => "/" } });
const root = runtime.createElement("div");
const container = runtime.createContainer(root);
container.render(createElement(MyComponent, { title: "hello" }));
console.log(root.toString()); // <div><p>hello</p></div>
container.disconnect();browser
import { createDOMContainer } from "@miqro/jsx-dom";
import { createElement } from "@miqro/jsx";
import { MyComponent } from "./component.js";
const root = document.getElementById("root");
const container = createDOMContainer(root);
container.render(createElement(MyComponent, { title: "hello" }));component options
shadowInit
function MyComponent() { ... }
MyComponent.shadowInit = false; // no shadow DOM
MyComponent.shadowInit = { mode: "open" }; // open shadow root
// default: { mode: "closed" }asFragment
render without wrapper element.
function MyComponent() { ... }
MyComponent.asFragment = true;debug
import { enableDebugLog } from "@miqro/jsx";
enableDebugLog();