Tags

Freeform tag input with inline editing. Arrow/backspace moves the caret so you can insert at any position, click a tag to edit it in place, and control rendering/validation via snippets. Arrow keys do not reorder tags; they only move the caret so tags stay put unless you edit their text.

Press to create a tag at the caret. at the start of a tag pulls the previous tag into edit mode; clicking a pill does the same. and move the cursor around


Usage

<script lang="ts">
	import { Tags } from '@obelus/trioxide';

	type Tag = { value: string };

	let tags: Tag[] = $state([{ value: 'svelte' }]);

	const normalize = ({ value }: Tag) => {
		const v = value.trim().toLowerCase();
		if (!v) return;
		return { value: v } satisfies Tag;
	};

	const shouldCreate: (event: KeyboardEvent) => boolean = (event) =>
		event.key === 'Enter' || event.key === ',';
</script>

<Tags
	bind:tags
	onTag={normalize}
	createOn={shouldCreate}
	class="flex flex-wrap items-center gap-2 rounded-sm border border-(--trioxide_neutral-7) p-2 focus-within:border-(--trioxide_highlight-10)"
>
	{#snippet Tag(tag, props)}
		<span class="rounded-sm bg-(--trioxide_neutral-3) px-2 py-1" {...props}>
			{tag.value}
			<button aria-label={`Remove ${tag.value}`} onclick={() => tags.splice(tags.indexOf(tag), 1)}>
				<i class="ri-close-line"></i>
			</button>
		</span>
	{/snippet}

	{#snippet PlaceHolder()}
		<span class="text-(--trioxide_neutral-7)">Add a tag...</span>
	{/snippet}
</Tags>

Props

  • tags / bind:tags: array mutated in place as the user types or deletes; keep it in $state if you need reactivity. tags is generic, so each item can carry additional props (id, color, count) that you render inside Tag.
  • Tag: snippet (tag, props); render your pill and spread props so the component can handle focus and click-to-edit behavior.
  • PlaceHolder: optional snippet shown when no tags are present and the input is unfocused.
  • createOn (default [' ', 'Enter']): keys that create a tag, or a predicate (event: KeyboardEvent) => boolean to decide per keystroke.
  • onTag({ value }): optional sanitizer; return the normalized tag object to accept it or undefined to reject (used on creation, blur, and when moving tags around).
  • cursorProps are used to pass props to the cursor <input>
  • Remaining HTMLLabelAttributes apply to the outer <label> (e.g., classes, aria-*).