Get started

The form (Svelte)

The same state machine, as a readable store: $form for the values, the methods on form for everything the reporter does.

svelte
<script lang="ts">
  import { createBugReport } from "bugbottle/svelte";
  import { htmlToImage } from "bugbottle/html-to-image"; // optional
  import { onDestroy, onMount } from "svelte";

  const form = createBugReport({
    endpoint: "/api/feedback",
    screenshot: htmlToImage, // leave out to disable screenshots
  });

  onMount(() => form.open());
  onDestroy(() => form.destroy());
</script>

<form data-bugbottle on:submit|preventDefault={() => form.submit()}>
  {#each $form.types as t}
    <button type="button" on:click={() => form.setType(t)}>{t}</button>
  {/each}

  <textarea value={$form.message} on:input={(e) => form.setMessage(e.currentTarget.value)} />

  {#if $form.canScreenshot}
    <label>
      <input
        type="checkbox"
        checked={$form.includeScreenshot}
        on:change={(e) => form.toggleScreenshot(e.currentTarget.checked)}
      />
      Attach a picture of this page
    </label>
  {/if}
  {#if $form.screenshot}<img src={$form.screenshot} alt="" />{/if}

  <button type="button" on:click={() => form.pickElement()}>
    {$form.isPicking ? "Click anything to attach it — Esc to stop" : "Point at the element"}
  </button>
  <ul>
    {#each $form.elements as el, i}
      <li><code>{el.selector}</code> {el.text}
        <button type="button" on:click={() => form.removeElement(i)}>×</button>
      </li>
    {/each}
  </ul>

  <p role="status">{$form.statusMessage}</p>
  <button disabled={$form.isSending}>Send</button>
</form>

svelte is an optional peer dependency, and only its Readable type is used: the store contract is one function, implemented here, so the adapter adds no runtime dependency at all.

Edit this page on GitHub