Coming in Broadcast 3.0. Until then, this page applies only to the Upcoming (edge) build.

The sg API

kit.js adds one global object, sg. It is how your page talks to Broadcast and to the person using it.

Name Does
sg.pageData(input, page) Calls your plugin’s pageData function
sg.call(op, args) Calls one bridge operation
sg.toast(text, options) Shows a short message
sg.confirm(options) Asks yes or no
sg.copy(text) Copies text
sg.openExternal(url) Opens a link outside Broadcast, after the person agrees
sg.subscriberId() Reads the subscriber of a panel
sg.SgError The error class
sg.version "1"

sg.pageData

Calls pageData in your dist/main.js, and gives you what it returns.

const data = await sg.pageData({ action: "refresh" });
Value Default Meaning
input {} Any JSON object. Your function gets it as input
page 0 A number your function gets as page. Use it for a page index, or ignore it

On the plugin side:

export default definePlugin({
  async pageData({ page, input }, ctx) {
    return { message: `You asked for ${input.action}` };
  },
});

This is the main door. Use it for everything that needs secrets, writes or fetch. pageData runs with the plugin’s permissions, so check input.

sg.call

Calls one bridge operation directly. It gives the result, or it throws.

const opens = await sg.call("store.increment", { key: "opens" });
const tags = await sg.call("tags.list");
const person = await sg.call("store.get", { key: "note", subscriber_id: 12 });

Plugin Pages lists the allowed operations. A page acts as the person viewing it, and it cannot change subscribers or read secrets. For those, use pageData.

SgError

sg.call and sg.pageData throw an SgError when a call fails. It has two useful fields:

Field Meaning
code A short code, such as forbidden, not_found, plugin_error or timeout
message Text for you
try {
  await sg.call("subscribers.list");
} catch (error) {
  if (error.code === "forbidden") {
    sg.toast("This plugin may not read subscribers.", { tone: "danger" });
  } else {
    throw error;
  }
}

Always handle errors. A call can fail for reasons that are not your bug, such as a slow plugin. Without a try, the page just stops, with no word to the person.

When your pageData function throws, Broadcast does not pass its message to the browser. Both code and message are then plugin_error. Read the plugin’s logs for the real message. To show a friendly message, return it as data instead of throwing:

if (!text) return { error: "Write a note first." };

sg.toast

A short message at the bottom right that goes away by itself.

sg.toast("Saved");
sg.toast("Not saved: the server did not answer", { tone: "danger", duration: 8000 });
Option Default Meaning
tone Dark danger makes it red. Other tones have no effect
duration 4000 Milliseconds on screen

It returns the message element. Text is shown as text.

Common mistake. Showing important information in a toast. It disappears. Use sg-banner.

sg.confirm

Asks a question in a dialog. It gives true or false.

const yes = await sg.confirm({
  title: "Delete this poll?",
  message: "The answers are deleted too.",
  confirmLabel: "Delete poll",
  danger: true,
});
if (!yes) return;
Option Default Meaning
title Are you sure? The question
message More detail
confirmLabel Confirm The button that says yes
cancelLabel Cancel The button that says no
danger false Makes the yes button red

The Esc key counts as no. Name the action in confirmLabel: “Delete poll” is better than “OK”.

sg.copy

Copies text. It gives true if that worked, and false if not.

const copied = await sg.copy(link);
sg.toast(copied ? "Copied" : "Press Cmd+C to copy");

Call it from a click. The sandbox can refuse the clipboard. In a test with no click, it gave false. So always check the answer, or use sg-copy, which handles this for you.

sg.openExternal

Asks Broadcast to open a link outside Broadcast. Broadcast shows the person the full address, and the person chooses. It gives { opened: true } or { opened: false }.

button.addEventListener("click", async () => {
  const { opened } = await sg.openExternal("https://example.com/guide");
});

Call it from a click. The address must be plain https, with no user name or password, and up to 2048 characters. Otherwise it throws an SgError with one of these codes:

Code Meaning
invalid_url Broadcast does not accept the address
no_user_action The call did not come from a recent click inside your page
busy The dialog is open, or it closed less than a second ago

sg.subscriberId

In a subscriberPanel frame, it gives the subscriber’s ID as a string. Anywhere else, it gives null.

const id = sg.subscriberId();

Components and events

All Signal events bubble, so you can listen on a parent. The data is in event.detail.

Event From detail
sg-submit sg-form { values }
sg-search sg-search { value }
sg-change sg-checkbox, sg-switch { checked }
sg-change sg-radio-group, sg-tag-input { value }
sg-select sg-menu { value }
sg-close sg-dialog { value }
sg-tab sg-tabs { index, value }
sg-page sg-pagination { page }
sg-row-click sg-table { row }

Last updated

Was this page helpful?

Thanks for your feedback!

Thanks for letting us know. We'll work on improving this page.