A junior developer's guide to understanding component execution strategies
If you're just starting your frontend journey, you've probably heard terms like "compilation," "build process," and "bundling" thrown around. Most modern frameworks require these steps, but Juris takes a completely different approach that's both simpler and more sophisticated. Let me explain why this matters for your development experience.
What is Component "Compilation" Anyway?
First, let's understand what other frameworks do:
Traditional Approach (React, Vue, Svelte)
// You write this (JSX/Template):
function MyComponent() {
return <div>Hello {name}</div>;
}
// Build tool converts it to this:
function MyComponent() {
return React.createElement('div', null, 'Hello ', name);
}
The process:
- ⏳ Write code in special syntax (JSX, Vue templates)
- ⏳ Wait for build tool to transform your code
- ⏳ Browser receives transformed JavaScript
- ⏳ Framework executes the transformed code
This happens for every single component before your app can run.
Juris: The "No Compilation" Revolution
Juris components are just regular JavaScript functions:
// You write this:
const MyComponent = (props, ctx) => {
return {
div: {
text: `Hello ${props.name}`
}
};
};
// Browser runs this EXACTLY as written - no transformation!
The process:
- ✅ Write code in plain JavaScript
- ✅ Browser runs it immediately
- ✅ Components execute only when needed
Why This is Actually Sophisticated
1. True Lazy Execution
Other frameworks:
// All components get processed at startup
import ComponentA from './ComponentA'; // Processed immediately
import ComponentB from './ComponentB'; // Processed immediately
import ComponentC from './ComponentC'; // Processed immediately
// Even if you only use ComponentA!
Juris:
// Components are just stored functions
juris.registerComponent('ComponentA', () => {
return { /* only runs when needed */ };
});
juris.registerComponent('ComponentB', () => {
return { /* only runs when needed */ };
});
juris.registerComponent('ComponentC', () => {
return { /* only runs when needed */ };
});
// If your page only shows ComponentA, B and C never execute!
This means faster startup times and less memory usage.
2. No Build Step = Instant Development
Traditional workflow:
# Make a change
vim MyComponent.jsx
# Wait for rebuild (3-15 seconds)
npm run build
# ☕ Time for coffee...
# Refresh browser
# See your change
Juris workflow:
# Make a change
vim MyComponent.js
# Refresh browser immediately
# See your change instantly!
No waiting, no build tools, no configuration. Just code and see results.
3. Debugging Like a Pro
When something breaks in traditional frameworks:
// Your error stack trace:
Error: Cannot read property 'name' of undefined
at Object.render (webpack://./src/MyComponent.jsx?:15:23)
at renderComponent (webpack://./node_modules/react/lib/React.js:1247:32)
at updateComponent (webpack://./node_modules/react/lib/React.js:1523:45)
// ... 47 more lines of framework code
Confusing, right? The error points to compiled code, not your actual code.
With Juris:
// Your error stack trace:
Error: Cannot read property 'name' of undefined
at MyComponent (MyComponent.js:3:15)
// Clear and direct!
The error points exactly to your source code because there's no compilation layer hiding it.
4. Dynamic Component Loading
This is where Juris gets really clever:
// Load components based on user actions
const loadAdminPanel = async () => {
const { AdminPanel } = await import('./admin/AdminPanel.js');
// Register and use immediately - no build step!
juris.registerComponent('AdminPanel', AdminPanel);
// Component is ready to use right away
return { AdminPanel: { userId: currentUser.id } };
};
// Only admin users ever load admin components
if (user.isAdmin) {
const adminUI = await loadAdminPanel();
renderAdminSection(adminUI);
}
5. Component Execution Strategy
Here's the sophisticated part - Juris uses intelligent execution timing:
// Registration phase (page load)
juris.registerComponent('ExpensiveChart', (props) => {
console.log('This does NOT run during registration');
// Expensive computation only happens when component is actually rendered
const chartData = processLargeDataset(props.data);
return {
render: () => {
return {
canvas: { id: 'chart-canvas' }
};
},
hooks: {
onMount: () => renderChart(chartData)
}
};
});
// The function above ONLY executes when:
// 1. A parent component renders { ExpensiveChart: {...} }
// 2. Or you explicitly call juris.createComponent('ExpensiveChart', props)
This means:
- Faster page loads (expensive components don't run upfront)
- Better memory usage (unused components never execute)
- Conditional loading (components for different user types)
Real-World Benefits for Junior Developers
1. Easier Learning Curve
// No special syntax to learn
// No build configuration
// Just JavaScript objects and functions
const Button = (props) => {
return {
button: {
text: props.text,
onclick: props.onClick
}
};
};
2. Instant Feedback Loop
- Change code → Refresh browser → See results
- No waiting for compilation
- No mysterious build errors
3. Straightforward Debugging
- Error messages point to your actual code
- Use browser debugger normally
- No source maps confusion
4. All Component Patterns
Juris supports multiple component patterns, giving you flexibility as you learn:
// Pattern 1: Direct return (implicit)
const Simple = () => ({ div: { text: 'Hello' } });
// Pattern 2: Explicit return
const WithReturn = (props, ctx) => {
const message = `Hello ${props.name}`;
return { div: { text: message } };
};
// Pattern 3: Render function without hooks
const WithRenderFunction = (props, ctx) => {
return {
render: () => {
return {
div: {
text: `Hello ${props.name}`,
className: 'greeting'
}
};
}
};
};
// Pattern 4: Render function with hooks
const WithHooks = (props, ctx) => {
return {
render: () => {
return {
div: { text: 'Component with lifecycle' }
};
},
hooks: {
onMount: () => console.log('Component mounted!'),
onUpdate: (oldProps, newProps) => console.log('Props updated'),
onUnmount: () => console.log('Component unmounted')
}
};
};
// Pattern 5: Component with API (for reusable logic)
const WithAPI = (props, ctx) => {
const localState = { count: 0 };
return {
render: () => {
return {
div: {
text: `Count: ${localState.count}`,
onclick: () => localState.count++
}
};
},
api: {
increment: () => localState.count++,
decrement: () => localState.count--,
getCount: () => localState.count,
reset: () => localState.count = 0
},
hooks: {
onMount: () => console.log('Counter ready!')
}
};
};
The Bottom Line
Juris's "no compilation" approach isn't about being simple — it's about being smart. By using plain JavaScript and intelligent execution timing, it achieves:
- ⚡ Faster development (no build step)
- 🐛 Easier debugging (direct error messages)
- 🚀 Better performance (true lazy loading)
- 📚 Simpler learning (just JavaScript)
- 🔧 Instant changes (refresh and see results)
For junior developers, this means you can focus on learning core programming concepts instead of fighting build tools and compilation errors. You write JavaScript, the browser runs JavaScript, and when something breaks, you debug JavaScript.
All component patterns work the same way - whether you use simple returns, render functions, hooks, or APIs, they all execute only when needed, not during registration.
That's sophisticated simplicity.
Want to try Juris? Check out the documentation at jurisjs.com and experience the difference of compilation-free development!
Next up: Understanding Juris's dual rendering modes and when to use each one.
Very nice, thank you :-)