Mini-Framework is a lightweight JavaScript framework for building interactive web applications. It features a virtual DOM, JSX-like API, hooks for state and effects, hash-based routing, and declarative event handling. The goal is to provide a simple, modern development experience with minimal code.
- Virtual DOM: Efficiently updates the real DOM by diffing virtual trees.
- JSX-like API: Use a
jsxfunction to describe UI elements and components. - Hooks: Includes
useState,useEffect, anduseReffor state and side effects. - Routing: Hash-based router for single-page app navigation.
- Declarative Events: Attach events directly in your JSX-like code.
- You write components as functions that return virtual DOM objects using the
jsxAPI. - The framework renders these to the real DOM and updates efficiently on state changes.
- Hooks provide state and lifecycle management in a functional style.
- The router listens to URL hash changes and updates the view accordingly.
<!DOCTYPE html>
<html>
<head>
<title>Mini-Framework App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="app.js"></script>
</body>
</html>import { jsx } from "./framework/jsx.js";
import { Render } from "./framework/render.js";
import { useState, useEffect, useRef } from "./framework/hooks.js";
import { Router } from "./framework/router.js";function HelloWorld() {
const [name, setName] = useState("World");
return jsx("div", null,
jsx("h1", null, `Hello, ${name}!`),
jsx("input", {
value: name,
onInput: e => setName(e.target.value)
})
);
}
Render.render(HelloWorld, document.getElementById("app"));const el = jsx("button", { class: "btn", id: "submit-btn" }, "Click me");const input = jsx("input", { type: "email", placeholder: "Enter email", required: true });const button = jsx("button", { onClick: () => alert("Clicked!") }, "Click");const form = jsx("form", null,
jsx("input", { type: "text" }),
jsx("button", { type: "submit" }, "Submit")
);function Counter() {
const [count, setCount] = useState(0);
return jsx("div", null,
jsx("span", null, `Count: ${count}`),
jsx("button", { onClick: () => setCount(count + 1) }, "+"),
jsx("button", { onClick: () => setCount(count - 1) }, "-")
);
}function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const id = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(id);
}, []);
return jsx("div", null, `Seconds: ${seconds}`);
}function FocusInput() {
const inputRef = useRef();
return jsx("div", null,
jsx("input", { ref: inputRef, type: "text" }),
jsx("button", { onClick: () => inputRef.current && inputRef.current.focus() }, "Focus Input")
);
}Router.initRouter();
function App() {
const { currentRoute, navigate } = Router.useRouter();
return jsx("div", null,
jsx("nav", null,
jsx("a", {
href: "#/",
onClick: e => { e.preventDefault(); navigate("/"); }
}, "Home"),
jsx("a", {
href: "#/about",
onClick: e => { e.preventDefault(); navigate("/about"); }
}, "About")
),
currentRoute === "/" && jsx("div", null, "Home Page"),
currentRoute === "/about" && jsx("div", null, "About Page")
);
}function TodoApp() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState("");
const inputRef = useRef();
const addTodo = () => {
if (input.trim()) {
setTodos([...todos, { id: Date.now(), text: input, completed: false }]);
setInput("");
}
};
const toggleTodo = id => setTodos(
todos.map(t => t.id === id ? { ...t, completed: !t.completed } : t)
);
const deleteTodo = id => setTodos(todos.filter(t => t.id !== id));
return jsx("div", { class: "todo-app" },
jsx("h1", null, "Todo List"),
jsx("input", {
ref: inputRef,
type: "text",
value: input,
onInput: e => setInput(e.target.value),
onKeyDown: e => { if (e.key === "Enter") addTodo(); },
placeholder: "Add a new todo..."
}),
jsx("button", { onClick: addTodo }, "Add"),
jsx("ul", { class: "todo-list" },
...todos.map(todo => jsx("li", {
key: todo.id,
class: todo.completed ? "completed" : ""
},
jsx("input", {
type: "checkbox",
checked: todo.completed,
onChange: () => toggleTodo(todo.id)
}),
jsx("span", null, todo.text),
jsx("button", { onClick: () => deleteTodo(todo.id) }, "Delete")
))
)
);
}
Render.render(TodoApp, document.getElementById("app"));- Virtual DOM: Fast, predictable UI updates.
- JSX-like API: Familiar, expressive, and composable.
- Hooks: Simple, functional state and effect management.
- Routing: SPA navigation without server config.
- Declarative Events: Cleaner code, less boilerplate.
mini-framework/
├── framework/
│ ├── jsx.js # JSX-like element creation
│ ├── render.js # Virtual DOM rendering
│ ├── hooks.js # State management hooks
│ ├── router.js # Hash-based routing
│ ├── virtual-dom.js # Virtual DOM implementation
│ └── dom-diff.js # DOM diffing algorithm
├── app.js # Main application file
├── index.html # HTML entry point
├── style.css # Application styles
└── README.md # This documentation
- Keep components focused and single-purpose
- Use hooks for local state
- Use descriptive function names for event handlers
- Clean up side effects in
useEffectreturn functions - Use meaningful variable names and comments
- Component not rendering: Make sure to call
Render.render(YourComponent, mountNode) - State not updating: Use the setter function from
useState - Events not working: Event names must start with
on(e.g.,onClick) - Routing not working: Call
Router.initRouter()before using routing - Check the browser console for errors
- ES6 modules
- Arrow functions
- Template literals
- Array methods (map, filter, reduce)
This mini-framework provides a solid foundation for building interactive web applications. Its modular architecture makes it easy to understand and extend, while its familiar syntax makes it accessible to developers with experience in modern JavaScript frameworks.