Build your own chat interface with complete control over HTML, CSS, and behavior. You provide the markup and styling — cStar binds everything automatically via data-cstar-* attributes.
Overview
Custom Mode gives you a lightweight embed (~9KB gzipped) that connects your HTML to cStar's chat backend. Unlike the prebuilt widget, custom mode reads data-cstar-* attributes from your HTML and wires up authentication, messaging, real-time updates, file attachments, and view switching.
cStar switches to custom mode when it sees data-cstar-launcher or data-cstar-panel on the page. No flag, no config.
For programmatic control via npm instead, see @cstar.help/js, @cstar.help/react, and @cstar.help/svelte.
Want a flat reference? All data-cstar-* Attributes is the same content, condensed into Cmd+F-friendly tables.
Installation
<script src="https://www.cstar.help/cstar.js?team=YOUR_TEAM_SLUG"></script>
Replace YOUR_TEAM_SLUG with the value in Settings → Team → Install.
The script scans the page for data-cstar-* attributes and binds them automatically. New attributes added after load are picked up via a MutationObserver — no rebind call needed.
Action Triggers
Add to any clickable element. cStar attaches a click handler.
| Attribute | What It Does |
|---|---|
data-cstar-launcher |
Toggle the panel open/closed |
data-cstar-close |
Close the panel |
data-cstar-minimize |
Minimize (same as close) |
data-cstar-back |
Return to the conversations list |
data-cstar-new-conversation |
Switch to the new-conversation view |
data-cstar-create-conversation |
Submit the new-conversation form |
data-cstar-send |
Send the current message draft |
data-cstar-signup-submit |
Submit the signup form |
data-cstar-login-submit |
Submit the login form |
data-cstar-forgot-password-submit |
Submit the forgot-password form |
data-cstar-magic-link |
Send a magic link to the email field |
data-cstar-logout |
Log the customer out |
data-cstar-switch-to-login |
Switch the auth view to login |
data-cstar-switch-to-signup |
Switch the auth view to signup |
data-cstar-switch-to-forgot |
Switch the auth view to forgot-password |
data-cstar-switch-auth="login|signup" |
Flexible: switch auth via attribute value |
data-cstar-attach-trigger |
Open the file picker (clicks [data-cstar-attach]) |
data-cstar-attachment-remove |
Remove the currently attached file |
data-cstar-notifications-enable |
Request browser push notification permission |
data-cstar-notifications-dismiss |
Dismiss the notification prompt |
data-cstar-delete-conversation |
Delete a conversation. Read data-conversation-id from the element or its closest [data-cstar-conversation] ancestor |
Form Inputs
Add to <input>, <textarea>, or <form> elements.
| Attribute | Element | Behavior |
|---|---|---|
data-cstar-input="messageDraft" |
textarea/input | Message composer. Enter sends, Shift+Enter newline. Default if no value given. |
data-cstar-input="authEmail" |
input | Email field for auth |
data-cstar-input="authName" |
input | Name field for signup |
data-cstar-input="authPassword" |
input | Password field for auth |
data-cstar-input="subject" |
input | Subject field for new conversations |
data-cstar-email |
input | Shorthand for data-cstar-input="authEmail" |
data-cstar-name |
input | Shorthand for data-cstar-input="authName" |
data-cstar-subject |
input | Shorthand for data-cstar-input="subject" |
data-cstar-attach |
<input type="file"> |
The hidden file input that data-cstar-attach-trigger clicks |
data-cstar-sound-toggle |
checkbox | Toggle agent-message sound notifications |
data-cstar-form="signup|login|forgot|compose|prechat|newConversation|new" |
form | Auto-binds the form's submit to the matching SDK action |
data-cstar-signup / -login / -compose / -prechat / -newConversation |
form | Boolean alternatives to data-cstar-form |
Live Content
cStar updates these elements automatically.
| Attribute | What It Renders |
|---|---|
data-cstar-company-name |
Your team's company name (textContent) |
data-cstar-company-logo |
Sets src on an <img> to your team logo |
data-cstar-status |
"Online" or "Offline" based on agent presence |
data-cstar-unread-count |
Total unread message count (hidden when 0, "99+" when over 99) |
data-cstar-content="currentConversationSubject" |
Subject of the currently open conversation |
data-cstar-connection="connected|connecting|reconnecting|offline" |
One element per state, shown only when realtime is in that state |
View Switching
Define mutually exclusive views. Only the active view is visible.
<div data-cstar-view="auth"><!-- login/signup forms --></div>
<div data-cstar-view="conversations"><!-- conversation list --></div>
<div data-cstar-view="messages"><!-- active conversation --></div>
<div data-cstar-view="new"><!-- new-conversation form --></div>
<div data-cstar-view="offline"><!-- offline fallback --></div>
Combine with auth-form state:
<div data-cstar-view="auth" data-cstar-show-when="authForm:signup"><!-- signup --></div>
<div data-cstar-view="auth" data-cstar-show-when="authForm:login"><!-- login --></div>
Standalone Auth-Form Visibility
If you don't want a separate data-cstar-view="auth" wrapper, hang these on individual auth panels:
| Attribute | Visible When |
|---|---|
data-cstar-auth-signup |
Auth form is "signup" |
data-cstar-auth-login |
Auth form is "login" |
data-cstar-auth-forgot |
Auth form is "forgot" |
data-cstar-forgot-password-sent |
After forgot-password email is sent |
Conditional Visibility (data-cstar-show-when)
| Value | Visible When |
|---|---|
open |
Panel is open |
closed |
Panel is closed |
identified |
A customer is logged in |
anonymous |
No customer is logged in |
online |
Agents are available |
offline |
No agents available |
currentView:conversations|messages|auth|new|offline |
Current view matches |
authForm:signup|login|forgot |
Auth form matches |
conversations:empty |
Customer has no conversations |
State-Driven Panels (Auto-Hidden)
Hidden by default. cStar reveals them when the state matches.
| Attribute | Behavior |
|---|---|
data-cstar-panel |
Wrap your whole chat panel — toggles with open/close. Sets data-open="true|false" for CSS hooks |
data-cstar-loading |
Visible while a request is in flight |
data-cstar-error |
Visible when an error message is set. Inner [data-cstar-error-text] receives the message |
data-cstar-error-text |
Receives the latest error message as textContent |
data-cstar-empty |
Visible when the conversation list is empty |
data-cstar-agent-typing |
Visible while an agent is typing |
data-cstar-attachment-preview |
Visible while a file is staged. Inner [data-cstar-attachment-name] shows the filename |
data-cstar-attachment-name |
Receives the staged attachment's filename |
data-cstar-notifications-prompt |
Visible only while browser notification permission is default |
Conversation List
Define the list and its row template:
<div data-cstar-conversations>
<template data-cstar-conversation-template>
<div data-cstar-conversation>
<span data-cstar-title></span>
<span data-cstar-preview></span>
<span data-cstar-time></span>
<span data-cstar-unread></span>
</div>
</template>
</div>
<div data-cstar-empty>No conversations yet.</div>
Per-row attributes cStar populates inside the template:
| Attribute | Content |
|---|---|
data-cstar-conversation |
The row root. cStar adds data-conversation-id and binds a click → opens the conversation |
data-cstar-title |
Conversation subject |
data-cstar-preview |
Latest-message preview |
data-cstar-time |
Relative timestamp |
data-cstar-unread |
Unread badge content |
Message List
Define the container plus one or two templates (one per role):
<div data-cstar-messages>
<template data-cstar-message-template="agent">
<div data-cstar-message>
<img data-cstar-avatar />
<span data-cstar-agent-name></span>
<div data-cstar-content></div>
<div data-cstar-attachments></div>
<span data-cstar-time></span>
<span data-cstar-message-status="sending"></span>
<span data-cstar-message-status="sent"></span>
<span data-cstar-message-status="failed"></span>
<button data-cstar-message-retry>Retry</button>
</div>
</template>
<template data-cstar-message-template="customer">
<div data-cstar-message>
<div data-cstar-content></div>
<div data-cstar-attachments></div>
<span data-cstar-time></span>
</div>
</template>
</div>
Per-message attributes:
| Attribute | Content |
|---|---|
data-cstar-message |
The message row root |
data-cstar-content |
Rendered message body (markdown for agent messages) |
data-cstar-attachments |
Container cStar fills with attachment chips |
data-cstar-time |
Relative timestamp |
data-cstar-avatar |
Receives src for the agent avatar |
data-cstar-agent-name |
Agent display name |
data-cstar-message-status="sending|sent|failed" |
Shown only when status matches |
data-cstar-message-retry |
Click to retry a failed message |
JavaScript API (window.CStarChat)
The SDK is exposed globally once the module loads. Calls made before load are queued.
// Panel
CStarChat.open();
CStarChat.close();
CStarChat.toggle();
CStarChat.isOpen();
// Identity
await CStarChat.signup({ email, name, password });
await CStarChat.login({ email, password });
await CStarChat.magicLink({ email });
await CStarChat.forgotPassword({ email });
CStarChat.identify({ email, name, customer_id });
CStarChat.verifyIdentity(customer, signature, testMode);
CStarChat.logout();
CStarChat.isIdentified();
CStarChat.getCustomer();
// Conversations
await CStarChat.startConversation({ subject, message });
CStarChat.openConversation(conversationId);
CStarChat.deleteConversation(conversationId);
CStarChat.getConversations();
CStarChat.getCurrentConversation();
// Messaging
CStarChat.send('Hello!');
CStarChat.attachFile(file);
CStarChat.removeAttachment();
CStarChat.getAttachment();
// Views & Forms
CStarChat.showView('conversations'); // or 'messages' | 'auth' | 'new' | 'offline'
CStarChat.getCurrentView();
CStarChat.showAuthForm('login'); // or 'signup' | 'forgot'
CStarChat.getAuthForm();
// Drafts (for headless input handling)
CStarChat.setDraft(value);
CStarChat.setSubject(value);
CStarChat.setAuthEmail(value);
CStarChat.setAuthName(value);
CStarChat.setAuthPassword(value);
// State
CStarChat.getUnreadCount();
CStarChat.getConnectionStatus();
CStarChat.isOnline();
CStarChat.isSoundEnabled();
CStarChat.setSoundEnabled(true);
CStarChat.requestNotifications();
// Knowledge Base
await CStarChat.searchArticles('billing', 5);
await CStarChat.getArticle('api-authentication');
await CStarChat.getPopularArticles(5);
await CStarChat.getCategories();
await CStarChat.getLibraryStats();
// Lifecycle
CStarChat.refresh();
CStarChat.destroy();
Events (CStarChat.on)
CStarChat.on('ready', () => { /* widget loaded */ });
CStarChat.on('open', () => { /* panel opened */ });
CStarChat.on('close', () => { /* panel closed */ });
CStarChat.on('identify', (customer) => { /* logged in */ });
CStarChat.on('logout', () => { });
CStarChat.on('view', (viewName) => { });
CStarChat.on('connection', (status) => { /* connected | connecting | reconnecting | offline */ });
CStarChat.on('online', (isOnline) => { });
CStarChat.on('unread', (count) => { });
CStarChat.on('message', (msg) => { /* any message */ });
CStarChat.on('message:received', (msg) => { /* from agent */ });
CStarChat.on('message:sent', (msg) => { /* from customer */ });
CStarChat.on('messages', (allMessages) => { });
CStarChat.on('conversations', (allConversations) => { });
CStarChat.on('conversation:open', (conversation) => { });
CStarChat.on('typing', (isTyping) => { });
CStarChat.on('attachment:complete', (attachment) => { });
CStarChat.on('attachment:removed', () => { });
CStarChat.on('loading', (isLoading) => { });
CStarChat.on('error', (error) => { });
CStarChat.on('session:expired', () => { });
// Unsubscribe
CStarChat.off('message', handler);
Tips
- Wrap your panel in
data-cstar-panelso open/close just works. - Templates beat manual rendering — let the binder handle conversation rows and message bubbles.
data-cstar-show-whencovers most state branches without writing JavaScript.- Keep clickable elements at least 42px tall for mobile.
- Agent message content is markdown — render with
markedor similar if you're not using the templates. - New DOM mounted after page load is auto-bound. No
bind()call needed.