Skip to content

Using it from a framework

The tags are real custom elements. There is no adapter package, no wrapper, and nothing to install on the other side. Register them once at the app entry, then render HTML.

A copy-paste starter that does exactly that lives in starter/ of the repo. This page is the same recipes, grouped by frontend and backend.

Every Alacris prop is both an attribute and a property. Frameworks disagree about which they set — React 18 sets attributes; Vue, Svelte, Angular, and Solid set properties. Both paths work.

el.setAttribute('age', '36'); // attribute path — parsed to a number
el.age = 36; // property path — used as-is

Alacris does not server-render shadow trees. Put content that must be visible before JavaScript in a slot. The element upgrades when the script loads; properties assigned before upgrade are replayed.

Call applyTheme once in the browser, then render tags. Named controls are form-associated — give them a name and they submit like native fields.

No bundler. Same pattern as the starter:

<script type="importmap">
{
"imports": {
"@alacris/core": "https://cdn.jsdelivr.net/npm/@alacris/core@0.11.3/dist/alacris.js",
"@alacris/ui": "https://cdn.jsdelivr.net/npm/@alacris/ui@0.3.0/src/index.js"
}
}
</script>
<script type="module">
import { applyTheme } from '@alacris/ui';
applyTheme({ seed: '#e8ad18' });
</script>
<ui-button>Save</ui-button>
<ui-text-field name="email" label="Email"></ui-text-field>

Vite, esbuild, and Rollup resolve @alacris/ui with no extra config — npm install @alacris/ui and import from the entry module.

Preact matches React 19 (properties on custom elements). Lit can host the tags in its own templates — share one @alacris/core on the page; do not load a second copy from a CDN. Remix and React Router 7 follow the React 19 path inside client modules.

Emit the tags from the template. Load the module once on the layout. Do not call applyTheme on the server — it writes a document stylesheet.

config/importmap.rb
pin "@alacris/core", to: "https://cdn.jsdelivr.net/npm/@alacris/core@0.11.3/dist/alacris.js"
pin "@alacris/ui", to: "https://cdn.jsdelivr.net/npm/@alacris/ui@0.3.0/src/index.js"
<%= javascript_importmap_tags %>
<script type="module">
import { applyTheme } from '@alacris/ui';
applyTheme({ seed: '#e8ad18' });
</script>
<ui-button><%= t('save') %></ui-button>
<ui-text-field name="email" label="Email" value="<%= user.email %>"></ui-text-field>

With jsbundling-rails or Vite Ruby, npm install @alacris/ui and import from the pack.

A component that only handles one of them works in half the ecosystem. The default’s type drives coercion, so props: { age: 0 } turns "36" into 36, and props: { tags: [] } parses '["a"]' as JSON.

The same contract is documented for components you write yourself on Using it from a framework.