A small, declarative, JavaScript-first UI library for building reactive interfaces with TypeScript
TypeScript
1
6 commits
updated Sep 26, 2026
A small, declarative, JavaScript-first UI library for building reactive interfaces with TypeScript. Redium distills UI down to its reactive, reduced, non-redundant elements.
Named after radium the element, (not the framework) Redium borrows science's habit of naming things for what they do at their smallest unit.
It is a small TypeScript UI library that makes reactive interfaces feel direct. Components are ordinary functions, state is explicit, and styles live next to the code that uses them no JSX transform, no CSS strings, no virtual DOM on the basic rendering path.
Redium is an early-stage experiment, The project is intentionally incomplete. That is also the invitation: if you enjoy UI architecture, reactive systems, DOM APIs, or developer tooling, there is plenty of room to shape Redium with us.
Redium aims to offer a lightweight alternative for developers who want:
Redium is not trying to be a React replacement. It has a deliberate philosophy zero CSS strings, zero HTML strings, state as named primitives and it holds that line. If you need SSR, a mature ecosystem, or a battle-tested component library, React or SolidJS will serve you better. Redium is for developers who want to understand every layer of what they are running.
Redium is pre-1.0 and under active development. The core primitives work, but APIs may change while the architecture settles. It is best suited for experiments, prototypes, learning, and contributors interested in helping define the library's direction.
See the documentation for the getting-started guide and unit reference.
import {
Root,
Button,
Column,
Row,
Text,
createState,
min,
ratio,
mountElement,
Shadow,
Colors,
Center,
} from "redium";
const Counter = () => {
const count = createState(0);
return Center(
Column({
gap: 16,
padding: [24, 32],
style: {
width: min(ratio(1), 384),
background: Colors.white,
radius: 16,
shadow: Shadow.md,
},
children: [
Center(Text(count, { style: { font: 48, weight: 700 } })),
Row({
gap: 8,
center: true,
children: [
Button("-", { onClick: () => count.value-- }),
Button("Reset", { onClick: () => (count.value = 0) }),
Button("+", { onClick: () => count.value++ }),
],
}), // Row
],
}), // Column
); // Center
};
mountElement(
Root(Counter(), {
style: { background: Colors.gray },
}),
);
State updates automatically notify subscribers, and Text can render a state directly. Derived values are created with createSelector.
const name = createState("Ada");
const greeting = createSelector(() => `Hello, ${name.value}!`);
Text(greeting);
name.value = "Grace";
State utilities include State, createState, and createSelector.
Components are ordinary functions that return an element:
function Welcome() {
return Column({
gap: 8,
children: [
Text("Welcome", { style: { font: 28, weight: 700 } }),
Text("A component can be composed from other components."),
],
});
}
Container - responsive vertical flex container; use row: true for low-level horizontal layoutColumn - vertical flex layoutRow - responsive horizontal flex layout that wraps by defaultGrid - independent CSS Grid layout with responsive columnsCenter - centers one childGrid({
columns: 3,
minColumnWidth: 220,
gap: 16,
children: cards,
});
Styles can be supplied with an element or applied through the chainable Style API:
const panel = Column({
padding: [24, 16], // top/bottom: 24px, left/right: 16px
margin: 12,
style: {
width: min(ratio(1), 512),
background: "#fff",
radius: 12,
shadow: Shadow.lg,
weight: 600,
},
});
panel.style
.width(400)
.padding([24, 16, 32, 16])
.background("#ffffff");
Whole-number sizes are pixels. Fractional values from 0 through 1 represent a ratio of the parent dimension; use ratio(1) for 100%. Raw CSS strings are not accepted by the sizing or spacing APIs; use the library sizing helpers instead.
Use grow and shrink to control flex space distribution:
Row({
children: [
Container({ grow: 1, children: [Text("Flexible content")] }),
Container({ width: 220, shrink: 0, children: [Text("Fixed panel")] }),
],
});
Button("Save", {
onClick: () => console.log("saved"),
disabled: isSaving,
});
Elements support mounting, temporary unmounting, and semantic click handlers. Page code uses Redium components and typed styles; browser implementation details stay inside the library.
Redium includes small helpers for common CSS values:
import { clamp, hex, min, ratio, rgba } from "redium";
const accent = hex("#38bdf8");
const translucent = rgba(15, 23, 42, 0.8);
const width = clamp(240, 0.5, 720);
const cardWidth = min(ratio(1), 420);
Clone the repository and install the development dependencies:
git clone https://github.com/YOUR_USERNAME/redium.git
cd redium
npm install
npm run dev
Vite transforms the TypeScript examples and resolves the local redium package aliases. The default page loads example/row.ts; change the script path in index.html to study another example. Static servers cannot load these TypeScript modules directly.
The package exports focused entry points for larger applications:
import { Root } from "redium/core";
import { mountElement } from "redium/render";
import { Column, Text } from "redium/elements";
import { Unit } from "redium/style";
npm run dev
npm run typecheck
npm run build
npm run build:library
npm test
npm run dev starts the Redium development server for the application selected by index.html. npm run build creates its minified browser bundle, HTML, and imported assets in dist/. Do not open index.html through a static server that does not transform TypeScript.
npm run build:library creates Redium's publishable ESM and CommonJS files, source maps, and TypeScript declarations in dist/. npm test runs that package build and the behavioral contract tests. See building applications for using npx redium dev and npx redium build in an application project.
The root import (redium) and subpath imports (such as redium/state and
redium/elements) share runtime identities within each module format. ESM uses
shared chunks; CommonJS subpaths re-export one runtime bundle. Keep the complete
dist/ directory when distributing the package, including its chunks/ folder.
ESM and CommonJS are separate runtimes: use one format consistently when passing
Redium states, styles, and elements between modules.
src/
core/ Element, node, and root foundations
elements/ Container, Text, Button, Row, Column, Grid, and Center
render/ DOM mounting
state/ State, selectors, and effects
style/ Style, units, borders, shadows, and sizing helpers
utils/ Color utilities
example/ Runnable demos and behavior studies
tests/ Behavioral contract tests
The most useful contributions are improvements that make the core easier to use without making it harder to understand. Good starting points include:
Please open an issue before making a large architectural change. For smaller fixes, a pull request with a clear description and a typecheck/build result is welcome.
These are ideas, not promises, and community feedback should influence their priority:
npm run typecheck and npm run build.If you are unsure where to begin, open a discussion or issue with an idea, question, or small experiment. Early feedback is especially valuable while Redium is still taking shape.
MIT License
6 commits
TypeScript
75.6%
JavaScript
22.6%
HTML
1.8%
A small, declarative, JavaScript-first UI library for building reactive interfaces with TypeScript
TypeScript
1
6 commits
updated Sep 26, 2026
A small, declarative, JavaScript-first UI library for building reactive interfaces with TypeScript. Redium distills UI down to its reactive, reduced, non-redundant elements.
Named after radium the element, (not the framework) Redium borrows science's habit of naming things for what they do at their smallest unit.
It is a small TypeScript UI library that makes reactive interfaces feel direct. Components are ordinary functions, state is explicit, and styles live next to the code that uses them no JSX transform, no CSS strings, no virtual DOM on the basic rendering path.
Redium is an early-stage experiment, The project is intentionally incomplete. That is also the invitation: if you enjoy UI architecture, reactive systems, DOM APIs, or developer tooling, there is plenty of room to shape Redium with us.
Redium aims to offer a lightweight alternative for developers who want:
Redium is not trying to be a React replacement. It has a deliberate philosophy zero CSS strings, zero HTML strings, state as named primitives and it holds that line. If you need SSR, a mature ecosystem, or a battle-tested component library, React or SolidJS will serve you better. Redium is for developers who want to understand every layer of what they are running.
Redium is pre-1.0 and under active development. The core primitives work, but APIs may change while the architecture settles. It is best suited for experiments, prototypes, learning, and contributors interested in helping define the library's direction.
See the documentation for the getting-started guide and unit reference.
import {
Root,
Button,
Column,
Row,
Text,
createState,
min,
ratio,
mountElement,
Shadow,
Colors,
Center,
} from "redium";
const Counter = () => {
const count = createState(0);
return Center(
Column({
gap: 16,
padding: [24, 32],
style: {
width: min(ratio(1), 384),
background: Colors.white,
radius: 16,
shadow: Shadow.md,
},
children: [
Center(Text(count, { style: { font: 48, weight: 700 } })),
Row({
gap: 8,
center: true,
children: [
Button("-", { onClick: () => count.value-- }),
Button("Reset", { onClick: () => (count.value = 0) }),
Button("+", { onClick: () => count.value++ }),
],
}), // Row
],
}), // Column
); // Center
};
mountElement(
Root(Counter(), {
style: { background: Colors.gray },
}),
);
State updates automatically notify subscribers, and Text can render a state directly. Derived values are created with createSelector.
const name = createState("Ada");
const greeting = createSelector(() => `Hello, ${name.value}!`);
Text(greeting);
name.value = "Grace";
State utilities include State, createState, and createSelector.
Components are ordinary functions that return an element:
function Welcome() {
return Column({
gap: 8,
children: [
Text("Welcome", { style: { font: 28, weight: 700 } }),
Text("A component can be composed from other components."),
],
});
}
Container - responsive vertical flex container; use row: true for low-level horizontal layoutColumn - vertical flex layoutRow - responsive horizontal flex layout that wraps by defaultGrid - independent CSS Grid layout with responsive columnsCenter - centers one childGrid({
columns: 3,
minColumnWidth: 220,
gap: 16,
children: cards,
});
Styles can be supplied with an element or applied through the chainable Style API:
const panel = Column({
padding: [24, 16], // top/bottom: 24px, left/right: 16px
margin: 12,
style: {
width: min(ratio(1), 512),
background: "#fff",
radius: 12,
shadow: Shadow.lg,
weight: 600,
},
});
panel.style
.width(400)
.padding([24, 16, 32, 16])
.background("#ffffff");
Whole-number sizes are pixels. Fractional values from 0 through 1 represent a ratio of the parent dimension; use ratio(1) for 100%. Raw CSS strings are not accepted by the sizing or spacing APIs; use the library sizing helpers instead.
Use grow and shrink to control flex space distribution:
Row({
children: [
Container({ grow: 1, children: [Text("Flexible content")] }),
Container({ width: 220, shrink: 0, children: [Text("Fixed panel")] }),
],
});
Button("Save", {
onClick: () => console.log("saved"),
disabled: isSaving,
});
Elements support mounting, temporary unmounting, and semantic click handlers. Page code uses Redium components and typed styles; browser implementation details stay inside the library.
Redium includes small helpers for common CSS values:
import { clamp, hex, min, ratio, rgba } from "redium";
const accent = hex("#38bdf8");
const translucent = rgba(15, 23, 42, 0.8);
const width = clamp(240, 0.5, 720);
const cardWidth = min(ratio(1), 420);
Clone the repository and install the development dependencies:
git clone https://github.com/YOUR_USERNAME/redium.git
cd redium
npm install
npm run dev
Vite transforms the TypeScript examples and resolves the local redium package aliases. The default page loads example/row.ts; change the script path in index.html to study another example. Static servers cannot load these TypeScript modules directly.
The package exports focused entry points for larger applications:
import { Root } from "redium/core";
import { mountElement } from "redium/render";
import { Column, Text } from "redium/elements";
import { Unit } from "redium/style";
npm run dev
npm run typecheck
npm run build
npm run build:library
npm test
npm run dev starts the Redium development server for the application selected by index.html. npm run build creates its minified browser bundle, HTML, and imported assets in dist/. Do not open index.html through a static server that does not transform TypeScript.
npm run build:library creates Redium's publishable ESM and CommonJS files, source maps, and TypeScript declarations in dist/. npm test runs that package build and the behavioral contract tests. See building applications for using npx redium dev and npx redium build in an application project.
The root import (redium) and subpath imports (such as redium/state and
redium/elements) share runtime identities within each module format. ESM uses
shared chunks; CommonJS subpaths re-export one runtime bundle. Keep the complete
dist/ directory when distributing the package, including its chunks/ folder.
ESM and CommonJS are separate runtimes: use one format consistently when passing
Redium states, styles, and elements between modules.
src/
core/ Element, node, and root foundations
elements/ Container, Text, Button, Row, Column, Grid, and Center
render/ DOM mounting
state/ State, selectors, and effects
style/ Style, units, borders, shadows, and sizing helpers
utils/ Color utilities
example/ Runnable demos and behavior studies
tests/ Behavioral contract tests
The most useful contributions are improvements that make the core easier to use without making it harder to understand. Good starting points include:
Please open an issue before making a large architectural change. For smaller fixes, a pull request with a clear description and a typecheck/build result is welcome.
These are ideas, not promises, and community feedback should influence their priority:
npm run typecheck and npm run build.If you are unsure where to begin, open a discussion or issue with an idea, question, or small experiment. Early feedback is especially valuable while Redium is still taking shape.
MIT License
6 commits
TypeScript
75.6%
JavaScript
22.6%
HTML
1.8%