A package to use JSX and TSX with the full power of Vanilla JS minus the constraints of React.
tsconfig.json:
{
"jsx": "react-jsx",
"jsxImportSource": "reactfree-jsx"
}// App.tsx
export default function App() {
return (
<div>This is what JSX looks like.</div>
);
}
// main.ts
import App from "./App.jsx";
document.body.appendChild(App());function Counter({ initialCount }: {
initialCount: number;
}) {
return (
<div>
<p className="count">Count: {initialCount}</p>
</div>
);
}
// Calling the component function
<Counter initialCount={0} />;An obs function is provided to work with reactive values.
import { obs } from "reactfree-jsx";
function Counter({ initialCount }: {
initialCount: number;
}) {
const count = obs(initialCount);
return (
<div>
<!-- The text node will be updated automatically
when the value of `count` changes. -->
<p className="count">Count: {count}</p>
<button on:click={() => count.value++}>+</button>
<button on:click={() => count.value--}>-</button>
</div>
);
}Elements props can be reactive. An attribute's value will thus be mapped on to the value of an observable.
const hidden = obs(true);
const toggleHidden = () => {
hidden.value = !hidden.value;
};
return (
<>
<button on:click={toggleHidden}>Toggle</button>
<p hidden={hidden}>Hidden text</p>
</>
);An element's class list can be reactive.
const isGradient = obs(true);
<div
className={{
"bg-primary": true,
"bg-gradient": isGradient
}}
></div>;An element's style property accepts both static and dynamic values.
const divStyle = {
color: "white",
backgroundColor: isRedBackground.map((value) => value ? "red" : "blue"),
};
<div style={divStyle}></div>;Elements also have a unique $init prop whose value is a function which takes in the current element and will be run after the element is created and all its other props have been added.
const initDiv = (element: HTMLElement): void => {
console.log(element);
};
<div $init={initDiv}></div>;It's possible to refer to an element indirectly using the utility function createRef.
import { createRef } from "reactfree-jsx";
const ref = createRef<HTMLDivElement>();
const toggleActive = (): void => {
ref?.value?.classList.toggle("active");
};
<div className="active" $ref={ref}>
<button on:click={toggleActive}>Toggle active</button>
</div>An alternative to the native EventTarget class. Allows emitting and listening to events with typed parameters.
import { TypedEventEmitter } from "reactfree-jsx/extra";
const emitter = new TypedEventEmitter<{
start: [date: Date];
end: [];
}>();
emitter.on("start", (date) => {
console.log(`start date = ${date}`);
});
emitter.emit("start", new Date());Handle localStorage and sessionStorage with the benefit of type safety.
import { WebStore } from "reactfree-jsx/extra";
const store = new WebStore(localStorage, "todos", () => []);
const todos = store.getData();
store.setData([ { id: 1, task: "Finish writing docs." } ]);import { Router, Route } from "reactfree-jsx/extra/router";
<Router>
<Route path="/(home)?" component={HomePage} />
<Route path="/profile/:id" component={ProfilePage} />
</Router>ProfilePage.tsx:
export default function ProfilePage({ id }: {
id: string; // must match route path
}) {
return (
<div>Profile id: {id}</div>
);
}