Skip to content

Basic Usage ​

Load the global design contract once, then register only the components used by the page.

ts
import '@vielzeug/refine/tokens.css';
import '@vielzeug/refine/button';
import '@vielzeug/refine/input';
import '@vielzeug/refine/dialog';

const input = document.querySelector('ore-input');
const save = document.querySelector('ore-button');

if (!(input instanceof HTMLElement) || !(save instanceof HTMLElement)) {
  throw new Error('Expected Refine controls');
}

save.addEventListener('click', () => {
  console.log((input as HTMLElement & { value: string }).value);
});

Add the browser reset only when the application owns global element styling:

ts
import '@vielzeug/refine/styles/preflight.css';

Choose an Icon Library ​

Refine includes only its internal component icons. Register application icons before mounting them: use registerIcons and neutral IconDefinition data from @vielzeug/refine/icons, the pure @vielzeug/refine/icons/lucide adapter for selected Lucide icons, or @vielzeug/refine/icons/iconify for local Heroicons, Tabler, and Material Design Icons data. Keep importing the component from @vielzeug/refine/icon. Lucide is an optional peer: install lucide yourself only when choosing its data or @vielzeug/refine/icons/lucide/all; core and structural adapters do not require it. The icon reference covers imports, licenses, and presentation behavior.

For selected individual Iconify data, use heroiconsIcons, tablerIcons, or mdiIcons from @vielzeug/refine/icons/heroicons, /tabler, or /mdi. For example, registerIcons(heroiconsIcons({ Search, Home })) preserves your app keys. Install the matching @iconify-icons/<family> data package yourself; these helpers do not accept framework components. Keep iconifyIcons for collection JSON and aliases.

Set Attributes and Properties ​

Use attributes for serializable HTML state.

html
<ore-button variant="outline" color="secondary" size="lg" disabled>
  Save changes
</ore-button>

Use JavaScript properties for arrays, objects, callbacks, and controlled state.

ts
import '@vielzeug/refine/datagrid';
import type { OreDataGridProps } from '@vielzeug/refine/datagrid';

const grid = document.querySelector('ore-datagrid') as HTMLElement & OreDataGridProps;
grid.columns = [
  { key: 'name', label: 'Name', sortable: true },
  { key: 'role', label: 'Role' },
];
grid.rows = [
  { id: '1', name: 'Ada', role: 'Admin' },
  { id: '2', name: 'Linus', role: 'Editor' },
];

Enumerated properties such as variant, size, and color accept the values listed on each component's reference page. An unsupported value resolves to the property's declared default (or undefined when it has no default).

In an Ore template, model() can bind a Refine field's value to a Ripple signal and write user input back:

ts
import { signal } from '@vielzeug/ripple';
import { html, model } from '@vielzeug/ore';
import '@vielzeug/refine/input';

const email = signal('');
const field = html`<ore-input label="Email" type="email" value=${model(email)} />`;

Handle Events ​

Form controls dispatch standard input and change events. Read their current property from event.currentTarget.

ts
const input = document.querySelector('ore-input');
input?.addEventListener('input', (event) => {
  const target = event.currentTarget as HTMLElement & { value: string };
  console.log(target.value);
});

Custom events use kebab-case names and typed detail objects.

ts
import type { OreDialogEvents } from '@vielzeug/refine/dialog';

const dialog = document.querySelector('ore-dialog');
dialog?.addEventListener('open-change', (event: CustomEvent<OreDialogEvents['open-change']>) => {
  console.log(event.detail.open, event.detail.reason);
});

Native Form Validation ​

Refine's form-associated controls integrate with native form submission, FormData, validity checks, and reset. Use required for a required-value constraint; setting error displays and announces feedback but does not, by itself, invalidate the form.

html
<form id="profile-form">
  <ore-input name="email" type="email" label="Email" required></ore-input>
  <ore-select name="role" label="Role" required>
    <option value="">Choose a role…</option>
    <option value="editor">Editor</option>
    <option value="admin">Admin</option>
  </ore-select>
  <ore-button type="submit">Save</ore-button>
</form>

<script type="module">
  import '@vielzeug/refine/input';
  import '@vielzeug/refine/select';
  import '@vielzeug/refine/button';

  const form = document.querySelector('#profile-form');
  form.addEventListener('submit', (event) => {
    event.preventDefault();
    console.log(new FormData(form).get('email'));
  });
</script>

The browser runs native validation before dispatching submit. Call form.reportValidity() to show validation feedback directly, or form.checkValidity() for a boolean check without the browser's validation UI.

Compose Slots ​

Default slots hold primary content. Named slots identify component-owned regions.

html
<ore-card>
  <span slot="header">Account</span>
  <p>Profile settings</p>
  <div slot="footer">
    <ore-button variant="ghost">Cancel</ore-button>
    <ore-button>Save</ore-button>
  </div>
</ore-card>

Input components commonly expose prefix, suffix, label, helper, and error slots. Each component reference lists its supported slots.

Load TypeScript Framework Declarations ​

Import the declaration matching the framework once in an application type entry point.

ts
import type {} from '@vielzeug/refine/frameworks/elements';
ts
import type {} from '@vielzeug/refine/frameworks/react';
ts
import type {} from '@vielzeug/refine/frameworks/vue';

These entry points provide typed tags and component properties. They do not register components or install runtime adapters.

Framework Integration ​

Refine elements use native DOM properties, events, and slots. Frameworks differ only in how they forward custom-element properties and events.

tsx
import '@vielzeug/refine/button';

export function SaveButton() {
  return <ore-button variant="solid">Save</ore-button>;
}
vue
<script setup lang="ts">
import '@vielzeug/refine/button';
</script>

<template>
  <ore-button variant="solid">Save</ore-button>
</template>
svelte
<script lang="ts">
  import '@vielzeug/refine/button';
</script>

<ore-button variant="solid">Save</ore-button>

See Framework Integration for custom events, controlled properties, SSR guards, and framework-specific configuration.

Working with Other Vielzeug Libraries ​

Ore ​

Use Ore when authoring a custom element that composes Refine components.

ts
import '@vielzeug/refine/button';
import { define, html } from '@vielzeug/ore';

define('save-panel', {
  setup() {
    return html`<ore-button @click=${() => console.log('save')}>Save</ore-button>`;
  },
});

Ripple ​

Use Ripple to drive component properties from application state.

ts
import '@vielzeug/refine/button';
import { effect, signal } from '@vielzeug/ripple';

const loading = signal(false);
const button = document.querySelector('ore-button') as HTMLElement & { loading?: boolean };
const loadingEffect = effect(() => {
  button.loading = loading.value;
});

loadingEffect.dispose();

Best Practices ​

  • Import tokens.css once before rendering components.
  • Register components through explicit subpaths.
  • Set structured data and callbacks as JavaScript properties.
  • Read form state from the event target instead of custom event payloads.
  • Provide accessible labels for icon-only controls.
  • Use component reference pages for supported slots, parts, and CSS properties.
  • Dispose application-owned Ripple effects and service handles.
  • Guard DOM-dependent component registration during SSR.
  • Working with AI agents? Install the vielzeug skill (npx -y @vielzeug/codex skills install) so agents reach for Refine components and tokens instead of custom widgets: see the Codex usage guide.