Skip to content

TolinSimpson/minima-js

Repository files navigation

MinimaJS

Ultra-lightweight React alternative with short API syntax, zero dependencies, and LLM-optimized development experience.

GitHub Package License: MIT Zero Dependencies

This was vibe coded lol

Why MinimaJS?

  • 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

Framework Comparison

Feature Comparison

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 ✅ ✅ ✅ ❌ ✅

Bundle Size Comparison

Framework Core Full Minified Notes
MinimaJS 13KB 53KB 9KB 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.

Developer Experience

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

Syntax Comparison

Basic Component

MinimaJS vs React vs Vue

MinimaJS

import { div, h1, button, useState, app } from '@tolinsimpson/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>

Template Syntax

HTML Templates Comparison

MinimaJS

import { html, useState } from '@tolinsimpson/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 Management

State & Effects Comparison

MinimaJS

import { useState, useEffect, toggle, counter } from '@tolinsimpson/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 @tolinsimpson/minimajs

Combined File (All Features)

For the complete experience with all features in a single file:

import { div, h1, button, useState, app, quickForm, html } from '@tolinsimpson/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 #app

Modular Imports (Tree-Shakeable)

For optimal bundle size with only needed features:

import { div, h1, button, useState, app } from '@tolinsimpson/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 #app

Template Syntax (HTML-like)

import { html, useState, app } from '@tolinsimpson/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

Components & Hooks

import { useState, useEffect, div, p } from '@tolinsimpson/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 Management

import { useState, toggle, counter, inputState } from '@tolinsimpson/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:

Template Builders

import { quickForm, quickTable, quickModal } from '@tolinsimpson/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) });

Chain Syntax

import { $div, $button, $input } from '@tolinsimpson/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();

Pattern Macros

import { llmCreateApp } from '@tolinsimpson/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 '@tolinsimpson/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 });

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.

Benefits of the Combined File

âś… 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

When to Use the Combined File

  • 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

File Information

// 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 minified

Modular Imports (Tree-Shakeable)

For optimal bundle size with only needed features:

// Full framework
import { div, useState } from '@tolinsimpson/minimajs/full';

// Core functionality only
import { createElement, useState, render } from '@tolinsimpson/minimajs/core';

// Template engine only
import { html } from '@tolinsimpson/minimajs/template';

// Component system only
import { defineComponent } from '@tolinsimpson/minimajs/component';

// SSR features only
import { renderToString } from '@tolinsimpson/minimajs/ssr';

// LLM helpers only
import { quickForm, $div } from '@tolinsimpson/minimajs/llm';

// Dev tools only (optional)
import { useDevTools, inspectComponentTree } from '@tolinsimpson/minimajs/devtools';

Module Features

MinimaJS is built as modular components that can be used independently:

Core Module (@tolinsimpson/minimajs/core)

  • Virtual DOM with efficient diffing algorithm
  • Hooks system (useState, useEffect, useContext)
  • Component lifecycle management
  • Event delegation for optimal performance
  • Memory management with automatic cleanup

Template Module (@tolinsimpson/minimajs/template)

  • 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

Component Module (@tolinsimpson/minimajs/component)

  • Advanced component system with props validation
  • Higher-order components (memo, withProps)
  • Component composition patterns
  • Render props and children patterns
  • Error boundaries for robust applications

SSR Module (@tolinsimpson/minimajs/ssr)

  • 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

API Module (@tolinsimpson/minimajs/api)

  • 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

LLM Module (@tolinsimpson/minimajs/llm)

  • 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

DevTools Module (@tolinsimpson/minimajs/devtools) - Optional

  • 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)

Enabling DevTools

// Enable dev tools globally
import { enableDevTools } from '@tolinsimpson/minimajs/devtools';
enableDevTools();

// Or set in browser console
window.__MINIMA_DEVTOOLS__ = true;

API Reference

Core API

Virtual DOM & Rendering

createElement(type, props, ...children)

Creates a virtual DOM node.

import { createElement } from '@tolinsimpson/minimajs/core';

const vnode = createElement('div', { id: 'app' }, 'Hello World');
// or
const vnode = createElement(MyComponent, { name: 'John' });

render(vnode, container)

Renders a virtual DOM node to the DOM.

import { render } from '@tolinsimpson/minimajs/core';

render(vnode, document.getElementById('app'));

Fragment

Groups elements without extra wrapper.

import { Fragment } from '@tolinsimpson/minimajs/core';

const list = createElement(Fragment, null, 
  createElement('li', null, 'Item 1'),
  createElement('li', null, 'Item 2')
);
Hooks

useState(initialValue)

Manages component state.

import { useState } from '@tolinsimpson/minimajs/core';

const [count, setCount] = useState(0);
setCount(count + 1);
setCount(prev => prev + 1); // functional update

useEffect(effect, dependencies)

Handles side effects and lifecycle.

import { useEffect } from '@tolinsimpson/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
}, []);

useContext(context)

Accesses context values.

import { useContext } from '@tolinsimpson/minimajs/core';

const theme = useContext(ThemeContext);

Template API

HTML Templates

html(strings, ...values)

Creates components using template literals.

import { html } from '@tolinsimpson/minimajs/template';

const MyComponent = ({ name }) => html`
  <div class="greeting">
    <h1>Hello ${name}!</h1>
    <button onclick="${() => alert('Hi!')}">Click me</button>
  </div>
`;

when(condition, template)

Conditional rendering helper.

import { when } from '@tolinsimpson/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>
`;

sanitizeText(text)

Sanitizes user input for XSS protection.

import { sanitizeText } from '@tolinsimpson/minimajs/template';

const safe = sanitizeText(userInput);

Component API

Component System

defineComponent(options)

Creates advanced components with lifecycle.

import { defineComponent } from '@tolinsimpson/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>
  `
});

memo(component)

Optimizes component re-renders.

import { memo } from '@tolinsimpson/minimajs/component';

const ExpensiveComponent = memo(({ data }) => {
  // Only re-renders if data changes
  return html`<div>${JSON.stringify(data)}</div>`;
});

withProps(component, props)

Higher-order component for prop injection.

import { withProps } from '@tolinsimpson/minimajs/component';

const RedButton = withProps(Button, { variant: 'danger' });

SSR API

Server-Side Rendering

renderToString(component, props)

Renders component to HTML string.

import { renderToString } from '@tolinsimpson/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>`;

hydrate(component, container, props)

Hydrates server-rendered HTML.

import { hydrate } from '@tolinsimpson/minimajs/ssr';

// Client-side hydration
hydrate(App, document.getElementById('app'), { user: userData });

LLM API

AI-Optimized Helpers

quickForm(config)

Generates complete forms.

import { quickForm } from '@tolinsimpson/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'
});

quickTable(data, columns)

Generates data tables.

import { quickTable } from '@tolinsimpson/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>
    `
  }
]);

Fluent Chain Syntax

import { $div, $button, $h1 } from '@tolinsimpson/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();

Utility API

State Helpers

toggle(initialValue)

Boolean state helper.

import { toggle } from '@tolinsimpson/minimajs/full';

const [isOpen, toggleOpen] = toggle(false);
// toggleOpen() flips the boolean
// toggleOpen(true) sets to true

counter(initialValue)

Counter state helper.

import { counter } from '@tolinsimpson/minimajs/full';

const [count, increment, decrement, setCount] = counter(0);

inputState(initialValue)

Input state helper with onChange handler.

import { inputState } from '@tolinsimpson/minimajs/full';

const [value, setValue, onChange] = inputState('');

// Use in JSX-like syntax
html`<input value="${value}" oninput="${onChange}">`;

Security

MinimaJS includes built-in XSS protection:

import { html, sanitizeText } from '@tolinsimpson/minimajs/full';

// Safe by default - auto-sanitized
const userContent = html`<div>${userInput}</div>`;

// Manual sanitization
const clean = sanitizeText(dangerousString);

// CSP-compliant - no eval() usage

TypeScript Support

import { useState, div, button } from '@tolinsimpson/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}`)
  ]);
};

Testing

// Examples use the combined file for simplicity - see examples/README.md for details
import { createElement, useState, render } from '@tolinsimpson/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);

Contributing

We welcome contributions! Please see our Contributing Guide.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT License - see LICENSE file for details.

Links

Why Choose MinimaJS?

  • 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.