MostJS is a lightweight frontend framework that gives you complete control over your code without imposing restrictive patterns. It provides a simple yet powerful approach to building web applications, allowing you to use the framework's utilities while maintaining the freedom to incorporate your preferred JavaScript techniques.
Unlike larger frameworks that dictate how your application should be structured, MostJS provides essential tools for creating reactive web applications while keeping the core API minimal and approachable.
You can install MostJS via npm:
# Using npm
npm i @hacker_man/most-jsFor direct use in browsers without a build step:
import { anything } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js'Here's a simple example showing how to create a counter application:
import { Div, H1, P, Button, useState, render } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
const App = () => {
const [count, setCount] = useState(0);
return Div({ className: 'app' }, [
H1({}, ["Hello, MostJS!"]),
P({}, [`Current count: ${count}`]),
Button({
onClick: () => setCount((count)=>{count + 1})
}, ["Increment"])
]);
}
// Render your app to the DOM
render("App", App);MostJS uses a virtual DOM approach to efficiently update the real DOM. The virtual DOM is a lightweight JavaScript representation of the actual DOM, allowing the framework to perform optimized updates.
Why use a Virtual DOM?
- Performance: By comparing virtual DOM trees before updating the real DOM, MostJS minimizes expensive DOM operations.
- Simplicity: Developers can describe the UI declaratively without worrying about manual DOM manipulations.
At its core, MostJS's virtual DOM nodes are simple JavaScript objects with the following structure:
{
tag: 'div', // HTML tag or component name
props: { /* ... */ }, // Element attributes, event handlers, etc.
children: [], // Child nodes or text content
ref: null // Reference to actual DOM element (populated after rendering)
}In MostJS, you create virtual DOM elements using the Create function:
Create(tag, props, ...children)Parameters:
tag: The HTML tag name (like 'div', 'span', etc.)props: An object containing attributes, event handlers, and other propertieschildren: An array of child elements or text content
Example:
const element = Create('div', {
className: 'container',
id: 'main',
onClick: () => console.log('Clicked!')
}, [
Create('h1', {}, ["Title"]),
Create('p', {}, ["Content"])
]);Under the hood, the Create function does the following:
- Processes children, converting strings and numbers to text nodes
- Returns a virtual DOM node object with the specified tag, props, and children
MostJS provides creation-components, which are predefined functions that use the Create function internally, making your code cleaner and more readable.
Example:
import { Div, P } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
return Div({ className: 'container' }, [
P({ id: "greeting" }, ["Hello world"])
]);MostJS includes creation components for most common HTML elements. Each one follows the same pattern - accepting props and children, then forwarding them to the Create function with the appropriate tag name:
Button: For<button>elementsDiv: For<div>elementsUl: For<ul>elementsLi: For<li>elementsH1throughH6: For heading elementsInput: For<input>elementsP: For paragraph elementsSpan: For<span>elementsLink: For<a>elements (with special routing behavior)Aside: For<aside>elementsHeader: For<header>elementsHr: For<hr>elementsBlockquote: For<blockquote>elementsFooter: For<footer>elementsSection: For<section>elementsLabel: For<label>elementsMain: For<main>elements
Special Case: Link Component
The Link component deserves special attention as it integrates with the MostJS router:
- Requires an
hrefprop - Automatically opens external links in a new tab
- Intercepts click events to trigger client-side routing
- Supports a
renderparameter to control whether the new route should be rendered or just change path
Link(props= {}, children= [], render=true)
render parameter is for choosing to just push the path but not render anything you can acheive same result with
router.pushOnly()
MostJS components are functions that return virtual DOM nodes. Unlike some other frameworks, components in MostJS are pure JavaScript functions, making them intuitive and flexible.
The Component function is used to render and manage reusable components:
Component(componentFn, props, title)Parameters:
componentFn: The component function to renderprops: Properties to pass to the componenttitle: A unique identifier for the component (required)
Example:
import { Div, Component } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
import Sidebar from './Sidebar.js';
import MainContent from './MainContent.js';
const App = () => {
return Div({ className: 'app' }, [
Component(Sidebar, { isCollapsed: false }, "sidebar"),
Component(MainContent, { title: "Welcome" }, "main-content")
]);
};Under the hood, Component does the following:
- Registers the component function with its title
- Manages component state tracking
- Handles the component stack for context during rendering
- Captures and returns the component's virtual DOM representation
MostJS provides a simple state management system with the useState fucntion, inspired by React's hook system.
The useState hook allows components to maintain and update state:
const [state, setState] = useState(initialValue);Parameters:
initialValue: Initial state value (can be a value or function)
Returns:
- An array containing:
state: Current state valuesetState: Function to update the state
Example:
import { Div, Button, useState } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
const Counter = () => {
const [count, setCount] = useState(0);
return Div({}, [
`Count: ${count}`,
Button({ onClick: () => setCount(count + 1) }, ["Increment"])
]);
};How it works:
The useState implementation:
- Identifies the current component from the component stack
- Retrieves or initializes the component's state storage
- Gets or sets the state at the current index
- Provides a setter function that:
- Accepts a new value or a function to compute a new value
- Performs equality checks based on the state type
- Triggers a re-render only when the state actually changes
The useRef hook provides direct access to DOM elements:
const elementRef = useRef(referenceId);Parameters:
referenceId: A unique string identifier that matches areferenceprop on an element
Returns:
- The actual DOM element
Example:
import { Div, Input, Button, useRef } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
const SearchField = () => {
const inputRef = useRef("search-input");
const focusInput = () => {
inputRef.focus();
};
return Div({}, [
Input({
reference: "search-input",
placeholder: "Search..."
}, []),
Button({ onClick: focusInput }, ["Focus Search"])
]);
};How it works:
The reference system in MostJS:
- Uses a global map to store references to DOM elements
- Requires elements to have a
referenceprop with a unique identifier - During element creation, elements with a
referenceprop are added to the refs map - The
useReffunction retrieves an element from therefsmap using the provided identifier.
If the reference is not found, it returnsundefined.
This can happen when elements are rendered conditionally and the reference was never attached.
The Watch function provides a way to perform side effects when dependencies change:
Watch(callback, dependencies);Parameters:
callback: Function to executedependencies: Array of values to watch (optional)
Behavior:
- If no dependency array is provided: The effect runs after every render
- If an empty array (
[]) is provided: The effect runs only once when the component mounts - With dependencies: The effect runs when any dependency changes
Example:
import { Div, H1, useState, Watch } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
const UserProfile = (props) => {
const [user, setUser] = useState(null);
Watch(() => {
fetch(`/api/users/${props.userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [props.userId]); // Re-fetch when userId changes
return Div({}, [
H1({}, [user ? user.name : "Loading..."])
]);
};How it works:
The Watch implementation:
- Identifies the current component from the component stack
- Compares the new dependencies with the previous ones using deep equality
- If dependencies have changed, schedules the effect to run after render
- Handles cleanup functions returned from the effect
MostJS includes a robust client-side router for creating single-page applications (SPAs). The router enables navigation between different views without full page reloads, making your applications feel more responsive and native-like.
- Path-based routing: Define routes with static paths or dynamic parameters
- Parameter extraction: Use route patterns like
/users/:idto capture URL segments - Query string parsing: Access query parameters with
router.useQuery() - Browser history integration: Seamless integration with the browser's History API
- Custom 404 handling: Define custom handlers for unmatched routes
- Title management: Set document titles for each route
import { router } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
// Set up routes
router.register('/path', ComponentHandler, 'Page Title');
router.register('/users/:id', UserComponent, 'User Profile');
// Set a custom 404 handler
router.setNotFoundHandler(NotFoundComponent);
// Start the router (initialize with current URL)
router.start();router.register(path, handler, title)Parameters:
path: A string path pattern (can include dynamic segments with:paramNamesyntax)handler: The component function to render when the route matchestitle: The document title to set when this route is active (defaults to window.location.origin)
Examples:
// Static route
router.register('/about', AboutComponent, 'About Us');
// Route with parameters
router.register('/products/:category/:id', ProductComponent, 'Product Details');
// Root route
router.register('/', HomeComponent, 'Home Page');The router provides several methods for navigation:
Navigates to a new route, updates the browser history, and renders the matching component.
router.push('/products/electronics/12345'); // Navigate and renderUpdates the URL and browser history without rendering any component. Useful for updating the URL without triggering a re-render.
router.pushOnly('/products?sort=price'); // Just update the URLRe-renders the component for the current route. Useful after state changes that should refresh the current view.
router.reload(); // Re-render current routeconst params = router.useParams();Returns: An object containing all route parameters extracted from the current URL.
Example:
// For URL: /users/42/profile
const { id } = router.useParams(); // id will be "42"const query = router.useQuery();Returns: An object containing all query parameters from the current URL.
Example:
// For URL: /search?q=javascript&sort=relevance
const { q, sort } = router.useQuery(); // q will be "javascript", sort will be "relevance"const path = router.currentPath();Returns: The current pathname (without query string).
router.setNotFoundHandler(NotFoundComponent);Parameters:
handler: Component function to render when no route matches the current URL
MostJS provides a special Link component that integrates with the router:
import { Link } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
// In your component:
Link({ href: '/users/profile', className: 'nav-link' }, ['Go to Profile'], true);Parameters:
props: Object containing attributes for the anchor element (required:href)children: Content of the linkrender: Boolean that controls whether to render the new route (default: true)
Features:
- Automatically opens external links in a new tab
- Intercepts click events for internal links and uses client-side routing
- Can update URL without rendering via the
renderparameter
Here's an example of a complete SPA with routing:
import {
Div, H1, P, Link, Component,
router
} from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
// Components
const Home = () => {
return Div({ className: 'page home' }, [
H1({}, ['Welcome to MostJS']),
P({}, ['This is the homepage']),
Link({ href: '/users' }, ['View Users'])
]);
};
const UserList = () => {
// Get query parameters
const query = router.useQuery();
const sortBy = query.sort || 'name';
return Div({ className: 'page users' }, [
H1({}, ['User List']),
P({}, [`Sorted by: ${sortBy}`]),
Link({ href: '/' }, ['Back to Home']),
// Sort links - notice the render=false parameter
Link({ href: `/users?sort=name` }, ['Sort by Name'], false),
Link({ href: `/users?sort=date` }, ['Sort by Date'], false)
]);
};
const UserProfile = () => {
// Get route parameters
const { id } = router.useParams();
return Div({ className: 'page profile' }, [
H1({}, [`User Profile: ${id}`]),
Link({ href: '/users' }, ['Back to Users'])
]);
};
const NotFound = () => {
return Div({ className: 'page not-found' }, [
H1({}, ['404 - Page Not Found']),
P({}, ['The requested page does not exist.']),
Link({ href: '/' }, ['Go to Homepage'])
]);
};
// Register routes
router.setNotFoundHandler(NotFound);
router.register('/', Home, 'Home - MostJS App');
router.register('/users', UserList, 'User List');
router.register('/users/:id', UserProfile, 'User Profile');
// Start the router
router.start();You can combine the router with state management to create dynamic views:
import { Div, P, useState, Watch, router } from 'https://cdn.jsdelivr.net/npm/@hacker_man/[email protected]/index.js';
const FilteredList = () => {
// Initialize state from URL parameters
const [filter, setFilter] = useState(() => {
return router.useParams().filter || "all";
});
// Watch for changes to the filter
Watch(() => {
// Update URL when filter changes
if (filter !== router.useParams().filter) {
router.pushOnly(`/${filter}`);
}
}, [filter]);
return Div({}, [
P({}, [`Current filter: ${filter}`])
// Rest of your component...
]);
};
router.register('/:filter', FilteredList, 'Filtered List');
router.register('/', FilteredList, 'All Items');The router automatically handles browser back/forward navigation by listening to the popstate event. When users navigate using browser controls, the router will update the application state and render the appropriate component.
The diffing algorithm is central to MostJS's efficiency. It compares the old and new virtual DOM trees to determine the minimal set of changes required to update the real DOM.
Key Features:
- Node type comparison
- Key-based reconciliation
- Attribute and event handler updates
- Child node reconciliation
- Text node updates
How it Works:
- Compare node types (tag names)
- If key props are used, match by keys first
- Update attributes and event handlers that have changed
- Process children, reusing existing nodes where possible
- Add new nodes and remove deleted ones
MostJS's rendering system is responsible for converting the virtual DOM into real DOM elements and updating the DOM when state changes.
Primary Rendering Functions:
- createElement : Creates actual DOM elements from virtual nodes
- render : Initial rendering of a component to the DOM
- rerender : Updates the DOM when component state changes
| Function | Description |
|---|---|
Create(tag, props, ...children) |
Creates a virtual DOM element |
Component(componentFn, props, title) |
Renders a reusable component with scoped state |
useState(initialValue) |
Creates a state variable with getter and setter |
useRef(referenceId) |
Accesses a DOM element by reference ID |
Watch(callback, dependencies) |
Runs side effects when dependencies change |
render(componentTitle, componentFn, props) |
Renders a component to the DOM |
createStore(initialState) |
Creates a global state store with subscribe/dispatch pattern |
diff(oldVNode, newVNode) |
Computes differences between virtual DOM trees |
createElement(vNode) |
Creates a real DOM element from a virtual DOM node |
rerender(component, props) |
Updates a component's DOM representation after state changes |
| Component | HTML Equivalent | Description |
|---|---|---|
Button(props, children) |
<button> |
Button element |
Div(props, children) |
<div> |
Division element |
Ul(props, children) |
<ul> |
Unordered list |
Li(props, children) |
<li> |
List item |
H1(props, children) to H6(props, children) |
<h1> to <h6> |
Heading elements |
Input(props, children) |
<input> |
Input element |
P(props, children) |
<p> |
Paragraph element |
Span(props, children) |
<span> |
Span element |
Link(props, children, render) |
<a> |
Anchor element with routing support |
Aside(props, children) |
<aside> |
Aside element |
Header(props, children) |
<header> |
Header element |
Footer(props, children) |
<footer> |
Footer element |
Section(props, children) |
<section> |
Section element |
Main(props, children) |
<main> |
Main content element |
Nav(props, children) |
<nav> |
Navigation element |
Article(props, children) |
<article> |
Article element |
Hr(props, children) |
<hr> |
Horizontal rule element |
Img(props, children) |
<img> |
Image element |
Label(props, children) |
<label> |
Form label element |
Form(props, children) |
<form> |
Form element |
Textarea(props, children) |
<textarea> |
Text area input element |
Select(props, children) |
<select> |
Select dropdown element |
Option(props, children) |
<option> |
Option for select element |
Table(props, children) |
<table> |
Table element |
Tr(props, children) |
<tr> |
Table row element |
Th(props, children) |
<th> |
Table header cell element |
Td(props, children) |
<td> |
Table data cell element |
Thead(props, children) |
<thead> |
Table header section |
Tbody(props, children) |
<tbody> |
Table body section |
Tfoot(props, children) |
<tfoot> |
Table footer section |
Blockquote(props, children) |
<blockquote> |
Block quotation element |
| Method | Description |
|---|---|
router.register(path, handler, title) |
Registers a route with a path pattern, component handler, and document title |
router.push(path) |
Navigates to a path, updates history, and renders the matching component |
router.pushOnly(path) |
Updates the URL without rendering a component |
router.reload() |
Re-renders the component for the current route |
router.useParams() |
Returns an object containing route parameters extracted from the current URL |
router.useQuery() |
Returns an object containing query parameters from the current URL |
router.getCurrentPath() |
Returns the current pathname (without query string) |
router.setNotFoundHandler(handler) |
Sets a component to render when no route matches the current URL |
router.start() |
Initializes the router with the current URL and renders the matching component |
MostJS supports all standard DOM events. When defining event handlers in props, use the camelCase version of the event name:
| Event Prop | DOM Event | Description |
|---|---|---|
onClick |
click |
Element click event |
onChange |
change |
Form element value change |
onInput |
input |
Input element value change (immediate) |
onSubmit |
submit |
Form submission event |
onKeyDown |
keydown |
Key press down event |
onKeyUp |
keyup |
Key release event |
onFocus |
focus |
Element receives focus |
onBlur |
blur |
Element loses focus |
onMouseOver |
mouseover |
Mouse pointer enters element |
onMouseOut |
mouseout |
Mouse pointer leaves element |
onMouseMove |
mousemove |
Mouse pointer moves within element |
onTouchStart |
touchstart |
Touch event begins |
onTouchEnd |
touchend |
Touch event ends |
onTouchMove |
touchmove |
Touch moves on element |
onScroll |
scroll |
Element or document scrolling |
onLoad |
load |
Element finished loading |
onError |
error |
Error during element loading |
| Prop | Description |
|---|---|
className |
Sets the HTML class attribute (use this instead of class) |
reference |
Sets a reference ID for accessing the DOM element via useRef() |
style |
Accepts an object of CSS properties in camelCase (e.g., {fontSize: '16px'}) |
key |
Special prop for optimizing list rendering and reconciliation |