Web Development

I Built a Micro-Framework. Seeking Your Expert Opinion.

I built a minimalist JavaScript micro-framework from scratch to challenge the complexity of modern tools. Is it viable? I need your expert opinion and feedback.

A

Alex Grayson

Senior Frontend Engineer passionate about performance, simplicity, and open-source software development.

9 min read15 views

Ever looked at your node_modules folder and felt a slight sense of dread? A digital black hole of dependencies, megabytes upon megabytes, all to build a relatively simple web app. I’ve been there. That feeling—that nagging voice asking, "Do we really need all this?"—is what pushed me to start a project I half-jokingly called "Operation Simplify."

The result is a tiny JavaScript micro-framework I've named Pico.js. It’s not meant to be the next React or Vue. Instead, it’s an experiment in minimalism. An exploration of what’s truly essential for building reactive, component-based user interfaces on the web. I built it to deepen my own understanding of the "magic" behind the big frameworks, and now that it’s reached a somewhat stable (and I think, interesting) state, I’m putting it out there. I'm not just sharing it; I'm asking for your help. I need your critical eye, your experience, and your honest feedback.

What Exactly is Pico.js?

At its heart, Pico.js is a dependency-free JavaScript library for building modern web frontends. The entire thing weighs in at under 4KB gzipped, which is smaller than the favicon on many websites. The core idea was to provide the bare essentials for reactivity and componentization without the overhead of a virtual DOM or the requirement of a complex build toolchain.

How does it achieve this? It leans heavily on modern browser features:

  • Tagged Template Literals for efficient and safe HTML rendering.
  • JavaScript Proxies for transparent, 'automagical' reactivity.

By combining these native browser capabilities, Pico.js avoids abstracting away the DOM. Instead, it provides a thin, declarative layer on top of it. This makes it incredibly fast for targeted updates and easy to reason about, as you're never too far from the underlying web platform.

The Core Philosophy: Less is More (and Faster)

Every design decision in Pico.js was guided by a few simple principles. Understanding them is key to understanding why the framework is the way it is.

Advertisement
  1. Transparency over Magic: While the reactivity might feel a bit like magic, the implementation is surprisingly straightforward. There are no hidden mechanics or complex lifecycles to memorize. The goal is for a developer to be able to read the Pico.js source code in an afternoon and understand exactly how it works.
  2. Performance by Default: By avoiding a Virtual DOM and its associated diffing algorithm, Pico.js eliminates a whole class of overhead. When state changes, it knows precisely which part of the DOM to update and does so directly. This approach is exceptionally performant for the small-to-medium-sized applications it's designed for.
  3. Zero Dependencies & Zero Config: You include one <script> tag. That's it. There's no npm install, no Webpack config, no Babel transpilation required to get started. This makes it an ideal candidate for quick prototypes, embedding interactive widgets into a server-rendered page (like in a Rails or Django app), or for educational purposes.
  4. Leverage the Platform: Pico.js doesn't try to reinvent the wheel. It embraces modern JavaScript and browser APIs, acting as a lightweight coordinator rather than a heavy-handed abstraction. If the browser can do it well, Pico.js lets it.

A Look at the Key Features

Pico.js is intentionally lean, but it packs a few powerful features that make for a pleasant development experience.

Declarative Rendering with Tagged Templates

Instead of JSX, Pico.js uses a tagged template literal called html. It looks familiar but provides some key benefits. It automatically handles interpolating values, functions, and even other components, all while sanitizing inputs to prevent XSS attacks.

const name = 'World';
const template = html`<h1>Hello, ${name}!</h1>`;
render(template, document.body);

Simple, Proxy-Based State Management

This is the core of Pico's reactivity. You can wrap any object in a reactive() function. This function returns a Proxy that tracks property access and changes. When a component uses a property from this reactive state, it subscribes to it. If that property later changes, only the components that subscribed to it are re-rendered.

const state = reactive({ count: 0 });

// Any component using state.count will now automatically
// re-render whenever state.count is modified.
state.count++; 

There are no setters, no `useState` hooks, no special `dispatch` functions. You just mutate the object, and the UI updates. Simple as that.

Component-Based Architecture

In Pico.js, a component is just a JavaScript function that returns a template. It can accept props as its first argument, making components reusable and composable. This functional approach should feel very familiar to anyone who has worked with modern frontend libraries.

function Greeting({ name }) {
  return html`<p>Welcome, ${name}!</p>`;
}

// You can then use this component within another template:
const App = html`
  <div>
    ${Greeting({ name: 'Alex' })}
    ${Greeting({ name: 'Sarah' })}
  </div>
`;

A Quick Code Example: The Classic Counter

Talk is cheap. Let's see how these pieces fit together with the quintessential "hello world" of reactive frameworks: the counter.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Pico.js Counter</title>
</head>
<body>
  <div id="app"></div>

  <!-- In a real app, you'd host this yourself -->
  <script src="https://unpkg.com/picojs@latest/dist/pico.min.js"></script>
  <script>
    const { reactive, html, render } = pico;

    // 1. Create a reactive state object
    const state = reactive({
      count: 0
    });

    // 2. Define event handlers
    const increment = () => state.count++;
    const decrement = () => state.count--;

    // 3. Define the main component
    // It automatically re-renders when state.count changes
    function CounterComponent() {
      return html`
        <div>
          <h1>Count: ${state.count}</h1>
          <button onclick="${increment}">Increment</button>
          <button onclick="${decrement}">Decrement</button>
        </div>
      `;
    }

    // 4. Render the component to the DOM
    render(CounterComponent(), document.getElementById('app'));
  </script>
</body>
</html>

That's the whole thing. It's readable, self-contained, and requires no build process. You can save that as an HTML file and open it directly in your browser.

How Does Pico.js Compare?

Pico.js was never intended to replace the giants of the ecosystem, but it's helpful to understand its trade-offs. It occupies a niche similar to libraries like Preact or Alpine.js, but with its own unique take on reactivity.

FeaturePico.jsReactPreactSvelte
Core ConceptDirect DOM ManipulationVirtual DOMVirtual DOMCompiler
Bundle Size (Gzipped)~4KB~45KB (w/ ReactDOM)~4KB~2KB (runtime)
Learning CurveVery LowModerateLowLow-Moderate
Reactivity ModelJS ProxiesHooks (`useState`)Signals / HooksCompiler-Generated
Build StepOptionalRequiredRecommendedRequired
Best ForSmall projects, prototypes, widgets, educationLarge-scale applications, rich ecosystemsPerformance-critical apps, React compatibilityHigh-performance apps of all scales

The Road Ahead and Where You Come In

Pico.js is a starting point, not a destination. I have a long list of ideas: a simple client-side router, helpers for server-side rendering, more robust testing utilities, improved developer tools. But before I build another thing, I need to know if the foundation is solid. This is where you, the community, come in.

I am humbly asking for your expert opinion. I need you to be my harshest critic and my most enthusiastic collaborator. Here’s how you can help:

  • Check out the code: The entire project is open-source on GitHub. Clone the repo, read the source, and see how it ticks.
  • Build something: The best way to find flaws is to use it. Try building a small to-do app or a weather widget. How does it feel? Where are the pain points?
  • Open an issue or PR: Found a bug? Have an idea for an API improvement? Think the documentation is unclear? Please, let me know.
  • Leave a comment below: What are its biggest weaknesses from your perspective? What are its surprising strengths? Is an idea like this even viable in 2025? What did I get horribly wrong, and what did I get right?

This project was born out of curiosity and a desire for simplicity. Now, its future depends on a conversation. Thank you for reading, and I genuinely can't wait to hear what you think.

Tags

You May Also Like