Get started
Frameworks
The same base runs in plain HTML and has official adapters for React, Vue, Nuxt, Svelte, Angular and Astro.
One base, many adapters
The base is a set of 33 <u-*> custom elements that runs on any page with JavaScript: plain HTML, PHP, Django, Rails, WordPress. On top of it sit official adapters, with TypeScript types, for React (Next and Vite), Vue, Nuxt, Svelte (SvelteKit), Angular and Astro.
The names are the same everywhere: USankey, USlider, USelect. The adapter only translates props, events and when the element gets registered. The component logic stays in the base.
Which package and how to wire it
| Framework | Package | Value | Events | SSR |
|---|---|---|---|---|
| React, Next | @leallabs/unslop-react | value + onChange | onChange, onApply, onReady | Yes |
| Vue | @leallabs/unslop-vue | v-model | @u-change | Yes |
| Nuxt | @leallabs/unslop-nuxt | v-model | @u-change | Yes |
| Svelte, SvelteKit | @leallabs/unslop-svelte | bind:value | onChange | Yes |
| Angular | @leallabs/unslop-angular | ngModel or formControl | (u-change) | Yes |
| Astro | @leallabs/unslop-astro | Initial value only | addEventListener("u-change") | Yes |
| Plain HTML | None: unslop.css and unslop.elements.js | value | addEventListener("u-change") | Doesn't apply |
The adapter lists @leallabs/unslop as a peer and npm installs it along. In Astro it's optional, so it goes in the same command. The <u-*> elements and define() come from that package; without a build, unslop.elements.js from the CDN defines the same <u-*>. The older unslop.js still works, with data-u and the u:change event.
One example in each
Each example binds a slider to a value and puts a bar list inside a card. The snippets come from the repo fixtures, which pass the test suite.
Install
npm install @leallabs/unslop-reactCSS and registration
CSS: once, at the root. In Next it goes in app/layout.tsx; in Vite, in main.tsx.
import "@leallabs/unslop/css";Example
Example (from fixtures/vite-react/src/Demo.tsx and fixtures/next/components/Demo.tsx). In Next, the file starts with "use client".
import { useState } from "react";
import { USlider, UBarlist } from "@leallabs/unslop-react";
const periods = [{
label: "Today", total: 120,
items: [{ label: "/docs", value: 80 }, { label: "/blog", value: 40 }],
}];
export default function Dashboard() {
const [level, setLevel] = useState(2);
return <>
<USlider min={0} max={4} value={level} label="Effort" onChange={(e) => setLevel(e.detail.value)} />
<UBarlist card label="Views" periods={periods} />
</>;
}Install
npm install @leallabs/unslop-vueCSS and registration
CSS and plugin, once, in main.ts (from fixtures/vite-vue/src/main.ts).
import { Unslop } from "@leallabs/unslop-vue";
import "@leallabs/unslop/css";
createApp(App).use(Unslop).mount("#app");Example
Example (from fixtures/vite-vue/src/views/HomeView.vue).
<script setup lang="ts">
import { ref } from "vue";
import { USlider, UBarlist } from "@leallabs/unslop-vue";
const level = ref(2);
const periods = [{
label: "Today", total: 120,
items: [{ label: "/docs", value: 80 }, { label: "/blog", value: 40 }],
}];
</script>
<template>
<USlider v-model="level" :min="0" :max="4" label="Effort" />
<UBarlist card label="Views" :periods="periods" />
</template>Install
npm install @leallabs/unslop-nuxtCSS and registration
The module injects the CSS and auto-imports the components (from fixtures/nuxt/nuxt.config.ts).
export default defineNuxtConfig({
modules: ['@leallabs/unslop-nuxt'],
})Example
Example (from fixtures/nuxt/app/pages/index.vue). No imports: ref and the components come from Nuxt.
<script setup lang="ts">
const level = ref(2)
const periods = [{
label: "Today", total: 120,
items: [{ label: "/docs", value: 80 }, { label: "/blog", value: 40 }],
}]
</script>
<template>
<USlider v-model="level" :min="0" :max="4" label="Effort" />
<UBarlist card label="Views" :periods="periods" />
</template>Install
npm install @leallabs/unslop-svelteCSS and registration
CSS, once, in src/routes/+layout.svelte (from fixtures/sveltekit).
import '@leallabs/unslop/css';Example
Example (from fixtures/sveltekit/src/routes/+page.svelte).
<script lang="ts">
import { USlider, UBarlist } from '@leallabs/unslop-svelte';
let level = $state(2);
const periods = [{
label: 'Today', total: 120,
items: [{ label: '/docs', value: 80 }, { label: '/blog', value: 40 }],
}];
</script>
<USlider min={0} max={4} bind:value={level} label="Effort" />
<UBarlist card label="Views" {periods} />Install
npm install @leallabs/unslop-angularCSS and registration
CSS in src/styles.css and provideUnslop() in app.config.ts (from fixtures/angular).
"k-com">/* styles.css */
@import '@leallabs/unslop/css';
"k-com">// app.config.ts
providers: [provideUnslop()]Example
Example (from fixtures/angular/src/app/home.ts and home.html). The chart frame is the <article> written by hand.
import { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { USlider, UBarlist } from '@leallabs/unslop-angular';
@Component({
selector: 'app-dashboard',
imports: [FormsModule, USlider, UBarlist],
template: `
<u-slider [(ngModel)]="level" [min]="0" [max]="4" label="Effort" />
<article class="u-chartc-card">
<u-barlist [props]="barlist" />
</article>`,
})
export class Dashboard {
level = signal(2);
barlist = {
label: 'Views',
periods: [{
label: 'Today', total: 120,
items: [{ label: '/docs', value: 80 }, { label: '/blog', value: 40 }],
}],
};
}Install
npm install @leallabs/unslop-astro @leallabs/unslopCSS and registration
The integration injects the CSS and define() (from fixtures/astro/astro.config.mjs).
import unslop from "@leallabs/unslop-astro";
export default defineConfig({ integrations: [unslop()] });Example
Example (from fixtures/astro/src/pages/index.astro). The value is only the initial one.
---
import { USlider, UBarlist } from "@leallabs/unslop-astro/components";
const periods = [{
label: "Today", total: 120,
items: [{ label: "/docs", value: 80 }, { label: "/blog", value: 40 }],
}];
---
<USlider min={0} max={4} value={2} label="Effort" />
<UBarlist card label="Views" periods={periods} />Wire up the page
No build: the CSS and JS come from the site itself, as in Installation. unslop.elements.js defines the <u-*> elements, and the chart frame is written by hand.
Example
Example (the same markup as Select and the chart-barlist block). It works in PHP, Django, Rails and WordPress, because it's just HTML. The <div data-u> with the older unslop.js still works.
<link rel="stylesheet" href="https://unslop.leallabs.dev/unslop.css">
<script src="https://unslop.leallabs.dev/unslop.elements.js" defer></script>
<u-select label="Period" value="Month" props='{"options":["Week","Month","Year"]}'></u-select>
<article class="u-chartc-card"><div class="u-chartc-panel">
<u-barlist label="Views" props='{"periods":[{"label":"Today","total":120,"items":[{"label":"/docs","value":80},{"label":"/blog","value":40}]}]}'></u-barlist>
</div></article>
<script>
document.querySelector("u-select").addEventListener("u-change", (e) => console.log(e.detail));
</script>What differs between them
- Angular has no
card. The chart frame is written by hand, because generating it outside would break hydration. - Astro only has the initial value. There's no reactive state on the server; you read changes through the event.
- Vue and Angular listen to
u-change.changewould collide with the native event of the innerinput.
How this is tested
On every release, each adapter runs in a real framework fixture and goes through server rendering, hydration, events, value and memory leaks.