How to build modern web apps without the complexity everyone else accepts as "normal"
The Problem You Don't Know You're Avoiding
If you're new to web development, you've probably been told this is the "normal" way to start a React project:
npx create-react-app my-app
cd my-app
npm start
# Wait 2 minutes for dependencies to install
# Wait another minute for the dev server to start
# Write "Hello World"
# Wait for hot reload
But what if I told you there's a better way? What if you could build the same modern, reactive applications with just this:
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/juris@0.76.0/juris.js"></script>
</head>
<body>
<div id="app"></div>
<script>
const app = new Juris({
layout: {
div: {
text: () => app.getState('message', 'Hello World!'),
children: [{
button: {
text: 'Click me',
onclick: () => app.setState('message', 'Welcome to the future!')
}
}]
}
}
});
app.render();
</script>
</body>
</html>
Save that as an HTML file, open it in your browser, and you have a fully reactive application. No install. No build. No waiting.
Why This Matters for New Developers
1. Learn the Web Platform First
When you start with build tools, you're actually learning an abstraction layer on top of the web. With Juris, you're learning:
- Real HTML
- Real JavaScript
- Real browser APIs
- Real debugging (no source maps needed)
This foundation will serve you for decades, regardless of which frameworks come and go.
2. Focus on Problem-Solving, Not Tooling
Compare these two scenarios:
Traditional Framework:
- "My build is broken" (30 minutes lost)
- "Which version of Node do I need?" (research time)
- "Why won't this deploy?" (configuration issues)
- "Help, my dependencies have conflicts" (dependency hell)
Juris Approach:
- Open HTML file
- Write code
- Refresh browser
- Ship
You'll spend more time solving actual problems and less time fighting your tools.
3. Instant Feedback Loop
Learning accelerates with fast feedback. With Juris:
- Make a change → refresh → see result (1 second)
- No compilation step
- No hot module replacement complexity
- No "why did my state reset?" confusion
4. Share Your Work Effortlessly
Built something cool? Just send the HTML file. No "clone this repo, install dependencies, configure your environment" instructions needed.
The Market Opportunity
Here's something experienced developers won't tell you: the industry is shifting back to simplicity, and new developers who embrace this early will have a significant advantage.
Signs of the Shift:
- Deno and Bun are pushing for simpler tooling
- Browser ES modules are eliminating the need for bundlers
- Web Components are gaining enterprise adoption
- "Boring technology" is becoming cool again
- Major companies are reducing build complexity to improve developer productivity
Your Advantage as a New Developer:
- Less Baggage - You don't have muscle memory for complex build setups
- Fresh Perspective - You can see that the complexity isn't actually necessary
- Faster Learning - You can master reactive programming concepts without framework-specific syntax
- Future-Proof Skills - Understanding the web platform directly is timeless
Real-World Example: Building a Todo App
Let's compare approaches. Here's a complete, reactive todo app in Juris:
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/juris@0.76.0/juris.js"></script>
<style>
.completed { text-decoration: line-through; opacity: 0.5; }
.todo-item { padding: 8px; border-bottom: 1px solid #eee; }
</style>
</head>
<body>
<div id="app"></div>
<script>
const app = new Juris({
states: {
todos: [],
newTodo: ''
},
layout: {
div: {
children: [
{
h1: { text: 'My Todos' }
},
{
input: {
type: 'text',
placeholder: 'Add a todo...',
value: () => app.getState('newTodo'),
oninput: (e) => app.setState('newTodo', e.target.value),
onkeypress: (e) => {
if (e.key === 'Enter' && e.target.value.trim()) {
const todos = app.getState('todos', []);
todos.push({
id: Date.now(),
text: e.target.value.trim(),
completed: false
});
app.setState('todos', todos);
app.setState('newTodo', '');
}
}
}
},
{
div: {
children: () => {
const todos = app.getState('todos', []);
return todos.map(todo => ({
div: {
className: () => todo.completed ? 'todo-item completed' : 'todo-item',
children: [
{
input: {
type: 'checkbox',
checked: todo.completed,
onchange: () => {
todo.completed = !todo.completed;
app.setState('todos', [...todos]);
}
}
},
{
span: { text: todo.text }
}
]
}
}));
}
}
}
]
}
}
});
app.render();
</script>
</body>
</html>
That's it. A complete, reactive todo app with:
- State management
- Real-time updates
- Event handling
- Conditional styling
No build step. No package.json. No configuration files. Just save and open in your browser.
Progressive Enhancement: The Secret Weapon
Here's where Juris really shines for new developers. You can enhance any existing HTML:
<!-- Start with regular HTML -->
<form class="contact-form">
<input name="email" type="email" placeholder="Your email">
<button type="submit">Subscribe</button>
</form>
<script>
// Add reactive behavior
app.enhance('.contact-form', {
onsubmit: async (e) => {
e.preventDefault();
const email = e.target.email.value;
// Add loading state
app.setState('subscribing', true);
try {
await fetch('/api/subscribe', {
method: 'POST',
body: JSON.stringify({ email })
});
app.setState('message', 'Thanks for subscribing!');
} catch (error) {
app.setState('message', 'Something went wrong!');
} finally {
app.setState('subscribing', false);
}
}
});
app.enhance('.contact-form button', {
text: () => app.getState('subscribing') ? 'Subscribing...' : 'Subscribe',
disabled: () => app.getState('subscribing')
});
</script>
This means you can:
- Start with plain HTML/CSS (works everywhere)
- Add Juris for interactivity (enhanced experience)
- Remove Juris anytime (graceful degradation)
Getting Started: Your Action Plan
Week 1: Basics
- Build the todo app above
- Try enhancing a simple HTML page
- Read through the Juris documentation
Week 2: Real Project
- Pick a small project idea (calculator, weather app, simple game)
- Build it entirely with Juris
- Share it with friends (just send the HTML file!)
Week 3: Enhancement
- Take an existing static website
- Use
enhance()
to add interactive features - Notice how you're not rewriting everything
Month 2: Async Mastery
- Build something that uses APIs
- Explore Juris's built-in async support
- Compare the simplicity to other approaches
The Long-Term View
While your peers are learning build tools, dependency management, and framework-specific patterns, you'll be mastering:
- Reactive programming principles (transferable to any framework)
- Web platform APIs (the foundation everything else builds on)
- Async programming patterns (crucial for modern apps)
- Progressive enhancement (a timeless approach)
When the next framework trend emerges, you'll evaluate it based on whether it actually improves on this simplicity. Most of the time, you'll find it doesn't.
Conclusion
The dirty secret of web development is that most of the complexity we accept as "normal" isn't actually necessary. Build tools, complex frameworks, and elaborate setup processes exist to solve problems that simpler approaches just don't have.
As a new developer, you have a unique opportunity: you can skip the complexity and start with the solution. While others are debugging webpack configurations, you'll be building and shipping.
Juris represents what web development could be: powerful, reactive, and simple. By embracing it early, you're not just learning a framework—you're developing a philosophy that will serve you throughout your career.
The future of web development is simpler, not more complex. Get there first.
Ready to start? Copy the todo app code above, save it as todo.html
, and open it in your browser. Welcome to the future.
Top comments (2)
await fetch('/api/subscribe', {...
What's the host if I double click the html file?
the example i gave was incomplete, you may refer to jurisauthor codepen.io for examples. here is what i found:
codepen.io/jurisauthor