I'm posting this as an author of JURIS.
How a 2,500-line JavaScript framework tackles challenges that typically require entire engineering teams
Web framework development is littered with trade-offs. React chose developer experience over performance. Svelte chose performance over flexibility. Vue chose balance over innovation. But what if a framework could solve all the hard problems simultaneously, without compromise?
Enter Juris—a framework that represents perhaps the most sophisticated piece of client-side architecture ever written. In just 2,500 lines of code, it tackles challenges that have stumped engineering teams for decades, creating a unified system where every hard problem becomes a solved problem.
The Fundamental Challenge: Everything is Async Now
Modern web applications are fundamentally asynchronous. API calls, user interactions, file uploads, database queries, animations—everything happens "eventually." Yet most frameworks treat asynchronous operations as special cases, bolt-on solutions, or afterthoughts.
Juris makes a radical architectural decision: everything is asynchronous by default. Synchronous operations become the special case.
// The same setState API handles both:
juris.setState('user.name', 'John'); // Sync value
juris.setState('user.data', fetch('/api/user')); // Promise value
juris.setState('posts', loadPostsAsync()); // Async function
This isn't just syntactic sugar. Under the hood, Juris runs every operation through its promisify
system:
const trackingPromisify = result => {
const promise = result?.then ? result : Promise.resolve(result);
// Now everything is a Promise and can be handled uniformly
return promise;
};
This single insight—making async the default paradigm—eliminates an entire class of complexity that plagues other frameworks. No more useEffect dependencies, no more loading state management, no more async/await ceremony. Juris handles the timing.
Hard Problem #1: Temporal Independence
Most frameworks are temporally dependent. The order of operations matters. Component lifecycle hooks must fire in sequence. State updates must happen before renders. Effects must run after DOM updates.
Juris achieves temporal independence—the framework works correctly regardless of timing:
// These all work the same way, regardless of timing:
const Component = (props, { getState }) => ({
div: {
text: () => getState('data', 'Loading...'), // Immediate value
className: () => slowAsyncComputation(), // Eventually resolves
style: () => maybeAsyncStyle(), // Could be either
}
});
Whether your functions return immediately or eventually, whether your data is cached or needs fetching, whether your computations are fast or slow—Juris handles all timing scenarios with the same code paths.
This is achieved through sophisticated dependency tracking and a promise coordination system that monitors all async operations:
const { promisify, startTracking, stopTracking, onAllComplete } = createPromisify();
// During hydration, wait for ALL async operations
await onAllComplete(); // System knows when everything is ready
Hard Problem #2: Memory Management Without Garbage
JavaScript's garbage collector can't clean up DOM event listeners, reactive subscriptions, or circular references between framework objects and DOM elements. Most frameworks either leak memory or require manual cleanup ceremonies.
Juris solves this with WeakMap-based automatic cleanup:
class ComponentManager {
constructor() {
this.instances = new WeakMap(); // Automatic cleanup
this.componentStates = new WeakMap(); // No manual intervention needed
this.asyncPlaceholders = new WeakMap();
}
}
When DOM elements are removed, all associated framework state automatically becomes eligible for garbage collection. No useEffect
cleanup functions, no manual unsubscriptions, no memory leak hunting.
But Juris goes further with element recycling pools:
_recycleElement(element) {
const tagName = element.tagName.toLowerCase();
if (!this.recyclePool.has(tagName)) {
this.recyclePool.set(tagName, []);
}
// Clean and reuse instead of creating new elements
this._resetElement(element);
this.recyclePool.get(tagName).push(element);
}
DOM creation is expensive. Juris recycles elements, dramatically reducing garbage collection pressure while improving performance.
Hard Problem #3: Graceful Degradation Under Failure
Most frameworks fail catastrophically. One broken component can crash the entire application. One circular dependency can freeze the browser. One memory leak can slow everything to a crawl.
Juris implements graceful degradation at every level:
try {
childElements = this._reconcileChildren(element, childElements, newChildren);
lastChildrenState = newChildren;
} catch (error) {
console.warn('Reconciliation failed, falling back to safe rendering:', error.message);
useOptimizedPath = false; // Automatically degrade to safer method
this._updateChildren(element, newChildren);
lastChildrenState = newChildren;
}
When the optimized path fails, Juris automatically falls back to a slower but safer approach. When circular dependencies are detected, the system warns and prevents infinite loops. When async operations fail, UI shows error states instead of breaking.
This isn't just error handling—it's self-healing architecture.
Hard Problem #4: Performance Without Sacrifice
The performance vs. developer experience trade-off has defined framework wars for years. React prioritizes DX but requires optimization work. Svelte optimizes aggressively but limits flexibility.
Juris achieves both through dual-mode rendering:
setRenderMode(mode) {
if (['fine-grained', 'batch'].includes(mode)) {
this.renderMode = mode;
}
}
// Same code, different execution paths:
if (this.renderMode === 'fine-grained') {
this._handleChildrenFineGrained(element, children, subscriptions);
} else {
this._handleChildrenOptimized(element, children, subscriptions);
}
- Fine-grained mode: Every change triggers immediate surgical DOM updates
- Batch mode: Changes are collected and reconciled efficiently
The system can even switch modes automatically based on performance characteristics or failure conditions.
But the real performance innovation is intelligent batching:
configureBatching({
maxBatchSize: 50, // Prevent overwhelming batches
batchDelayMs: 0, // Configurable delay
enabled: true // Can disable entirely
});
Updates are batched intelligently, with overflow protection and configurable timing. If batches get too large, they're processed immediately. If the system detects performance issues, it adjusts automatically.
Hard Problem #5: Unified State Management
Most applications end up with multiple state management solutions:
- Component state (useState, this.setState)
- Global state (Redux, Zustand, Context)
- Server state (React Query, SWR)
- URL state (React Router)
- Form state (Formik, React Hook Form)
Each has different APIs, different mental models, different debugging tools.
Juris provides one state system for everything:
// All of these use the same setState API:
juris.setState('component.local.value', userInput); // Component state
juris.setState('global.user.preferences', settings); // Global state
juris.setState('api.posts', fetch('/api/posts')); // Server state
juris.setState('router.currentPage', '/dashboard'); // URL state
juris.setState('forms.login.email', email); // Form state
Path-based state management with dot notation makes everything discoverable. Middleware can intercept and transform any state change. Subscriptions work hierarchically—changing user.profile
notifies listeners of user
and user.profile.name
.
The system includes sophisticated circular dependency detection:
_hasCircularUpdate(path) {
if (this.currentlyUpdating.has(path)) {
console.warn('Circular dependency detected', { path });
return true;
}
return false;
}
State updates can trigger other state updates safely, with automatic loop prevention.
Hard Problem #6: Component Architecture
React's component model forces you to choose between function components (hooks limitations) and class components (verbose ceremony). Vue's Options API vs. Composition API creates cognitive overhead. Svelte's file-based components can't be composed programmatically.
Juris supports every component pattern simultaneously:
// Simple function components
const Button = (props) => ({ button: { text: props.label } });
// Lifecycle components
const DataLoader = (props, context) => ({
hooks: {
onMount: () => loadData(),
onUnmount: () => cleanup()
},
render: () => ({ div: { text: () => context.getState('data') } })
});
// Headless components (logic only)
const useCounter = (props, context) => ({
api: {
increment: () => context.setState('count', c => c + 1),
decrement: () => context.setState('count', c => c - 1)
}
});
// Async components
const AsyncWidget = async (props) => {
const data = await loadWidget();
return { div: { text: data.content } };
};
All patterns work together seamlessly. Headless components become APIs available to other components. Async components show loading states automatically. Lifecycle hooks can be sync or async.
Hard Problem #7: Developer Experience
Framework complexity often gets passed to developers. Complex build systems, intricate mental models, verbose ceremony, debugging difficulties.
Juris prioritizes zero-friction development:
// No build step required
<script src="https://unpkg.com/juris@0.76.0/juris.js"></script>
// No special syntax to learn
const app = new Juris({
layout: { div: { text: 'Hello World' } }
});
// No complex setup
app.render('#app');
The entire framework loads from a CDN. No webpack, no babel, no configuration files. The API is just JavaScript objects and functions.
But zero-config doesn't mean zero-power. The framework includes sophisticated debugging:
// Built-in observability
const logger = {
log: log,
warn: log.w,
error: log.e,
info: log.i,
debug: log.d,
subscribe: logSub,
unsubscribe: logUnsub
};
Every operation is logged with context. Performance timing is built-in. State changes are tracked. Component lifecycle events are visible.
The Architectural Breakthrough
What makes Juris remarkable isn't any single feature—it's how every hard problem becomes a solved problem through unified architectural principles:
1. Async-First Design: Everything flows through the same Promise-based pipeline
2. Reactive Dependencies: Automatic tracking eliminates manual subscription management
3. Graceful Degradation: Every optimization has a safe fallback
4. Memory Safety: WeakMaps and recycling eliminate leak categories
5. Temporal Independence: Timing doesn't affect correctness
6. Unified State: One API for all state management needs
7. Performance Modes: Automatic optimization with manual override
These aren't separate systems—they're emergent properties of a coherent architectural vision.
Looking Forward
Juris represents a new generation of web frameworks—ones that solve problems instead of managing trade-offs. While React, Vue, and Svelte debate the merits of different approaches, Juris demonstrates that the right architecture can have it all:
- Performance without complexity
- Flexibility without verbosity
- Power without configuration
- Safety without ceremony
In 2,500 lines of code, one developer has created what entire engineering teams struggle to build. This isn't just a framework—it's a proof of concept that the hard problems in web development aren't unsolvable.
They just require rethinking everything from first principles.
The Juris framework is available at jurisjs.com and represents active research into next-generation web architecture. The complete source code analysis reveals architectural patterns that will likely influence web development for years to come.
Website: https://jurisjs.com/
GitHub: https://github.com/jurisjs/juri
NPM: https://www.npmjs.com/package/juris
Codepen: https://codepen.io/jurisauthor
To Build Trust and Transparency Try Juris Online Testing Interface
Online Testing: https://jurisjs.com/tests/juris_pure_test_interface.html
Top comments (2)
honestly, the level of care and thought behind this blows my mind. i've tried so many frameworks and this async-first mentality already sets it apart for me
you think most devs actually want all the tough stuff hidden, or would they rather see how the magic works under the hood
Making async the default just makes so much sense - I'm curious, do you have any real world apps running on Juris yet?