Skip to the content.
Building a Reactive Frontend Framework from Scratch | AI Systems Design From Scratch

Connect with Amin Boulouma Official

AI Systems Design From First Principles - An implementation of AI Systems Design From First Principles | Product Hunt

🏠 Documentation Hub 📝 Engineering Blog 💻 GitHub Repository

Building a Reactive Frontend Framework from Scratch

Amin Boulouma — Software Engineer

Modern client-side web development is dominated by component-driven frameworks like React, Vue, and Angular. These platforms abstract away direct, tedious DOM manipulations by introducing high-level software design patterns: Component Isolation, Data Binding, and Decoupled Event Dispatching. Instead of writing imperatively, you declare your state, and the core engine reflects those data states dynamically onto the user interface view.

But beneath the heavy Javascript bundle sizes and Virtual DOM nodes, how do these systems operate from first principles?

To honor our repository’s strict zero-dependency constraint, we will peel back the browser layer entirely. We will build a complete, state-bound frontend rendering engine prototype using nothing but standard Python objects.


The Frontend Engine Architecture

Our custom system divides state tracking and UI rendering across four explicit structural roles:

  1. Component: The baseline atomic module housing localized metadata, state definitions, and template hook pointers.
  2. DataBinder: The state coordinator that maps raw component states into structured virtual UI nodes.
  3. EventDispatcher: The centralized observer bus allowing modular decoupling of behavior execution from structural state mutation.
  4. UI: The orchestration interface binding the individual sub-modules together into a clean API loop wrapper.

Here is the complete implementation block:

class Component:
    def __init__(self, name, data=None, methods=None):
        self.name = name
        self.data = data if data is not None else {}
        self.methods = methods if methods is not None else {}
        self.events = {}

    def __repr__(self):
        return f'Component ({self.name}, data={self.data}, methods={self.methods})'


class DataBinder:
    def __init__(self):
        self.components = {}
        self.rendered = []

    def add_component(self, component):
        """Registers a detached UI component blueprint into active tracking state."""
        self.components[component.name] = component 

    def render(self):
        """Triggers a clean rendering tree pass over all tracked components."""
        self.rendered = []
        for component in self.components.values(): 
            self._render_component(component)
        return self.rendered

    def _render_component(self, component):
        """Simulates browser DOM string parsing by resolving state markers."""
        dom = f"<div id='{component.name}'>{component.data['text']}</div>"
        self.rendered.append(dom)
        return dom


class EventDispatcher:
    def __init__(self):
        self.listeners = {}

    def add_listener(self, event_type, handler):
        """Binds an execution handler function pointer to a targeted event key."""
        self.listeners[event_type] = handler 

    def trigger_event(self, event_type, data):
        """Fires an action message down the bus to evaluate registered event handlers."""
        handler = self.listeners.get(event_type)
        if handler: 
            handler(data)
    

class UI:
    def __init__(self):
        self.data_binder = DataBinder()
        self.event_dispatcher = EventDispatcher()

    def create_component(self, name, data=None, methods=None):
        """Convenience factory to initialize a node and link it to the data binder."""
        component = Component(name, data, methods)
        self.data_binder.add_component(component)
        return component

    def bind_data(self, component, key, value):
        """Mutates a target state component property to flag data adjustments."""
        component.data[key] = value

    def run(self):
        """Executes the render pipeline cycle and dumps resulting tree templates to standard out."""
        self.data_binder.render()
        print("Rendered UI:")
        for html in self.data_binder.render():
            print(html)


if __name__ == "__main__":
    ui = UI()
    
    # Example: create an active button component structure
    button = ui.create_component("button", {"text": "Click Me"}, {"click": ui.event_dispatcher.trigger_event})
    
    # Programmatically mutate state boundary values
    ui.bind_data(button, "text", "Hello world!")
    
    # Run structural pipeline compilation
    ui.run()


Architectural Mechanisms Breakdown

1. Isolated Component Encapsulation

The Component class maps directly to the design patterns behind modern framework design. By storing properties inside a distinct dictionary boundary (self.data), states stay highly contained. Changing properties on a button node cannot trigger unintended adjustments across adjacent modules, mitigating global mutation risks.

2. The Declarative Render Pipeline

In old imperative programming setups, updating text forced a manual lookup string call like document.getElementById('button').innerText = "New Text". Our DataBinder implements a primitive rendering pipeline. The _render_component helper acts as a template parser:

dom = f"<div id='{component.name}'>{component.data['text']}</div>"

The underlying text is pulled straight from the component’s state memory block. When ui.run() triggers its compile loop, it renders the HTML structure dynamically using whatever parameters are stored inside the data dictionary at that exact frame moment.

3. Decoupled Behavior Dispatching

Rather than tightly coupling execution statements to individual components, the system delegates actions through the EventDispatcher. The engine registers function handles within a tracking dictionary bus (self.listeners). When user triggers roll in, the target method is pulled dynamically and fired, preserving clear decoupling between interface elements and background business logic handlers.


Framework Verification and Execution

When you run this framework module inside your terminal instance, it demonstrates how shifting data configurations changes the final UI layout automatically:

python py_frontend.py

Expected Output Log

Rendered UI:
<div id='button'>Hello world!</div>

Notice that although the button instance was initialized with a default text property of "Click Me", invoking ui.bind_data(button, "text", "Hello world!") overwrote that state memory cell before the rendering pipeline step commenced. The engine compiled the accurate output data representation cleanly without hardcoded string alterations.


Next Evolutionary Milestones

While this pattern demonstrates component initialization and data lookup mechanisms, it operates as a pull-based framework—meaning rendering only occurs when we manually invoke the run() system method.

To upgrade this framework into a highly interactive, push-based client emulator, our architecture roadmap highlights these feature enhancements:

Connect with Amin Boulouma Official