npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@viclem321/leaf-js

v1.4.0

Published

A minimalist educative JSX-based framework.

Downloads

4

Readme

🧠 Leaf-js Core (./src/core)

This is the core engine of the leaf-js framework, the part responsible for rendering components, managing the virtual DOM, executing hooks, handling updates, and committing changes to the real DOM.

Unlike production frameworks like React, leaf-js is intentionally simple, fully documented, and written with learning in mind. Its goal is not performance or feature-completeness, but transparency.

Whether you're a beginner curious about how frameworks work under the hood, or an advanced developer wanting to build your own tools, this is where to look.

All the core logic is contained in this folder, no black magic, no hidden steps.

⚙️ Important concepts to understand

Leaf-js follows a simplified version of how modern frameworks like React operate. Below is a high-level overview of the main concepts behind its internal mechanics:

- Component :
    Components in leaf-js are simple functions written in TSX/JSX that return a LeafElement (see next section for details about LeafElement).
    If you look at the return value of your components, you will not directly see a LeafElement, but rather a tag containing another component (<Component1/>), or a known html tag (<div>). In fact, at compile time, typescript takes care of transforming this tag into a call to the Leaf.createElement(Component1) function, which returns a LeafElement containing all the information about Component1.
    From there, leaf-js uses the LeafElement to building the app tree recursively.
    📄 See ../packages/demo/App.tsx and src/elements.mts.


-LeafElement :
    A LeafElement is a lightweight, temporary object created by Leaf.createElement(...). It represents whether a component like <Component1 /> or a native DOM element like <div>.
    It contains the metadata needed to run the component (if needed) and build a Fiber Node (see below for details about Fiber Nodes).
    Metadata contained are : the type (string or function), the props, and the children.
    ⚠️ LeafElements are not the virtual DOM yet, they are a simplified structure used to describe what to render and to help build the actual Fiber tree.
    📄 see src/element.mts



- Virtual DOM (Fiber Tree) :
    The virtual DOM in leaf-js is an in-memory tree built from LeafElements. It represents the full structure of the UI at a given time.
    During the rendering phase, leaf-js recursively executes components and transforms the resulting LeafElements into a FiberNode tree, also called the virtual DOM.
    This structure allows leaf-js to later compare two versions of the tree (diffing), detect changes, and update the real DOM efficiently.
    📄 See src/vdom.mts


- Fiber Node :
    A FiberNode is the core structure used to represent each element in the virtual DOM.
    Each FiberNode contains:
        type: the tag name or component function
        props: the props passed to the element
        children: child FiberNodes
        dom: a reference to the actual DOM element (if created)
        modifTag: used during the diffing phase to mark additions, updates, or deletions
        instanceKey: a stable key used to identify a element across renders
    ⚠️ Don't confuse LeafElement with FiberNode: LeafElement is a temporary structure created by createElement(...) while FiberNode is the real node in the virtual DOM used for diffing and rendering
    📄 See src/vdom.mts


- Diffing (Reconciliation) :
    Once the new fiber tree (virtual DOM) is constructed, leaf-js compares it with the previous one. This process identifies changes: New nodes = PLACEMENT, Modified props = UPDATE, Missing nodes = marked for DELETION
    The result is a new fiber tree where each node is tagged with a modifTag that drives the DOM update.
    📄 See src/vdom.mts


- Commit Phase :
    After diffing, the real DOM is updated: new DOM elements are created and inserted, updated props are synced, Removed elements are detached, and cleanup (hooks, effects) is performed.
    This is the final part of the render cycle.
    📄 See src/commitDOM.mts


- InstanceKey (Component identity):
    Each component in leaf-js is uniquely identified using an instanceKey. This key is determined based on the explicit key prop provided by the user (preferred), or, if no key is given, the component's exact path in the virtual DOM tree.
    This identity allows leaf-js to persist internal data (like hooks) between renders, even when the component is re-executed.
    ⚠️ Important: When rendering conditional branches or lists of components of the same type, always use a key prop. This ensures leaf-js can match components across renders and preserve their state correctly.


- Hooks: useState & useEffect
    Hooks are persistent values bound to a component's lifecycle across renders.
    Each component is identified by an instanceKey, which allows leaf-js to associate the right hook data during execution.
        useState: holds reactive state
        useEffect: manages side effects and supports cleanup on unmount
    Hooks are automatically destroyed when the component associate unmounts.
    📄 See src/hooks.mts


- Scheduler :
    A simple scheduling mechanism prevents multiple redundant renders and ensures updates are batched using microtasks. A render is only triggered once per tick, even if setState is called many times.
    Important: Always use the scheduler to run a render (calling Leaf.render directly may cause conflicts).
    📄 See src/schedulerRender.mts

Recap of a Render Cycle :

When scheduler.render() is called, leaf-js performs the rendering in 4 main steps:

1. Scheduling :
The scheduler checks whether a render is already in progress. If not, it calls the main render() function, else it queues the render.

2. Building the Virtual DOM : 
The root component (typically <App />) is run.
Leaf-js recursively walks through each component and its children, calling reconcile(...) to generate a new virtual DOM tree made of FiberNodes.
This phase executes user-defined components and builds up the structure of the UI.

3. Diffing (Reconciliation) :
The new virtual DOM is compared with the previous one using the diff(...) function.
For each node, leaf-js determines whether it is new, needs to be updated, or should be removed, by tagging it with a modifTag.

4. Commit Phase (Updating the Real DOM) :
Finally, commitDOM(...) applies the changes to the actual DOM based on the tagged FiberNodes. New elements are created and inserted, updated props are synced, removed elements are deleted, cleanup is performed (e.g. for unmounted effects)

✅ The engine is all written from scratch and fully understandable in a few files.