A small JavaScript library for building interactive web pages: write less code, add no extra libraries, and get pages that update smoothly when data changes. Lightweight, short syntax, zero dependencies, and friendly for AI-assisted development.
⚠️ EXPERIMENTAL – This project is pending testing in production senerios. Use at own risk.
Looking for a tiny web development ecosystem? See: Minimum Viable Web
- Short API syntax - Build UIs faster with ultra-concise code
- Lightweight & modular - Use only what you need
- Zero dependencies - No supply chain vulnerabilities
- LLM-optimized - AI-friendly APIs for faster development
- Modern features - Hooks, SSR, TypeScript, templating
- XSS-safe - Built-in security by default
| Feature | MinimaJS | React | Vue 3 | Preact | Svelte |
|---|---|---|---|---|---|
| Virtual DOM | ✅ | ✅ | ✅ | ✅ | ❌ |
| Hooks | ✅ | ✅ | ✅ | ✅ | ❌ |
| SSR | ✅ | ✅ | ✅ | ✅ | ✅ |
| TypeScript | ✅ | ✅ | ✅ | ✅ | ✅ |
| useMemo/useCallback | ✅ | ✅ | ✅ | ✅ | ❌ |
| Concurrent Features | ✅ | ✅ | ✅ | ❌ | ❌ |
| Error Boundaries | ✅ | ✅ | ✅ | ✅ | ❌ |
| Suspense | ✅ | ✅ | ✅ | ❌ | ✅ |
| Zero Dependencies | ✅ | ❌ | ❌ | ❌ | ❌ |
| Template Literals | ✅ | ❌ | ❌ | ❌ | ❌ |
| Built-in XSS Protection | ✅ | ❌ | ❌ | ❌ | ❌ |
| LLM-Optimized API | ✅ | ❌ | ❌ | ❌ | ❌ |
| Advanced DevTools | ✅ | ✅ | ✅ | ❌ | ✅ |
| Framework | Core | Full | Minified | Notes |
|---|---|---|---|---|
| MinimaJS | 15KB | 53KB | 10KB core, 37KB full | All features in single file |
| React + ReactDOM | 130KB | 130KB | 42KB (+deps) | Requires ecosystem |
| Vue 3 | 120KB | 120KB | 34KB (+deps) | Requires ecosystem |
| Svelte | 1KB | 1KB | 1KB (compiled) | Compile-time only |
| Preact | 10KB | 10KB | 3KB (+deps) | React-compatible only |
MinimaJS includes React 18+ features while being smaller than React. The combined file provides all features in a single import.
| Aspect | MinimaJS | React | Vue 3 | Svelte |
|---|---|---|---|---|
| Learning Curve | Minimal | Moderate | Moderate | Steep |
| Boilerplate | Ultra-minimal | High | Moderate | Low |
| AI Code Gen | Optimized | Standard | Standard | Limited |
| Setup Complexity | Zero config | High | Moderate | Build required |
MinimaJS vs React vs Vue
MinimaJS
import { div, h1, button, useState, app } from '@minimum-viable-web/minimajs/full';
const Counter = () => {
const [count, setCount] = useState(0);
return div([
h1(`Count: ${count}`),
button({ onClick: () => setCount(count + 1) }, 'Click me!')
]);
};
app(Counter, 'app');React
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Click me!</button>
</div>
);
};
ReactDOM.render(<Counter />, document.getElementById('app'));Vue 3
<template>
<div>
<h1>Count: {{ count }}</h1>
<button @click="count++">Click me!</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>HTML Templates Comparison
MinimaJS
import { html, useState } from '@minimum-viable-web/minimajs/full';
const TodoApp = () => {
const [todos, setTodos] = useState([]);
return html`
<div class="todo-app">
<h1>Todos</h1>
${todos.map(todo => html`
<div class="todo ${todo.done ? 'done' : ''}">
${todo.text}
</div>
`)}
</div>
`;
};React (JSX)
const TodoApp = () => {
const [todos, setTodos] = useState([]);
return (
<div className="todo-app">
<h1>Todos</h1>
{todos.map(todo => (
<div key={todo.id} className={`todo ${todo.done ? 'done' : ''}`}>
{todo.text}
</div>
))}
</div>
);
};Vue 3
<template>
<div class="todo-app">
<h1>Todos</h1>
<div v-for="todo in todos" :key="todo.id"
:class="['todo', { done: todo.done }]">
{{ todo.text }}
</div>
</div>
</template>State & Effects Comparison
MinimaJS
import { useState, useEffect, toggle, counter } from '@minimum-viable-web/minimajs/full';
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, toggleLoading] = toggle(true);
const [visits, increment] = counter(0);
useEffect(() => {
fetch(`/users/${userId}`)
.then(res => res.json())
.then(setUser)
.finally(() => toggleLoading());
}, [userId]);
return user ? div([
p(`Name: ${user.name}`),
button({ onClick: increment }, `Visits: ${visits}`)
]) : div('Loading...');
};React
import { useState, useEffect } from 'react';
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [visits, setVisits] = useState(0);
useEffect(() => {
fetch(`/users/${userId}`)
.then(res => res.json())
.then(setUser)
.finally(() => setLoading(false));
}, [userId]);
return user ? (
<div>
<p>Name: {user.name}</p>
<button onClick={() => setVisits(v => v + 1)}>
Visits: {visits}
</button>
</div>
) : <div>Loading...</div>;
};Quick Start
npm install @minimum-viable-web/minimajsFor the complete experience with all features in a single file:
import { div, h1, button, useState, app, quickForm, html } from '@minimum-viable-web/minimajs/full';
const Counter = () => {
const [count, setCount] = useState(0);
return div([
h1(`Count: ${count}`),
button({ onClick: () => setCount(count + 1) }, 'Click me!')
]);
};
app(Counter, 'app'); // Mounts to #appFor optimal bundle size with only needed features:
import { div, h1, button, useState, app } from '@minimum-viable-web/minimajs/full';
const Counter = () => {
const [count, setCount] = useState(0);
return div([
h1(`Count: ${count}`),
button({ onClick: () => setCount(count + 1) }, 'Click me!')
]);
};
app(Counter, 'app'); // Mounts to #appimport { html, useState, app } from '@minimum-viable-web/minimajs/full';
const TodoApp = () => {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
return html`
<div class="todo-app">
<h1>My Todos</h1>
<input value="${input}" oninput="${e => setInput(e.target.value)}">
<button onclick="${() => addTodo()}">Add</button>
${todos.map(todo => html`
<div class="todo ${todo.done ? 'done' : ''}">
<input type="checkbox" checked="${todo.done}" onchange="${() => toggle(todo.id)}">
${todo.text}
</div>
`)}
</div>
`;
};Core Concepts
import { useState, useEffect, div, p } from '@minimum-viable-web/minimajs/full';
const UserProfile = ({ userId }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(setUser)
.finally(() => setLoading(false));
}, [userId]);
if (loading) return div('Loading...');
if (!user) return div('User not found');
return div([
p(`Name: ${user.name}`),
p(`Email: ${user.email}`)
]);
};State Managment
import { useState, toggle, counter, inputState } from '@minimum-viable-web/minimajs/full';
const MyComponent = () => {
// Basic state
const [name, setName] = useState('');
// Toggle helper
const [isVisible, toggleVisible] = toggle(false);
// Counter helper
const [count, increment, decrement, setCount] = counter(0);
// Input helper
const [email, setEmail, onEmailChange] = inputState('');
return div([
input({ value: email, onChange: onEmailChange }),
button({ onClick: toggleVisible }, 'Toggle'),
button({ onClick: increment }, `Count: ${count}`)
]);
};LLM-Optimized APIs
Perfect for AI code generation with predictable patterns:
import { quickForm, quickTable, quickModal } from '@minimum-viable-web/minimajs/full';
// Complete form in one line
const form = quickForm({
fields: ['name', 'email', { name: 'password', type: 'password' }],
onSubmit: (values) => console.log(values)
});
// Data table in one line
const table = quickTable(users, [
{ key: 'name', header: 'Name' },
{ key: 'email', header: 'Email' },
{ key: 'role', header: 'Role' }
]);
// Modal in one line
const modal = quickModal(isOpen, content, { onClose: () => setOpen(false) });import { $div, $button, $input } from '@minimum-viable-web/minimajs/full';
// Fluent chain building - perfect for LLMs
const card = $div()
.class('card')
.child($h3().text('Title'))
.child($p().text('Description'))
.child($button().text('Action').onClick(handleClick))
.build();import { llmCreateApp } from '@minimum-viable-web/minimajs/full';
// Complete todo app in one call
const todoApp = llmCreateApp.todo({
initialTodos: [{ text: 'Learn MinimaJS', done: false }]
});
// Complete counter in one call
const counter = llmCreateApp.counter({
title: 'My Counter',
initialValue: 5,
step: 2
});Server-Side Rendering
import { renderToString, hydrate } from '@minimum-viable-web/minimajs/full';
// Server
const html = renderToString(App, { props });
res.send(`<!DOCTYPE html><html><body><div id="app">${html}</div></body></html>`);
// Client
hydrate(App, document.getElementById('app'), { props });Built Framework Options
## Combined File (`minima-full.js`)MinimaJS provides a complete combined file that includes all features in a single import for maximum convenience and rapid prototyping.
✅ Everything Included - All features (Core, API, LLM, SSR, DevTools) in one file ✅ Rapid Prototyping - Perfect for quick development and testing ✅ No Import Decisions - Single import gives you everything ✅ Self-Contained - No external dependencies or module resolution issues ✅ CDN-Friendly - Easy to use directly from CDN
- Development & Prototyping - When you need all features quickly
- Learning & Examples - Perfect for tutorials and documentation
- Simple Applications - When bundle size isn't a primary concern
- CDN Usage - When you want to avoid build tools entirely
// The combined file includes:
✅ Core Virtual DOM & Hooks (13KB minified)
✅ Template Engine with XSS Protection
✅ Advanced Component System
✅ Server-Side Rendering
✅ LLM-Optimized APIs (quickForm, $ builders, etc.)
✅ Developer Tools
✅ State Management Helpers
✅ Animation Utilities
// Total: 54KB unminified, 37KB when minifiedFor optimal bundle size with only needed features:
// Full framework
import { div, useState } from '@minimum-viable-web/minimajs/full';
// Core functionality only
import { createElement, useState, render } from '@minimum-viable-web/minimajs/core';
// Template engine only
import { html } from '@minimum-viable-web/minimajs/template';
// Component system only
import { defineComponent } from '@minimum-viable-web/minimajs/component';
// SSR features only
import { renderToString } from '@minimum-viable-web/minimajs/ssr';
// LLM helpers only
import { quickForm, $div } from '@minimum-viable-web/minimajs/llm';
// Dev tools only (optional)
import { useDevTools, inspectComponentTree } from '@minimum-viable-web/minimajs/devtools';MinimaJS is built as modular components that can be used independently:
- Virtual DOM with efficient diffing algorithm
- Hooks system (useState, useEffect, useContext)
- Component lifecycle management
- Event delegation for optimal performance
- Memory management with automatic cleanup
- HTML template literals with tagged template strings
- XSS protection built-in with automatic sanitization
- Conditional rendering helpers (when, unless)
- List rendering with optimized mapping
- Dynamic attributes and class binding
- Advanced component system with props validation
- Higher-order components (memo, withProps)
- Component composition patterns
- Render props and children patterns
- Error boundaries for robust applications
- Server-side rendering with hydration
- Static site generation capabilities
- SEO optimization with meta tag support
- Streaming rendering for improved performance
- Universal routing for client/server compatibility
- Shorthand element creators (div, span, button, etc.)
- State helpers (toggle, counter, inputState, formState)
- Animation utilities (fade, slide transitions)
- Routing helpers (route, link, navigate)
- Context providers for global state
- AI-optimized builders (quickForm, quickTable, quickModal)
- Fluent chain syntax ($div().class().child().build())
- Pattern macros for common UI patterns
- Code generation helpers for LLM workflows
- Semantic component creation with natural language
- Component inspection (useDevTools hook for runtime debugging)
- Performance profiling (render timing and memory analysis)
- Component tree visualization (console-based debugging)
- Development utilities (enable/disable dev mode)
- Browser extension ready (extensible for GUI tools)
// Enable dev tools globally
import { enableDevTools } from '@minimum-viable-web/minimajs/devtools';
enableDevTools();
// Or set in browser console
window.__MINIMA_DEVTOOLS__ = true;API Reference
Virtual DOM & Rendering
Creates a virtual DOM node.
import { createElement } from '@minimum-viable-web/minimajs/core';
const vnode = createElement('div', { id: 'app' }, 'Hello World');
// or
const vnode = createElement(MyComponent, { name: 'John' });Renders a virtual DOM node to the DOM.
import { render } from '@minimum-viable-web/minimajs/core';
render(vnode, document.getElementById('app'));Groups elements without extra wrapper.
import { Fragment } from '@minimum-viable-web/minimajs/core';
const list = createElement(Fragment, null,
createElement('li', null, 'Item 1'),
createElement('li', null, 'Item 2')
);Hooks
Manages component state.
import { useState } from '@minimum-viable-web/minimajs/core';
const [count, setCount] = useState(0);
setCount(count + 1);
setCount(prev => prev + 1); // functional updateHandles side effects and lifecycle.
import { useEffect } from '@minimum-viable-web/minimajs/core';
// On mount and every render
useEffect(() => {
console.log('Effect ran');
});
// On mount only
useEffect(() => {
console.log('Mount effect');
}, []);
// With dependencies
useEffect(() => {
fetchUser(userId);
}, [userId]);
// With cleanup
useEffect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer); // cleanup
}, []);Accesses context values.
import { useContext } from '@minimum-viable-web/minimajs/core';
const theme = useContext(ThemeContext);HTML Templates
Creates components using template literals.
import { html } from '@minimum-viable-web/minimajs/template';
const MyComponent = ({ name }) => html`
<div class="greeting">
<h1>Hello ${name}!</h1>
<button onclick="${() => alert('Hi!')}">Click me</button>
</div>
`;Conditional rendering helper.
import { when } from '@minimum-viable-web/minimajs/template';
const Profile = ({ user }) => html`
<div>
${when(user, html`<h1>Welcome ${user.name}</h1>`)}
${when(!user, html`<a href="/login">Please log in</a>`)}
</div>
`;Sanitizes user input for XSS protection.
import { sanitizeText } from '@minimum-viable-web/minimajs/template';
const safe = sanitizeText(userInput);Component System
Creates advanced components with lifecycle.
import { defineComponent } from '@minimum-viable-web/minimajs/component';
const Button = defineComponent({
name: 'Button',
props: {
variant: { type: String, default: 'primary' },
disabled: { type: Boolean, default: false }
},
setup(props) {
const [clicked, setClicked] = useState(false);
onMounted(() => {
console.log('Button mounted');
});
return { clicked, setClicked };
},
template: ({ props, clicked, setClicked }) => html`
<button
class="btn btn-${props.variant}"
disabled="${props.disabled}"
onclick="${() => setClicked(true)}"
>
<slot></slot>
</button>
`
});Optimizes component re-renders.
import { memo } from '@minimum-viable-web/minimajs/component';
const ExpensiveComponent = memo(({ data }) => {
// Only re-renders if data changes
return html`<div>${JSON.stringify(data)}</div>`;
});Higher-order component for prop injection.
import { withProps } from '@minimum-viable-web/minimajs/component';
const RedButton = withProps(Button, { variant: 'danger' });Server-Side Rendering
Renders component to HTML string.
import { renderToString } from '@minimum-viable-web/minimajs/ssr';
const html = renderToString(App, { user: userData });
const response = `<!DOCTYPE html>
<html>
<body>
<div id="app">${html}</div>
<script src="app.js"></script>
</body>
</html>`;Hydrates server-rendered HTML.
import { hydrate } from '@minimum-viable-web/minimajs/ssr';
// Client-side hydration
hydrate(App, document.getElementById('app'), { user: userData });AI-Optimized Helpers
Generates complete forms.
import { quickForm } from '@minimum-viable-web/minimajs/llm';
const form = quickForm({
fields: [
{ name: 'email', type: 'email', required: true },
{ name: 'password', type: 'password', required: true },
{ name: 'remember', type: 'checkbox', label: 'Remember me' }
],
onSubmit: (values) => login(values),
submitText: 'Sign In'
});Generates data tables.
import { quickTable } from '@minimum-viable-web/minimajs/llm';
const table = quickTable(users, [
{ key: 'name', header: 'Name', sortable: true },
{ key: 'email', header: 'Email' },
{ key: 'role', header: 'Role', render: (value) => value.toUpperCase() },
{
key: 'actions',
header: 'Actions',
render: (_, row) => html`
<button onclick="${() => editUser(row.id)}">Edit</button>
<button onclick="${() => deleteUser(row.id)}">Delete</button>
`
}
]);import { $div, $button, $h1 } from '@minimum-viable-web/minimajs/llm';
const card = $div()
.class('card shadow-lg')
.child($h1().text('Card Title'))
.child($button()
.class('btn btn-primary')
.text('Action')
.onClick(() => handleClick())
)
.build();State Helpers
Boolean state helper.
import { toggle } from '@minimum-viable-web/minimajs/full';
const [isOpen, toggleOpen] = toggle(false);
// toggleOpen() flips the boolean
// toggleOpen(true) sets to trueCounter state helper.
import { counter } from '@minimum-viable-web/minimajs/full';
const [count, increment, decrement, setCount] = counter(0);Input state helper with onChange handler.
import { inputState } from '@minimum-viable-web/minimajs/full';
const [value, setValue, onChange] = inputState('');
// Use in JSX-like syntax
html`<input value="${value}" oninput="${onChange}">`;MinimaJS includes built-in XSS protection (loadTemplate and preloadComponent accept only HTTPS or same-origin relative URLs). See Security (XSS/CSP).
import { html, sanitizeText } from '@minimum-viable-web/minimajs/full';
// Safe by default - auto-sanitized
const userContent = html`<div>${userInput}</div>`;
// Manual sanitization
const clean = sanitizeText(dangerousString);
// CSP-compliant - no eval() usageimport { useState, div, button } from '@minimum-viable-web/minimajs/full';
interface User {
id: number;
name: string;
}
const UserCard = ({ user }: { user: User }) => {
const [expanded, setExpanded] = useState<boolean>(false);
return div([
button({ onClick: () => setExpanded(!expanded) }, user.name),
expanded && div(`ID: ${user.id}`)
]);
};// Examples use the combined file for simplicity - see examples/README.md for details
import { createElement, useState, render } from '@minimum-viable-web/minimajs';
const TestComponent = () => {
const [count, setCount] = useState(0);
return div([
span({ id: 'count' }, count),
button({ id: 'inc', onClick: () => setCount(count + 1) }, '+')
]);
};
// Test renders and state updates
render(TestComponent(), document.body);We welcome contributions! Please see our Contributing Guide.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
We use GitHub Issues for all feedback. Open an issue to report bugs, request features, ask questions, or suggest improvements. Please search existing issues first to avoid duplicates.
Private vulnerability reports are supported. To send a report in a way that is kept private, use GitHub’s private vulnerability reporting (submissions are sent over HTTPS and visible only to maintainers):
https://github.com/Minimum-Viable-Web/minima-js/security/advisories/new
We’ll triage the report and work with you on a fix. Do not open a public issue for security-sensitive bugs.
MIT License - see LICENSE file for details.
- Release notes
- Docs index
- API reference
- Glossary
- Getting started
- Modules and imports
- Build, test, size
- Security (XSS/CSP)
- SSR and hydration
- DevTools
- LLM docs (overview + links)
- Examples:
cd examples && python3 -m http.server 8000 - GitHub Package
- GitHub
- Issues
- Shortest syntax - Write less, do more
- Zero dependencies - No supply chain risks
- Modern features - Hooks, SSR, TypeScript
- LLM-optimized - Perfect for AI development
- Fast & small - Tiny codebase, optimized code
- Security-first - XSS protection built-in
- Flexible deployment - Modular or combined file options
- Production-ready - Enterprise-grade with advanced features
Built for developers who value simplicity and performance.