ranui
A UI library built on native custom elements. Every component is an <r-*> tag, so it works
in React, Vue, Svelte, Solid, Astro or a plain HTML file the same way. There is no adapter and
no framework version to match. TypeScript types, light/dark theming through design tokens,
Shadow DOM encapsulation and server rendering are included.
v0.5.0-alpha.7MITesm · cjs · iifepackages/ranui
- ranui is alpha: versions ship breaking changes. Pin an exact version and read the changelog before upgrading.
Install
npm install ranui<!-- or from a CDN, no build step -->
<script src="https://unpkg.com/ranui/dist/umd/index.umd.cjs"></script>Use it
Importing registers the elements; after that you write tags.
import 'ranui'; // every component
import 'ranui/button'; // or just one<r-button type="primary">Deploy project</r-button>It is the same tag in every framework: the differences are in how each one passes values and binds events, which the coding guidelines cover in full:
<script src="https://unpkg.com/ranui/dist/umd/index.umd.cjs"></script>
<body>
<r-button>Button</r-button>
</body>import 'ranui';
export const App = () => <r-button type="primary">Deploy</r-button>;
// Rich values and event listeners go through a ref — see the coding guidelines.<template>
<r-button type="primary" @click="deploy">Deploy</r-button>
</template>
<!-- Add `r-` to compilerOptions.isCustomElement in your build config. -->import 'ranui';
const button = document.createElement('r-button');
button.textContent = 'Deploy';
document.body.appendChild(button);Entry points
Each entry registers exactly what its name says, so a page that only wants theming never pays for the component library.
| Import | Contains |
|---|---|
ranui |
Every component |
ranui/<component> |
One component: ranui/button, ranui/select, … |
ranui/theme |
Light/dark theming and token overrides; no elements |
ranui/i18n |
The translation engine; no elements |
ranui/fonts |
Self-hosted Geist Sans + Geist Mono |
ranui/style |
The stylesheet, if your setup does not pick it up |
ranui/builder |
The fluent DOM builder with fine-grained reactivity |
ranui/ssr, ranui/ssr-stream |
Server rendering |
ranui/testing |
Helpers for reaching into a closed shadow root from a test |
ranui/typings |
Ambient JSX / TS element types |
Components
40 elements. Every one of them, with its attributes, properties, events, slots and ::part()
names, is in the element API reference.
Common: Button · Icon · Loading
Data entry: Input · CheckBox · Select · ColorPicker · Attachments · VoiceButton · Forms
Data display: Card · Section · Tabs · Image · Progress · Radar · Player · Preview · Glass · Scratch · StateDot · DisclosureRow
Content rendering: Markdown · Math · Mermaid
AI & chat: Conversation · Reasoning · ToolCard · TokenMeter
Overlays & feedback: Modal · Popover · Dropdown · Message · Skeleton
Navigation: Router · Route · Link
Foundations: Theming · ThemeSwitch · i18n
Five elements have no page of their own because they only exist inside another: <r-option>
(Select), <r-tabs> (Tabs), <r-img> (Image), <r-dropdown-item> (Dropdown) and
<r-content> (Popover). They are in the API reference like everything else.
Live
Styling
Components render into a closed shadow root: page CSS cannot leak in, and selectors cannot reach through. There are four ways in, in order of preference.
1. Design tokens (CSS custom properties): they inherit across the boundary, so setting one
on :root, on a wrapper or on the element all work:
<r-progress
percent="0.7"
type="drag"
style="--ran-progress-track-background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f)"
></r-progress>2. ::part() for structural tweaks the tokens do not cover ·
3. the sheet attribute to inject CSS into the shadow root ·
4. slotted content, which stays in your document and takes your page CSS.
The token names are the design system; the rules for choosing between them are the design guidelines; the mechanics are in the coding guidelines.
Events
Components dispatch CustomEvents with the payload in detail. Bind on the element: whether
an event bubbles is a per-component decision, and the API reference states it for every one:
<r-select id="env"></r-select>
<script>
document.getElementById('env').addEventListener('change', (event) => {
console.log(event.detail.value);
});
</script>The onchange="…" attribute form and the el.onchange = … property form both work, since
these are ordinary DOM elements, but they allow only one handler and no capture phase, so
addEventListener is the one to reach for.
Where to go next
| If you want to… | Read |
|---|---|
| Look up an element's exact API | Element API |
| Know which token to use, and why | Design system |
| Build a screen that looks like one system | Design guidelines |
| Wire ranui into an app correctly | Coding guidelines |
| Add light/dark, or restyle everything | Theming |
| Translate the interface | i18n |
| Render on a server | Server rendering |
| Build reactive views without a framework | Builder |
| See what changed before upgrading | Changelog |
Browser support
The library works in every modern browser: it is built on Custom Elements v1, Shadow DOM v1 and CSS custom properties. Internet Explorer is not supported.

Contributors
Further reading
Standards this library is built on: W3C · ECMA · RFCs · Can I use
Design references worth keeping open: Checklist Design · Laws of UX · Geist · Ant Design · Element UI · Animista · WebGradients