Skip to content

Widget builder reference

This page lists what your code can use in a KickBot widget builder widget: the widgetEvent events, the globalValues object, the userVariables object, and the window.kickbotWidget helpers. Every widget is plain HTML, CSS and JavaScript that runs as a browser source in OBS. KickBot connects to Kick for you and sends events to your code.

When OBS opens the widget URL, KickBot:

  1. Loads the widget page with a transparent background.
  2. Adds your CSS in a <style> element and puts your HTML in the page.
  3. Loads your channel data from Kick (follower count, viewer count, stream status and recent chat) into globalValues.
  4. Runs your JavaScript. It runs once, after step 3, so globalValues is ready when your code starts.
  5. Sends live events to your code as widgetEvent events on document.

Your JavaScript can read two objects without importing anything: globalValues and userVariables. The helpers are on window.kickbotWidget.

Limits:

ItemLimit
Width and heightSet in Basic settings. A new blank widget is 800 × 600. The New widget popup accepts up to 7680 × 4320.
HTML, CSS and JavaScriptUp to 2,000,000 characters per file
Widget nameUp to 500 characters
Files for user variablesUp to 25 MB per file
Builder widgets on the Free plan10 (no limit on Premium)

Add one listener for widgetEvent on document. Each event has event.detail with three fields:

FieldTypeValue
event_namestringThe event, for example followEvent
sourcestringAlways kick
dataobjectThe event payload (see below)
document.addEventListener('widgetEvent', (event) => {
const { event_name, data } = event.detail;
if (event_name === 'followEvent') {
showAlert(data.user.username + ' followed!');
}
if (event_name === 'subEvent') {
showAlert(data.username + ' subscribed for ' + data.months + ' months');
}
});
Event nameWhen it firesTest button
subEventA viewer subscribesSub
subGiftEventA viewer gifts subsGifted sub
kicksEventA viewer sends KICKsKICKs
followEventA viewer followsFollow
unfollowEventA viewer unfollowsUnfollow
chatMessageEventA chat message is sentMessage
chatMessageDeletedEventA moderator deletes a messageMessage deleted
chatMessagePinnedEventA message is pinnedMessage pin
chatMessageUnpinnedEventThe pinned message is removedMessage unpin
chatClearedEventA moderator clears the chatChat cleared
followerCountUpdatedEventKick sends a new follower countFollower count updated
subCountUpdatedEventThe sub count on Kick changedSub count updated
viewerCountUpdatedEventKick sends a new viewer countViewer count updated
raidEventAnother channel raids (hosts) youRaid
streamOnlineYour stream startsStream online
streamOfflineYour stream endsStream offline

There is no tip event. For tip alerts, use the tip alert widget.

These are the payloads in event.detail.data. They match the test events in the editor. Real events can have more fields. Names, numbers and IDs here are examples.

subEvent

{
months: 1,
username: 'viewer_one',
chatroom_id: 3847194
}

subGiftEvent

{
chatroom_id: 3847194,
gifted_usernames: ['viewer_one', 'viewer_two', 'viewer_three'],
gifter_username: 'big_gifter'
}

kicksEvent

{
message: 'Great stream!',
sender: { id: 12345678, username: 'viewer_one', username_color: '#93EBE0' },
gift: {
gift_id: 'full_send',
name: 'Full Send',
amount: 100,
type: 'BASIC',
tier: 'BASIC',
character_limit: 0,
pinned_time: 0
},
channel: 'channel_3984507'
}

followEvent and unfollowEvent (same shape; followers_count is the new count)

{
id: 'd4e5f6a7-89b0-12c3-d456-78901e2f3a4b',
channel: { id: 3984507, slug: 'kickbotdev' },
user: { id: 14873539, slug: 'viewer_one', username: 'viewer_one' },
followers_count: 36,
created_at: '2024-05-13T22:19:07+00:00'
}

chatMessageEvent

{
id: '85c884b3-7377-478c-b6aa-22b7db752b93',
chatroom_id: 3974112,
content: 'hello [emote:37226:KEKW]',
type: 'message',
created_at: '2024-05-13T22:29:52+00:00',
sender: {
id: 4063326,
username: 'viewer_one',
slug: 'viewer_one',
identity: {
color: '#72ACED',
badges: [{ type: 'subscriber', text: 'Subscriber', count: 3 }]
}
}
}

Kick emotes are in content as [emote:ID:NAME]. Use createChatMessageFragment (see Show chat text safely) to turn them into images.

chatMessageDeletedEvent

{
id: '9cd3788a-ecd2-4051-a162-a7ea9be8ce7a',
message: { id: '89d6be86-63db-4581-922e-14a6c4a9c2e6' }
}

Use data.message.id to find and remove the message you showed.

chatMessagePinnedEvent (data.message has the same shape as a chatMessageEvent payload)

{
message: {
id: '5d798a38-ad17-45e4-8169-bda2faa85b79',
chatroom_id: 3974112,
content: 'This is a pinned message!',
type: 'message',
created_at: '2024-05-13T22:30:41+00:00',
sender: { id: 4063326, username: 'kickbotdev', slug: 'kickbotdev', identity: { color: '#72ACED', badges: [] } }
}
}

chatMessageUnpinnedEvent: data is an empty array ([]).

chatClearedEvent

{ id: '47e3596f-0274-46e0-85b1-029fa3bfc5ea' }

followerCountUpdatedEvent (note the camelCase followersCount)

{ followersCount: 1532, channel_id: 4599, created_at: 1715639984 }

subCountUpdatedEvent

{ subscriber_count: 184, channel_id: 4599 }

viewerCountUpdatedEvent

{ viewer_count: 320, channel_id: 5937485 }

raidEvent

{
chatroom_id: 839485,
optional_message: 'gg stream',
number_viewers: 150,
host_username: 'raiding_channel'
}

streamOnline

{
livestream: {
id: 3984507,
channel_id: 3984507,
session_title: 'Test stream!',
created_at: '2024-05-13T22:30:41+00:00'
}
}

streamOffline

{
livestream: {
id: 3984507,
channel: { id: 3984507, is_banned: false }
}
}

globalValues holds the current state of your channel. KickBot fills it before your code runs and keeps it up to date as events come in. Use it to draw your widget at the start, before any event arrives.

globalValues = {
subCount: 184,
followerCount: 1532,
viewerCount: 320,
lastFollower: { /* the last follow */ },
lastSub: { /* the last sub, for example { username, months } */ },
lastGiftedSub: { /* the last gift, for example { gifter_username, gifted_usernames } */ },
chatMessages: [ /* chatMessageEvent payloads */ ],
streamerInfo: {
kick: {
username: 'KickBotDev',
slug: 'kickbotdev',
profile_picture: 'https://files.kick.com/images/user/.../fullsize.webp',
user_id: '4063326',
channel_id: '3984507',
chatroom_id: '3974112',
bio: 'Channel bio',
subscriber_badges: [ /* your channel's sub badges from Kick */ ]
}
},
currentStream: {
is_live: true,
title: 'Stream title',
started_at: '2026-10-24 18:00:00'
}
}
ValueHow it updates
subCountLoaded from Kick. Goes up by 1 for each subEvent and by 1 for each gifted sub, and is set again by subCountUpdatedEvent.
followerCountLoaded from Kick. Set by followEvent, unfollowEvent and followerCountUpdatedEvent.
viewerCountLoaded from Kick (0 when offline). Set by viewerCountUpdatedEvent.
lastFollower, lastSub, lastGiftedSubThe last one that KickBot saw on your channel, or null. Updated by the matching event.
chatMessagesRecent chat from Kick when the widget loads, then each new message. Keeps the newest 100. chatClearedEvent empties it. To hide deleted messages, handle chatMessageDeletedEvent in your own code.
streamerInfo.kickYour Kick profile. Does not change while the widget runs.
currentStreamLoaded from Kick when the widget loads. It is not updated by streamOnline or streamOffline. Listen to those events if you need the live state.

User variables are the settings that streamers see when they open your widget in Saved widgets. You add them under User variables in the editor. In your code, read the value with userVariables.<variable name>.value.

const goal = Number(userVariables.follower_goal.value);
document.body.style.color = userVariables.text_color.value;

Fields of a variable:

FieldWhat it is
TitleThe label streamers see.
Variable nameThe name in your code. Letters, numbers and _ only. You cannot change it after you create the variable.
TooltipExtra help text under the title.
Variable typeSee the table below.
Default valueThe value before the streamer changes it. It can be empty.
RequiredStreamers must enter a value before they can save.
TypeWhat the streamer seesvalue in your code
TextA text boxA string
NumberA number boxA number or a numeric string. Use Number() to be safe.
ColorA color pickerA hex color string, for example #ffffff
DropdownA menu with your optionsThe chosen option as a string. The default is the first option.
ToggleAn on/off switchtrue or false
Image/GIFA URL box and an upload buttonA URL to the file
VideoA URL box and an upload buttonA URL to the file
AudioA URL box and an upload buttonA URL to the file

Uploaded files are stored by KickBot, and value is the file URL. Use the menu next to a variable to move it up or down, edit it or delete it. The order in the editor is the order streamers see.

Treat every event field and user variable as text from a stranger. KickBot cleans chat and event text before your code gets it, so it is safe as HTML text, but not as a URL, CSS or script. Do not put raw event fields into innerHTML. Use these helpers:

HelperWhat it does
window.kickbotWidget.createChatMessageFragment(content, font_size)Returns a DocumentFragment with text nodes and Kick emote images. Emotes are font_size + 3 pixels. font_size defaults to 20.
window.kickbotWidget.createTextNode(value)Returns a text node for any value.
window.kickbotWidget.escapeHtmlText(value)Escapes text for use between HTML tags when you must build an HTML string. Do not use it for URLs, CSS or script.
document.addEventListener('widgetEvent', (event) => {
if (event.detail.event_name !== 'chatMessageEvent') return;
const data = event.detail.data;
const line = document.createElement('div');
line.id = 'msg-' + data.id;
const name = document.createElement('strong');
name.style.color = data.sender.identity.color;
name.append(window.kickbotWidget.createTextNode(data.sender.username + ': '));
line.append(name);
line.append(window.kickbotWidget.createChatMessageFragment(data.content, 20));
document.getElementById('chat').append(line);
});

For other text, use textContent or createTextNode.

The preview in the editor works like OBS, with a few differences for testing:

  • When you change the code or a user variable, the preview runs your JavaScript again. KickBot removes the widgetEvent listeners from the last run first. Timers from setInterval and setTimeout are not removed, so click the reload button if they pile up.
  • When you change the width or height, the preview reloads.
  • Test events only go to the preview. A test Follow adds 1 to globalValues.followerCount, and Unfollow takes 1 away. Viewer count updated sends a sample number. Message deleted removes a random message from the preview chat.
  • If your channel has no last follower, sub or gifted sub yet, the preview uses sample data for lastFollower, lastSub and lastGiftedSub.
  • To see console.log output and errors, click the pop-out button and open the developer tools in that window.

This is a short follower goal. It uses an event listener, globalValues and two user variables: follower_goal (Number) and bar_color (Color).

HTML:

<div class="goal">
<span id="count"></span> / <span id="target"></span>
<div class="bar"><div class="fill"></div></div>
</div>

CSS:

body { margin: 0; font-family: Arial, sans-serif; color: #fff; }
.goal { padding: 12px; background: #100c28; border-radius: 16px; font-size: 32px; font-weight: bold; }
.bar { height: 24px; margin-top: 8px; background: #3d3957; border-radius: 12px; overflow: hidden; }
.fill { height: 100%; width: 0; transition: width 0.3s ease; }

JavaScript:

const target = Number(userVariables.follower_goal.value) || 100;
document.getElementById('target').textContent = target;
document.querySelector('.fill').style.background = userVariables.bar_color.value;
function update(count) {
document.getElementById('count').textContent = count;
document.querySelector('.fill').style.width = Math.min((count / target) * 100, 100) + '%';
}
document.addEventListener('widgetEvent', (event) => {
const { event_name, data } = event.detail;
if (event_name === 'followEvent' || event_name === 'unfollowEvent') update(data.followers_count);
if (event_name === 'followerCountUpdatedEvent') update(data.followersCount);
});
update(globalValues.followerCount);

For a full version with more settings, start a new widget from the Simple Follower Goal template.

  • Draw the first state from globalValues, then change it on events. A widget that waits for an event looks empty until something happens.
  • Saved widgets only shows test buttons for event names that appear in your JavaScript. Write the full event name (for example 'followEvent') in your code so streamers can test your widget.
  • Keep the background transparent unless a background is part of the design, so the widget sits on top of the scene.
  • You can also manage builder widgets with the KickBot API. See the widgets API reference.