Fast, Ultra Light weight (~5kb gzip), DOM-first, Rootless, with Native Observables, No configuration, Component-based UI library with Android-style View inflation.
Based on No Framework Principle.
bun i @denshya/proton
For the JSX and types to work properly, you should add this to your tsconfig.json
/jsconfig.json
It means you don't have to hijack an element in order to render the App, it cancles Root Component and Host element completely.
That is a novel wording, another good phrase is "Reversed Element Factory Ownership". These all stand for a component (or element) factory function producing/providing ownership to owned elements rather than being a side-effects function, which only modifies given element.
From querying the element and modifying
const element = document.getElementById("id")
makeWidget(element)
function makeWidget(element) {
element.style = "..."
// ... Some other styling and structure
}
To creating desired element and sharing ownership
function createWidget() { // returns element instead.
const element = document.createElement("div")
element.style = "..."
// ... Some other styling and structure
return element
}
This forces you to find the exact place where the new element should go, which may be tricky, this what Proton solves with JSX while still letting you choose the place to attach or reattach Proton Component.
Which allows you do to this: (Somewhat an alternative to Web Components)
function Widget() {
return <div style={...} /> // Some code.
}
Widget.Standalone = inflator.inflate(<Widget />)
const container = document.querySelector(".container")
container?.append(Widget.Standalone)
The turning point is that JSX element attributes and children can consume WICG Observables, meaning practically any library can be used as a State Manager.
const text = new State("")
const button = document.getElementById("button")
button.append(inflate.inflate(<div>{text}</div>))
Continue reading about JSX Reactivity
Adding your own JSX Attribute for any element is as easy as never.
For example, classMods
- it will ensure BEM for elements without anoying imports.
inflator.jsxAttributes.set("classMods", context => {
if (context.value == null) return
context.bind("className", bem(context.props.className, context.value))
})
More about customization
Unlike to React - Proton will not propogate thrown errors to parents - errors in Children will not break Parents while you still can catch them.
function Child() { throw new Error("Test") }
function Parent(this: Proton.Component) { return <div>123<Child /></div> }
document.body.append(inflate.inflate(<Parent />)) // Will render `123` without errors.
Learn how you catch errors
To maintain open internals this library uses Classes instead of Functions as factories and uses private
identifier in TypeScript,
which gives you propert types while not stopping you from experimenting with internal variables and even allowing you to override them in convential way.
If you want manage your components in a somewhat complex way (like in React), you can continue reading this, but otherwise you may want to consider these alternatives:
function App() {
return <div>Hello World!</div>
}
const inflator = new WebInflator
const AppView = inflator.inflate(<App />)
document.getElementById("root").replaceChildren(AppView)
Proton supports React-like JSX, except that it maps directly to Document elements and allows any value to be put into attributes or as children of any elements.
<div className="product-card">
<h2>Title</h2>
<p>Description</p>
<img src="/static/product-card.jpg" />
// You can put your weird staff.
<aside id={new MyIdObject} />
</div>
Learn more about Inflator to provide custom handlers.
Report if there are anything uncovered for TypeScript.