Get started
The form (Solid)
The same state machine, as accessors: every value is a function, so the JSX tracks exactly what it reads.
tsx
import { createBugReport } from "bugbottle/solid";
import { htmlToImage } from "bugbottle/html-to-image"; // optional
import { For, Show, onMount } from "solid-js";
function FeedbackForm() {
const form = createBugReport({
endpoint: "/api/feedback",
screenshot: htmlToImage, // leave out to disable screenshots
});
onMount(() => form.open());
return (
<form data-bugbottle onSubmit={(e) => (e.preventDefault(), form.submit())}>
<For each={form.types}>
{(t) => (
<button type="button" onClick={() => form.setType(t)}>
{t}
</button>
)}
</For>
<textarea value={form.message()} onInput={(e) => form.setMessage(e.currentTarget.value)} />
<Show when={form.canScreenshot()}>
<label>
<input
type="checkbox"
checked={form.includeScreenshot()}
onChange={(e) => form.toggleScreenshot(e.currentTarget.checked)}
/>
Attach a picture of this page
</label>
</Show>
<Show when={form.screenshot()}>{(src) => <img src={src()} alt="" />}</Show>
<button type="button" onClick={() => form.pickElement()}>
{form.isPicking() ? "Click anything to attach it — Esc to stop" : "Point at the element"}
</button>
<ul>
<For each={form.elements()}>
{(el, i) => (
<li>
<code>{el.selector}</code> {el.text}
<button type="button" onClick={() => form.removeElement(i())}>
×
</button>
</li>
)}
</For>
</ul>
<p role="status">{form.statusMessage()}</p>
<button disabled={form.isSending()}>Send</button>
</form>
);
}The subscription is torn down with the owner the function was called in — the
component, normally; call form.destroy() yourself if you called it outside
one. solid-js is an optional peer dependency, so nothing about it reaches a
project that does not use it.