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, so 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

  1. Wrap your panel in data-cstar-panel so open/close just works.
  2. Templates beat manual rendering. Let the binder handle conversation rows and message bubbles.
  3. data-cstar-show-when covers most state branches without writing JavaScript.
  4. Keep clickable elements at least 42px tall for mobile.
  5. Agent message content is markdown. Render with marked or similar if you're not using the templates.
  6. New DOM mounted after page load is auto-bound. No bind() call needed.