Skip to content

Settings Panels

Gadgets can provide their own settings panel that appears in the Torchsnap settings window. This lets users configure gadget-specific options through a native-feeling UI rather than editing files manually. Settings panels use the same React and SDK infrastructure as views, but run in the settings window instead of the launcher.

Settings panel components receive an empty props object. All data access happens through hooks:

import type { GadgetSettingsProps } from "@torchsnap/gadget-sdk";
export function MySettings(props: GadgetSettingsProps) {
// ...
}

The host wraps your component in a standardized settings header (gadget name, description, enable/disable toggle) and a GadgetContextProvider. Your component renders the gadget-specific configuration controls below that header.

The primary hook for settings panels. It provides reactive read/write access to a single setting key, scoped to the gadget’s namespace:

import { useGadgetSetting } from "@torchsnap/gadget-sdk/hooks";
const [retentionDays, setRetentionDays] = useGadgetSetting<number>("retentionDays");
Return Description
value: T The current value, JSON-parsed. Available synchronously on first render (the settings store is pre-loaded).
setValue: (v: T) => Promise<void> Write a new value. The change propagates to all windows and to the backend’s on_setting_changed callback.

Keys are relative to the gadget’s namespace. Requesting "retentionDays" reads the full key gadgets.<gadget-id>.retentionDays in the store, but you never see the prefix.

Declare defaults in the manifest’s [settings] section. These are seeded into the store on first load and never overwrite existing user values:

manifest.toml
[settings]
retentionDays = 30
historyEnabled = true
theme = "auto"

Your useGadgetSetting calls will return these defaults until the user changes them.

The SDK provides pre-built UI components that match the host’s design language. Import them from @torchsnap/gadget-sdk/components:

A toggle switch for boolean settings:

import { Switch } from "@torchsnap/gadget-sdk/components";
const [enabled, setEnabled] = useGadgetSetting<boolean>("historyEnabled");
<Switch
checked={enabled}
onChange={setEnabled}
aria-label="Enable history"
/>

A numeric range input with optional value formatting:

import { Slider } from "@torchsnap/gadget-sdk/components";
const [days, setDays] = useGadgetSetting<number>("retentionDays");
<Slider
value={days}
onChange={setDays}
min={1}
max={90}
step={1}
formatValue={(v) => `${v} days`}
/>

Groups related settings under an optional heading:

import { Section } from "@torchsnap/gadget-sdk/components";
<Section title="History">
{/* Setting controls */}
</Section>

A labeled row for a single setting, with an optional description:

import { Entry } from "@torchsnap/gadget-sdk/components";
<Entry label="Retention period" description="How long to keep history entries">
<Slider value={days} onChange={setDays} min={1} max={90} />
</Entry>

A structured list with optional per-item actions:

import { List } from "@torchsnap/gadget-sdk/components";
<List
items={[
{
key: "item-1",
primary: "First item",
secondary: "Description text",
actions: [
{ label: "Remove", onClick: () => remove("item-1"), variant: "danger" },
],
},
]}
emptyState={<p>No items yet.</p>}
/>

Register the settings panel in the manifest. The component value is the JavaScript named export from the settings bundle:

manifest.toml
[frontend]
settings-bundle = "frontend/dist/settings.js"
settings-css = "frontend/dist/settings.css"
[frontend.settings]
component = "MySettings"

Your settings entry point must export the matching name:

frontend/src/settings.tsx
export { Settings as MySettings } from "./settings/Settings";