Frequently Asked Questions About Glitchy Devs Build-Your-Own Frontend Framework
23 answers covering everything from basics to advanced usage.
// Basics
What does 'no black boxes' mean in this methodology?
It means every part of the framework — templating, virtual DOM handling, state management — must be explicable and hand-built. The guiding rule is: if you cannot create it, you do not understand it. This principle drives the entire methodology, pushing you to build the template engine, reactivity system, and component factory yourself rather than importing an existing framework abstraction.
What is a component in this framework made of?
A component is always a combination of three things: a template function (the HTML structure), a methods object (state mutations), and an initial state object (default data). You pass these to createComponent, which returns a reusable, reactive render function. Keeping template, methods, and state co-located in one file per component is the recommended structure.
Why use tagged template literals instead of JSX?
Tagged template literals are native JavaScript — they require no transpilation, no build-time JSX plugin, and no external templating language. The tag function receives static strings and dynamic args separately, letting you process them into HTML structures yourself. This keeps the framework dependency-light and makes the templating mechanism fully transparent, aligning with the no-black-boxes principle.
What is createElement and how does it work as a higher-order function?
createElement(tagName) takes a tag name and returns a function that processes tagged template literals. The returned function accepts (strings, ...args) and combines static strings with dynamic values — including event objects — producing a Snabbdom virtual node via the h function. You export helpers like div and p by calling createElement('div') and createElement('p'), avoiding per-element code duplication.
// How To
How do I set up the project to start building?
Scaffold a JavaScript project with Parcel as a zero-config bundler — no webpack config needed. Install Snabbdom via yarn add snabbdom. Configure Babel with the env preset to transpile modern JavaScript. Create two directories: framework/ for core logic and source/ for application components. Add a <div id='app'></div> to index.html, then run yarn start.
How do I implement the createComponent factory?
In framework/index.js, build createComponent({ template, methods, initialState }) to return a render function. Maintain a mutable state variable set to initialState and a previous variable for the last VDOM. Use reduce over Object.keys(methods) to build mappedMethods — each calls the mutation, re-evaluates the template, and calls patch(previous, nextNode), then updates previous. The initial render calls template(props, state, mappedMethods).
How do I wire a component into the app and run it?
In index.js, import your component and call init('#app', userComponent(props)). Ensure index.html has <div id='app'></div>. Run yarn start to clear cache and launch the Parcel dev server, then open localhost:1234. Confirm the component renders, click events fire, and DOM updates are scoped only to the changed nodes.
How do I add a counter widget with increment and decrement?
Set initialState = { count: 0 }. Define methods = { increment: (state) => ({ ...state, count: state.count + 1 }), decrement: (state) => ({ ...state, count: state.count - 1 }) }. In the template, render the count and two buttons each wired with onClick to their mapped methods. Snabbdom patches only the text node holding the count on each click, leaving the rest of the DOM untouched.
// Troubleshooting
My click handlers do nothing — what's wrong?
You most likely forgot to include Snabbdom's eventlisteners module in snabbdom.init([eventListenersModule]). Without it, event objects routed to the on property are silently ignored. Also verify your createElement detects event objects by checking their keys against known event names and routes them to the on property of the h data argument rather than concatenating them as a string.
Why does patching break after the first render?
You're probably passing the real DOM element (like document.querySelector('#app')) to patch on every render. That only works once. Subsequent patches must use the previous virtual node stored in your previous variable, not the real DOM element. Store nextNode as previous after each patch so Snabbdom can diff old against new correctly.
Why are my DOM updates re-rendering everything instead of just the change?
You're likely not storing the last rendered VDOM in the previous variable, so Snabbdom cannot diff correctly and falls back to full re-renders or errors. Ensure createComponent maintains previous and updates it after each patch. Also confirm your state mutations return new objects via spread — mutating in place breaks change detection.
Why can't I add event handling cleanly to my createElement?
You probably skipped the createReducer refactor and wrote inline reduce logic directly in createElement. That makes event handling nearly impossible to add later. Extract the reduce logic into createReducer, which accumulates event handler objects into an on map when a dynamic arg is an event object, and concatenates strings otherwise. This separation keeps event routing clean.
Why should I avoid writing separate reducers per element?
Writing per-element reducer logic (separate functions for div, p, span) creates code duplication the methodology explicitly rejects. The whole point of createElement as a higher-order function is that one implementation handles every tag — you just pass the tag name. Duplicating logic per element makes the framework harder to maintain and defeats the abstraction's purpose.
// Comparisons
How does this framework compare to Vue's reactivity system?
Both use Snabbdom-derived virtual DOM diffing — Vue actually forked Snabbdom. Vue uses reactive proxies and dependency tracking to auto-detect changes, while this framework uses explicit mapped methods that manually trigger re-evaluation and patching after each pure state mutation. Vue's approach is more automatic and scalable; the from-scratch approach is more transparent and easier to fully understand.
How does mapped methods reactivity compare to React's setState?
React's setState triggers a re-render and reconciliation via the React runtime. Mapped methods do the same conceptually: each calls a pure mutation to produce new state, re-evaluates the template, and patches the DOM. The difference is that React manages this in a large runtime with fibers and scheduling, while mapped methods are a few lines of hand-written wrapper logic you fully control.
How does this approach compare to manipulating the DOM directly with vanilla JS?
Direct DOM manipulation is imperative — you manually query and update nodes, which becomes error-prone and hard to maintain as state grows. This framework is declarative: you describe the UI from state, and Snabbdom diffs and patches only what changed. You get predictable, scoped updates and a clear separation between state, template, and rendering, at the cost of a small abstraction layer.
How does using tagged template literals compare to using a templating library like Handlebars?
Tagged template literals are native JavaScript with zero dependencies and full programmatic control over strings and dynamic values, including event objects. Handlebars and similar libraries add a runtime, a custom syntax, and compilation steps. The tagged-literal approach keeps everything in JavaScript and transparent, which suits a no-black-boxes learning framework, though it lacks features like partials and helpers out of the box.
// Advanced
How do I add lifecycle hooks to the framework?
Introduce onMount, onUpdate, and onUnmount callbacks triggered at appropriate points in the render cycle inside createComponent. Fire onMount after the first patch, onUpdate after each subsequent patch in mapped methods, and onUnmount when a component is removed. This extends reactivity with side-effect timing similar to React's useEffect or Vue's lifecycle hooks.
How do I render child components?
Modify createComponent to accept and render child components passed as props. Each child is itself a createComponent render function producing a VDOM node, which you include in the parent's template output as children in the h call. Manage children re-rendering so parent state changes propagate correctly and children patch independently where possible.
How can I improve performance for long lists?
Consider memoization to skip re-evaluating unchanged templates, batched updates to group multiple state mutations into a single patch cycle instead of one patch per mutation, and virtual scrolling to render only visible list items. Batching is especially impactful since mapped methods otherwise patch on every call, which can cause redundant diffing in rapid update scenarios.
How do I prevent styles from leaking between components?
Use the Shadow DOM API to encapsulate component styles, or adopt a CSS-in-JS approach where styles are scoped to the component at render time. Both prevent global style leakage. Shadow DOM gives true isolation at the platform level, while CSS-in-JS integrates more naturally with the JavaScript-first, no-external-dependency spirit of the framework.
How do I inject services or dependencies into components?
Pass services into components at creation time via a context or service-locator pattern rather than hardcoding them. Extend createComponent to accept a context or services argument, then make it available to the template and methods. This keeps components testable and decoupled, mirroring dependency injection patterns in larger frameworks like Angular.
How do I batch multiple state mutations into one DOM patch?
Refactor mapped methods so that instead of patching immediately, they queue state changes and schedule a single patch on the next microtask or animation frame. Collapse the queued mutations into one new state object, re-evaluate the template once, and patch once. This avoids redundant diffing when several mutations fire in quick succession within the same event handler.