ARCY AI
BETA
Reference

Customization

Configure the ARCY widget's theme, individual elements, content, modes, and usage limits via code.

ARCY lets you tailor the embedded widget: logo and color theme, launcher position, size, and mount point, individual element styling, greeting copy, which modes are available, and per-mode usage limits. To hide the widget entirely on a given page, use the showWidget prop, see Configuration.

Configuring the widget

Pass customization directly as a prop on ARCYProvider. This is useful for applying per-tenant or per-user branding at runtime.

tsx
import { ARCYProvider } from "@arcyai/sdk"
import type { ArcyCustomization } from "@arcyai/sdk"
import logo from "./logo.svg"

const customization: ArcyCustomization = {
  theme: {
    logo,
    primaryColor: "#2563eb",
    borderRadius: 20,
    glassiness: 40,
    initialPosition: "bottom-right",
  },
  elements: {
    chatInput: { className: "my-input-style" },
    sendButton: { className: "my-button-style" },
  },
  content: {
    assistantName: "Arc",
    chatPlaceholder: "Ask Arc anything...",
    greetingMessage: "Hi! Ask me anything about this app.",
  },
  modes: {
    enabled: ["chat", "copilot", "autopilot"],
  },
  limits: {
    copilot: { max: 10, windowMs: 86400000 }, // Copilot mode's limit
    maxCreditsPerUser: { max: 500, windowMs: 2592000000 }, // 500 credits per 30 days, shared by all three capabilities including Chat
  },
}

export default function RootLayout({ children }) {
  return (
    <ARCYProvider publicKey={(process.env.NEXT_PUBLIC_ARCY_PUBLISHABLE_KEY || process.env.NEXT_PUBLIC_ARCY_TEST_PUBLISHABLE_KEY)!} customization={customization}>
      {children}
    </ARCYProvider>
  )
}

Theme

customization.theme (ArcyThemeConfig) controls the widget's visual skin and mount behavior: logo, colors, border, glass effect, font, stacking order, mount point, and launcher position. It skins and positions the existing widget UI; it does not change its structure (use Elements for that).

FieldTypeRange / formatDefault
logostring | { src: string }https:// URL, or a local bundler-resolved file import (e.g. import logo from "./logo.svg")ARCY's own brand logo
logoWidthnumberpxproportional to buttonSize in the launcher; 20 in the chat bar header
logoHeightnumberpxproportional to buttonSize in the launcher; 20 in the chat bar header
primaryColorstringhex, e.g. "#2563eb"widget's built-in accent color
highlightColorstringhexfalls back to primaryColor, then to a static default if neither is set
borderWidthnumberpx, 081
borderColorstringhex"#3f3f46"
borderRadiusnumberpx, 03228
backgroundColorstringhex. Composes with glassiness"#18181b"
glassinessnumber0100. Drives backdrop blur and background alpha0
textColorstringhex. Overrides the automatic contrast-derived foreground computed from backgroundColorauto-computed contrast color
scrollbarThumbColorstringhex"#3f3f46" (matches borderColor's default)
scrollbarThumbHoverColorstringhex. Chromium/Safari only (Firefox's scrollbar has no hover state)slightly lighter than scrollbarThumbColor
scrollbarTrackColorstringhextransparent
scrollbarWidthnumberpx, 2164
fontFamilystringany valid CSS font-family valueSDK's built-in font stack
zIndexnumberstacking order of the launcher and panel9994
containerRefReact.RefObject<HTMLElement | null>mounts the widget inside this element instead of fixed to the viewportviewport-fixed
draggablebooleanwhether the end user can drag and edge-dock the launchertrue
initialPosition"bottom-right" | "bottom-left" | "top-right" | "top-left" | "bottom-center" | "top-center" | "left-center" | "right-center"screen edge/corner the launcher spawns at on first mount"bottom-right"
buttonSizenumberpx, 40120. Diameter of the floating launcher button. The logo inside scales proportionally76
tsx
import logo from "./logo.svg"

const customization: ArcyCustomization = {
  theme: {
    logo, // or a hosted URL string: "https://example.com/logo.png"
    logoWidth: 32,
    logoHeight: 32,
    primaryColor: "#2563eb",
    highlightColor: "#f59e0b",
    borderWidth: 1,
    borderColor: "#3f3f46",
    borderRadius: 20,
    backgroundColor: "#18181b",
    glassiness: 40,
    textColor: "#f0f0f0",
    scrollbarThumbColor: "#52525b",
    scrollbarThumbHoverColor: "#71717a",
    scrollbarTrackColor: "transparent",
    scrollbarWidth: 6,
    fontFamily: "'Inter', sans-serif",
    zIndex: 2147483000,
    draggable: false,
    initialPosition: "bottom-left",
    buttonSize: 56,
  },
}

Logo sizing

logoWidth and logoHeight apply identically wherever the logo renders: the closed launcher button and the open chat bar header. Either can be set alone. The dimension you leave unset falls back to that render site's own default size, not to the other dimension's value, so setting only logoWidth on a non-square logo will not preserve its aspect ratio automatically. Values are not clamped: a logo larger than the launcher button or the chat bar's fixed logo badge will visually overflow.

How glassiness and backgroundColor compose?

They're not independent. glassiness drives two things at once, both derived from backgroundColor: the background's alpha (opacity) and its backdrop blur. At glassiness: 0 the widget background is fully opaque at your chosen backgroundColor. As glassiness increases toward 100, the background becomes more translucent (down to a minimum alpha, never fully transparent) and picks up backdrop blur, so content behind the widget shows through with a frosted-glass look. Changing backgroundColor alone (at any glassiness) changes the tint of that glass; changing glassiness alone changes how much glass effect is applied to that tint.

Scrollbar

The four scrollbar* fields style the vertical scrollbar in both the chat history and the settings panel's conversation history list, so the whole widget reads as one consistent surface. Leave any field unset to keep the SDK's built-in slim scrollbar (4px, transparent track, a muted thumb that matches the default border color). The styling applies in Chromium and Safari (::-webkit-scrollbar) and in Firefox (scrollbar-width / scrollbar-color); scrollbarThumbHoverColor only has an effect in Chromium/Safari, since Firefox's scrollbar-color has no hover state.

How initialPosition and draggable interact?

initialPosition only sets where the launcher spawns on first mount, and now supports the four corners (bottom-right, bottom-left, top-right, top-left) plus the four edge-center positions (bottom-center, top-center, left-center, right-center). If draggable is unset or true (the default), the end user can still drag it anywhere and it will edge-snap to the nearest corner or side. Set draggable: false to keep the launcher pinned exactly at its initialPosition spot with no drag or snap behavior at all.

Mounting inside your own layout

By default the widget is fixed to the viewport. Pass containerRef (a ref to an element in your own layout) to mount it inline instead — useful for embedding the launcher inside a specific panel or sidebar rather than floating over the whole page. initialPosition still applies, but relative to that container's bounds instead of the viewport's.

The ARCY dashboard's theme editor has no control for highlightColor. Set it via the customization prop in code, as shown above.

Elements

customization.elements (ArcyElementsConfig) lets you restyle individual internal parts of the widget, not just the overall theme. Each key is a stable public name for one internal DOM node, unaffected by internal SDK refactors, so an override you write today keeps working across SDK upgrades even if ARCY renames its internal CSS classes.

Each entry accepts an ArcyElementOverride:

FieldTypeNotes
classNamestringAppended to the widget's own base classes. Never replaces them, so the element keeps its built-in layout and behavior.
styleReact.CSSPropertiesInline style merged onto the element.

Supported element keys:

KeyTargets
launcherButtonThe floating launcher button
launcherWrapperThe launcher's positioning wrapper
panelContainerThe main panel container
panelHeaderThe panel's header bar
panelCloseButtonThe panel's close button
chatInputThe chat message input
sendButtonThe send/submit button
messageBubbleUserUser message bubbles
messageBubbleAssistantAssistant message bubbles
historyListThe conversation history list/container
interventionPromptThe proactive intervention prompt
coachmarkCopilot mode's coachmark/tooltip
toastStatus toasts (e.g. autopilot plan progress)
tsx
const customization: ArcyCustomization = {
  elements: {
    launcherButton: { className: "my-fab-style" },
    panelContainer: { style: { fontFamily: "inherit" } },
    chatInput: { className: "my-input-style" },
    sendButton: { className: "my-button-style" },
    messageBubbleUser: { className: "my-user-bubble" },
    messageBubbleAssistant: { className: "my-assistant-bubble" },
  },
}

Content

customization.content (ArcyContentConfig) configures copy shown inside the widget.

FieldTypeNotesDefault
assistantNamestringDisplay name for the assistant. Used in the panel header, chat/panel aria-labels, the "is thinking" status, close-confirmation copy, and coachmark/tooltip text (e.g. "Let {name} do it")."ARCY"
chatPlaceholderstringPlaceholder text for the chat input. Set this to override the name-derived default entirely (e.g. drop the "anything..." phrasing)."Ask {assistantName} anything..."
greetingMessagestringShown as the first message whenever the chat is opened with no history. Reappears any time history is empty, not just once per user.none
tsx
const customization: ArcyCustomization = {
  content: {
    assistantName: "Arc",
    chatPlaceholder: "Ask Arc anything...",
    greetingMessage: "Hi! Ask me anything about this app.",
  },
}

chatPlaceholder only needs to be set if you want placeholder copy that doesn't follow the "Ask {assistantName} anything..." pattern. Setting assistantName alone already updates the placeholder, the panel brand, and every aria-label/coachmark string that mentions the assistant by name.

content is a separate, sibling prop from ARCYProvider's uiLanguage, not part of it. uiLanguage picks a language for every piece of built-in widget chrome and for assistantName/chatPlaceholder/greetingMessage's defaults. content hand-writes your own copy for those same three fields. An explicit content field always wins over the uiLanguage default for that field, everything else still follows uiLanguage. See uiLanguage in Configuration.

Modes

customization.modes (ArcyModesConfig) restricts which of the three AI capabilities (Chat, Copilot, Autopilot) are available to end users. There is no per-query mode picker: on every message, the backend query router infers the right capability from the query text itself, constrained to whatever is listed in enabled.

FieldTypeRange / formatDefault
enabledArcyCapability[]at least one entryall three capabilities enabled

ArcyCapability is "chat" \| "autopilot" \| "copilot".

tsx
const customization: ArcyCustomization = {
  modes: {
    enabled: ["chat", "copilot", "autopilot"],
  },
}

Limits

customization.limits (ArcyLimitsConfig) sets per-mode and account-wide usage ceilings. Each limit is a { max, windowMs } pair (ArcyLimitConfig): max requests (or credits) per rolling windowMs milliseconds.

FieldTypeNotesDefault
copilot{ max: number; windowMs: number }Per-user Copilot mode limitunlimited
autopilot{ max: number; windowMs: number }Per-user Autopilot mode limitunlimited
maxCreditsPerUser{ max: number; windowMs: number }Global per-user credit ceiling across all modes combinedunlimited
tsx
const customization: ArcyCustomization = {
  limits: {
    copilot: { max: 10, windowMs: 86400000 },
    autopilot: { max: 5, windowMs: 86400000 },
    maxCreditsPerUser: { max: 500, windowMs: 2592000000 }, // 500 credits per 30 days
  },
}

ArcyLimitsConfig has no chat field. Chat shares maxCreditsPerUser (the account-wide credit ceiling across all capabilities) rather than getting its own per-mode limit like copilot and autopilot do.

windowMs is a duration in milliseconds. Common values:

DurationValue
1 hour3600000
24 hours86400000
7 days604800000
30 days2592000000

ArcyCustomization type reference

ts
interface ArcyThemeConfig {
  logo?: string | { src: string }
  logoWidth?: number   // px, applies at both render sites; unset falls back to that site's default
  logoHeight?: number  // px, applies at both render sites; unset falls back to that site's default
  primaryColor?: string
  highlightColor?: string
  borderWidth?: number      // px, 0-8
  borderColor?: string      // hex
  borderRadius?: number     // px, 0-32
  backgroundColor?: string  // hex, composes with glassiness
  glassiness?: number       // 0-100
  textColor?: string        // hex
  scrollbarThumbColor?: string       // hex
  scrollbarThumbHoverColor?: string  // hex, Chromium/Safari only
  scrollbarTrackColor?: string       // hex
  scrollbarWidth?: number            // px, 2-16
  fontFamily?: string
  zIndex?: number
  containerRef?: React.RefObject<HTMLElement | null>
  draggable?: boolean
  initialPosition?:
    | "bottom-right"
    | "bottom-left"
    | "top-right"
    | "top-left"
    | "bottom-center"
    | "top-center"
    | "left-center"
    | "right-center"
  buttonSize?: number  // px, 40-120, defaults to 76
}

interface ArcyElementOverride {
  className?: string
  style?: React.CSSProperties
}

type ArcyElementKey =
  | "launcherButton"
  | "launcherWrapper"
  | "panelContainer"
  | "panelHeader"
  | "panelCloseButton"
  | "chatInput"
  | "sendButton"
  | "messageBubbleUser"
  | "messageBubbleAssistant"
  | "historyList"
  | "interventionPrompt"
  | "coachmark"
  | "toast"

type ArcyElementsConfig = Partial<Record<ArcyElementKey, ArcyElementOverride>>

interface ArcyContentConfig {
  greetingMessage?: string
  assistantName?: string     // defaults to "ARCY"
  chatPlaceholder?: string   // defaults to "Ask {assistantName} anything..."
}

type ArcyCapability = "chat" | "autopilot" | "copilot"

interface ArcyModesConfig {
  enabled?: ArcyCapability[]
}

interface ArcyLimitConfig {
  max: number
  windowMs: number  // duration in milliseconds
}

interface ArcyLimitsConfig {
  autopilot?: ArcyLimitConfig
  copilot?: ArcyLimitConfig
  maxCreditsPerUser?: ArcyLimitConfig
}

interface ArcyCustomization {
  theme?: ArcyThemeConfig
  elements?: ArcyElementsConfig
  content?: ArcyContentConfig
  modes?: ArcyModesConfig
  limits?: ArcyLimitsConfig
}

FAQ

On this page