Framework-less Framework
In my previous blog post I introduced the No Framework principle and argued that enforcing a rigid framework can sometimes stifle creativity and control. Today I’m excited to take that conversation a step further by introducing a new twist in this journey — the Framework-less Framework. The Philosophy Behind Traditional frameworks, while immensely popular, often come with a set of built-in “primitives” and hidden implementations that dictate how you build your applications. They’re designed to abstract away the complexities of DOM manipulation and state management, but this also means you sometimes lose direct control over the inner workings of your UI. The Framework-less Framework concept embraces the idea of no framework in the sense that it strips away these superstructures, exposing open internals and giving you the flexibility to shape your UI as you see fit. It’s about having a lean, minimalistic foundation that handles the essentials while letting you extend, experiment, and even replace parts as your project grows. Introducing Proton: A (No)Framework Library Or just read README. It is a fast, lightweight (around 5kb gzip) and component-based UI builder that embodies the no-framework philosophy. You can check it out on GitHub: denshya/proton. Proton isn’t meant to become the next giant framework that monopolizes your codebase; instead, it’s a tool that gives you the potential to build app and manage your UI the way you see it. Key Pillars of Proton Proton’s design is built on four foundational pillars: No Framework: It liberates you from the heavy abstractions of typical frameworks, giving you direct control over the rendered output. Open Internals: Every part of Proton is exposed and customizable, ensuring that you’re never stuck with hidden magic. Fault Tolerance: Robust enough to handle errors gracefully while giving you tools like error boundaries and context APIs. Customization: From JSX mapping straight to DOM elements to flexible event handling and custom attribute management, Proton is designed with extension in mind. These pillars reflect the beliefs I shared in my previous post, where I argued for simplicity and transparency over abstraction and obfuscation. Exploring Proton Through Code Let’s look at some of the basic examples and usage patterns that Proton makes available: Quick Start bun i @denshya/proton For TypeScript and JSX support, add the following snippet to your tsconfig.json or jsconfig.json: { "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "@denshya/proton/jsx/virtual" } } Once configured, you can get started by creating a simple component: function App() { return Hello World! } const inflator = new WebInflator const appView = inflator.inflate() document.getElementById("root").replaceChildren(appView) You can see how this is rather standalone and unbound comparing to things you've seen before. See a playground. Observables and State Instead of relying on a built-in state management, Proton treats any object with a Symbol.subscribe method as an observable. This opens up declarative and fine-grained UI updates just like SolidJs. function ProductCard() { const title = new State("Title") const image = new State("Image source") return ( {title} ) } See implementation of State class. Custom JSX Attributes and Rendering Proton supports React-like JSX but maps it directly to the DOM. This means you’re free to pass any value—primitive or object—into your JSX attributes. Moreover, Proton allows you to define custom JSX attributes. For instance, if you want to override the default className with a dynamic set of CSS modifiers, you can do so like this: const inflator = new WebInflator inflator.jsxAttributes.set("classMods", context => { if (context.value == null) return context.bind("className", bem(context.props.className, ...castArray(context.value))) }) Moreover, you can define how you treat values put into JSX as children: const inflator = new WebInflator inflator.adapters.add(class { constructor(inflator) { this.inflator = inflator } inflate(value) { if (value instanceof MyObject === false) return return document.createElement("div") // Do something cool. } }) This level of customization means you’re not fighting against a rigid API—you're co-authoring it. The beauty of Proton is the freedom it provides. It does not lock you into a preordained structure or magic; instead, it exposes its internals and invites you to innovate. If you’ve ever felt that a framework was limiting your creative control, Proton offers a counterpoint: a toolkit that empowers you to build the UI exactly the way you want. In a sense, it is as much a challenge that encourages developers to rethink how they view UI libraries. Component API In Prot

In my previous blog post I introduced the No Framework principle and argued that enforcing a rigid framework can sometimes stifle creativity and control. Today I’m excited to take that conversation a step further by introducing a new twist in this journey — the Framework-less Framework.
The Philosophy Behind
Traditional frameworks, while immensely popular, often come with a set of built-in “primitives” and hidden implementations that dictate how you build your applications. They’re designed to abstract away the complexities of DOM manipulation and state management, but this also means you sometimes lose direct control over the inner workings of your UI.
The Framework-less Framework concept embraces the idea of no framework in the sense that it strips away these superstructures, exposing open internals and giving you the flexibility to shape your UI as you see fit. It’s about having a lean, minimalistic foundation that handles the essentials while letting you extend, experiment, and even replace parts as your project grows.
Introducing Proton: A (No)Framework Library
Or just read README.
It is a fast, lightweight (around 5kb gzip) and component-based UI builder that embodies the no-framework philosophy. You can check it out on GitHub: denshya/proton. Proton isn’t meant to become the next giant framework that monopolizes your codebase; instead, it’s a tool that gives you the potential to build app and manage your UI the way you see it.
Key Pillars of Proton
Proton’s design is built on four foundational pillars:
- No Framework: It liberates you from the heavy abstractions of typical frameworks, giving you direct control over the rendered output.
- Open Internals: Every part of Proton is exposed and customizable, ensuring that you’re never stuck with hidden magic.
- Fault Tolerance: Robust enough to handle errors gracefully while giving you tools like error boundaries and context APIs.
- Customization: From JSX mapping straight to DOM elements to flexible event handling and custom attribute management, Proton is designed with extension in mind.
These pillars reflect the beliefs I shared in my previous post, where I argued for simplicity and transparency over abstraction and obfuscation.
Exploring Proton Through Code
Let’s look at some of the basic examples and usage patterns that Proton makes available:
Quick Start
bun i @denshya/proton
For TypeScript and JSX support, add the following snippet to your tsconfig.json
or jsconfig.json
:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@denshya/proton/jsx/virtual"
}
}
Once configured, you can get started by creating a simple component:
function App() {
return <div>Hello World!div>
}
const inflator = new WebInflator
const appView = inflator.inflate(<App />)
document.getElementById("root").replaceChildren(appView)
You can see how this is rather standalone and unbound comparing to things you've seen before.
See a playground.
Observables and State
Instead of relying on a built-in state management, Proton treats any object with a Symbol.subscribe
method as an observable. This opens up declarative and fine-grained UI updates just like SolidJs.
function ProductCard() {
const title = new State("Title")
const image = new State("Image source")
return (
<>
<div className="product-card__title">{title}div>
<img className="product-card__image" src={image} alt="Preview" />
<input value={title} />
<input value={image} />
>
)
}
See implementation of State
class.
Custom JSX Attributes and Rendering
Proton supports React-like JSX but maps it directly to the DOM. This means you’re free to pass any value—primitive or object—into your JSX attributes. Moreover, Proton allows you to define custom JSX attributes. For instance, if you want to override the default className
with a dynamic set of CSS modifiers, you can do so like this:
const inflator = new WebInflator
inflator.jsxAttributes.set("classMods", context => {
if (context.value == null) return
context.bind("className", bem(context.props.className, ...castArray(context.value)))
})
Moreover, you can define how you treat values put into JSX as children:
const inflator = new WebInflator
inflator.adapters.add(class {
constructor(inflator) { this.inflator = inflator }
inflate(value) {
if (value instanceof MyObject === false) return
return document.createElement("div") // Do something cool.
}
})
This level of customization means you’re not fighting against a rigid API—you're co-authoring it.
The beauty of Proton is the freedom it provides. It does not lock you into a preordained structure or magic; instead, it exposes its internals and invites you to innovate. If you’ve ever felt that a framework was limiting your creative control, Proton offers a counterpoint: a toolkit that empowers you to build the UI exactly the way you want. In a sense, it is as much a challenge that encourages developers to rethink how they view UI libraries.
Component API
In Proton, components are constructed using functions that serve as both constructors and evaluators for Proton.Component
. This design allows developers to have direct control over the component's behavior and lifecycle.
When a component function is invoked, an instance of Proton.Component
is automatically created and passed as the this
context to the function. This approach provides access to various APIs that manage the component's behavior.
View API
The View API manages the element displayed by a component. Developers can set or update the component's view using the this.view.set
method. For example:
function MyView(this: Proton.Component) {
this.view.set(<div>Hello World!div>)
setTimeout(() => {
this.view.set(<div>I'm Replaced!div>)
}, 1000)
}
In this example, the component initially displays "Hello World!" and then updates to "I'm Replaced!" after one second. The View API facilitates dynamic content updates within components.
Async/Await Support
Proton components can be asynchronous functions, enabling the use of async/await
for handling asynchronous operations. The View API is particularly useful in these scenarios:
async function MyView(this: Proton.Component) {
this.view.set(<Loader />)
await new Promise(resolve => setTimeout(resolve, 1000))
return <div>I'm loaded!div>
}
In this example, a loader is displayed while waiting for the asynchronous operation to complete, after which the main content is rendered.
Error Boundaries
Similar to React, Proton provides an interface to catch errors thrown in child components, including those occurring in event handlers:
function Child() { throw new Error("Test") }
function Parent(this: Proton.Component) {
this.catch(thrown => { /* Handle error */ })
return <div><Child />div>
}
Context API
Proton's Context API allows for the creation and consumption of context using classes:
class MyContext {
constructor(readonly value: string) { }
}
function Child(this: Proton.Component) {
const context = this.context.require(MyContext)
return <span>{context.value}span>
}
function Parent(this: Proton.Component) {
this.context.provide(new MyContext("value"))
return <div><Child />div>
}
By defining contexts as classes, it gives a developer complete freedom in how they define contexts since they can be anything, while also giving general structure and predictability.
Proof of Concept
The Framework-less Framework philosophy isn’t about discarding all structure. It’s about providing a flexible, transparent, and ultra-lightweight set of tools that let you decide what really matters in your application. Proton encapsulates this idea by giving you a robust set of primitives—state management, JSX transformation, observable handling, and more—without the bloat of an all-encompassing framework.
I invite you to dive into the Proton library and experiment with building your next project with this no-framework approach.
Remember this is a way to understand new possibilities and play around with alternative technologies. I'm trying to see what it can turn into.
For contributors and enthusiasts
My idea behind proton
Beside what I mentioned above about Proton, it was created with a thought in mind that it can be very useful in Web Game Development as well, as its nature is atomic inflation.
Movement
Ideally, I want to make this much more than one single library, but create a whole new movement.
I have plenty to do - pretty big roadmap, it includes various IDE plugins, remastering existing libraries, patterns/practices and forming a new way of thinking as a Web Developer.
Generally, I'm trying to make Web Dev much easier and "entertaining" rather than vIbECoDiNg, LLMs are cool, but as a developer I want real vibe during development not Commercially Advertised. If you share my ideas, connect.
Eventually
I encourage you to explore the Proton repository on GitHub. Fork it, extend it, share your projects, and contribute ideas. It’s through this kind of community engagement that we redefine what it means to build UI efficiently and with minimal overhead maybe.