Skip to content
Svelte Helpers
Language:en Author:AtomForge

Recipes and pitfalls

A reusable button

This component combines the prop types, XOR, and variantMap.

<script lang="ts">
  import { variantMap } from "@atom-forge/svelte-helpers";
  import type {
    AnyProp,
    ChildrenProp,
    ClassProp,
    XOR
  } from "@atom-forge/svelte-helpers";
  import { twMerge } from "tailwind-merge"; // external

  type Variant = XOR<{ primary: true }, { secondary: true }, { ghost: true }, {}>;

  let {
    primary,
    secondary,
    ghost,
    class: classes,
    children,
    ...props
  }: Variant & ChildrenProp & ClassProp & AnyProp = $props();

  const variant = $derived(variantMap({ primary, secondary, ghost }, "default"));
  const cls = $derived(twMerge("btn", `btn-${variant}`, classes));
</script>

<button {...props} class={cls}>{@render children()}</button>

Usage: <Button primary>Save</Button>. <Button primary ghost> is a type error.

A debounced search input

<script lang="ts">
  import { debounce } from "@atom-forge/svelte-helpers";

  let { onsearch }: { onsearch: (q: string) => void } = $props();

  const emit = debounce((q: string) => onsearch(q), 300);
</script>

<input oninput={(e) => emit(e.currentTarget.value)} />

Pitfalls

Symptom Cause Fix
Variant never changes after prop update variantMap wrapped in untrack Use $derived(variantMap(...))
small always wins over compact First truthy entry wins and no XOR Type the props with XOR
Awaited debounced call never resolves Superseded debounceAsync promises stay pending Do not await it; update state inside the callback
Error from debounced async function disappears Rejections are not forwarded try/catch inside the callback
Runtime type error after as.string(...) as performs no conversion Validate or convert the value yourself
RenderSnippet throws Missing snippet, or snippet takes no argument object Pass both props; define {#snippet name(args)}

Prerequisites (3)

  • prop-types
  • variant-props
  • runtime-helpers