I built NexusLite to understand the machinery behind a frontend framework and to have a small codebase I could inspect while working. It provides TypeScript element helpers, a state store, routing, and static rendering without runtime dependencies.
This website uses NexusLite to render its pages during the build. The browser receives HTML, CSS, and a few scripts for the menu, theme switch, and project filters.
What the framework does
The core lives in framework/src/nexuslite.ts. Static-site generation lives in the separate prerender.ts module. Both are part of the same package.
Element helpers describe a tree using ordinary function calls.
createDOM()turns that tree into browser nodes.createStore()holds state and notifies subscribers when it changes.createApp()connects a store to a root element and a render function.createRouter()supports hash and history navigation.renderToString()produces HTML from the same element descriptions.prerender()writes a route table to static files.
The package also includes HTTP, event delegation, drag-and-drop, and lazy-container helpers. Applications can use the pieces they need.
A small interactive example
Given an HTML element with id="app", this creates a counter:
import { createApp, createStore, div, h1, button, on } from 'nexuslite';
const store = createStore({ count: 0 });
createApp({
root: '#app',
state: store,
render: (state) => div([
h1(`Count: ${state.count}`),
button('Decrease', on('click', () => {
store.setState({ count: state.count - 1 });
})),
button('Increase', on('click', () => {
store.setState({ count: state.count + 1 });
})),
]),
});
In NexusLite 1.2.0, createApp() clears and rebuilds the root when the store changes. It does not compare the old and new trees with a virtual-DOM diff.
That makes the implementation easy to follow, but it has a cost. Replacing a focused input can lose focus or browser-managed state. Large interfaces can do unnecessary rendering. Those are real constraints to consider when choosing the framework and deciding how much UI belongs under one reactive root.
Rendering a static page
For a portfolio, most content does not need a reactive root at all. The build can render it once:
import { readFileSync } from 'node:fs';
import { h } from 'nexuslite';
import { prerender } from 'nexuslite/prerender';
await prerender({
routes: {
'/': () => h('main', {}, [
h('h1', {}, 'My projects'),
h('p', {}, 'Software I have built and what it does.'),
]),
},
outDir: 'dist',
template: readFileSync('index.template.html', 'utf8'),
notFoundPath: false,
});
The template is an HTML string containing <!--ROOT-->. NexusLite replaces that marker with the rendered page. A separate transform(html, path) callback can set titles and metadata for individual routes.
The site build is in scripts/prerender.ts. It renders English and Estonian routes, copies the static assets, generates the sitemap, and creates sharing images. Each project page reads the same project registry as the catalogue and homepage.
What this website adds
NexusLite handles the element trees and HTML rendering. The website supplies the content, design system, metadata, and browser interactions.
The Master Design System keeps the palette, typography, spacing, and theme values in one token file. The build turns those tokens into CSS and uses the same palette for link-preview images. Fonts are served locally.
Featured projects are present in the initial HTML. Search and technology filters enhance the catalogue after it loads, and the query is reflected in the URL. This keeps the content readable when JavaScript is unavailable and makes filtered views shareable.
Verification and limits
The NexusLite 1.2.0 checkout used for this site has 167 tests covering the core framework and prerendering. The website has its own checks for routes, metadata, sharing images, search behavior, and security regressions.
Those checks do not establish a universal performance result. Loading time depends on the assets, server, cache, device, and connection. A useful benchmark needs to record those conditions. The main advantage here is that the rendering path is small enough to inspect when something goes wrong.
NexusLite is a useful fit for learning, small interfaces, and sites that benefit from static rendering. Larger applications may need more sophisticated rendering and a broader component ecosystem. I would choose based on those requirements rather than the framework's size alone.
Explore the code
The source is at github.com/AG064/nexuslite. The framework is MIT-licensed. Its README and examples are the starting point for trying the API.
This website is a working example of the static-rendering path. View its HTML, then follow the corresponding page function in src/pages/ to see how it was assembled.