Skip to main content
For most use cases, the built-in widget customization options in Widget settings are sufficient. But if you need more visual control, you can customize the widget’s appearance with your own CSS or by passing additional configuration options in the chat.load() function.

Using built-in styling options

You can control the widget’s appearance directly in your chat.load() configuration using the assistant object. Here’s an example with the available styling parameters:
window.voiceflow.chat.load({
  verify: { projectID: 'YOUR_PROJECT_ID' },
  url: 'https://general-runtime.voiceflow.com',
  versionID: 'production',
  assistant: {
    title: 'Support Chat',
    description: 'How can I help you today?',
    image: 'https://example.com/avatar.png',
    color: '#3D82E2',
    launcher: 'https://example.com/launcher-icon.png',
    stylesheet: 'https://example.com/custom-styles.css'
  }
});

Visual & Behavioral Overrides

These override the remote publishing configuration fetched from the Voiceflow API. All fields are optional but local overrides take priority over remote settings.

Widget Type

PropertyTypeDescription
type'chat' or 'voice'Widget type
renderMode'widget' or 'popover'Chat render mode
PropertyTypeDescription
header.hideImagebooleanHide the header image
header.imageUrlstringCustom header image URL
header.titlestringCustom header title

Welcome Banner

PropertyTypeDescription
banner.hidebooleanHide the welcome banner
banner.titlestringBanner title
banner.descriptionstringBanner description
banner.imageUrlstringBanner image URL

Agent Avatar

PropertyTypeDescription
avatar.hidebooleanHide the agent avatar
avatar.imageUrlstringCustom avatar image URL

Input

PropertyTypeDescription
inputPlaceholderstringInput field placeholder text

Launcher

PropertyTypeDescription
launcher.type'icon' or 'label'Launcher button style
launcher.labelstringText label for the launcher
launcher.imageUrlstringCustom launcher image URL
PropertyTypeDescription
footer.hidebooleanHide the footer link
footer.linkTextstringFooter link text
footer.linkUrlstringFooter link URL

Appearance

PropertyTypeDescription
colorstringPrimary brand color (see Color section below)
palettePaletteFull color palette override (see Palette section below)
fontFamilystringFont family name (use 'inherit' for page font)

Position

PropertyTypeDescription
side'left' or 'right'Widget position side
spacing.sidestringDistance from the side edge (see Spacing section below)
spacing.bottomstringDistance from the bottom edge (see Spacing section below)

AI Disclaimer

PropertyTypeDescription
aiDisclaimer.hidebooleanHide the AI disclaimer
aiDisclaimer.textstringDisclaimer text

Behavior

PropertyTypeDescription
streamingDisabledbooleanDisable streaming message animation
persistence'localStorage' or 'memory'Session persistence strategy

External Additions

PropertyTypeDescription
stylesheetstringCustom CSS stylesheet URL
extensionsAnyExtension[]Custom extensions

Color, Palette, and Spacing

color

Type: string (any valid CSS color parseable by chroma-js) Default: #387dff The primary brand color used throughout the widget (buttons, links, active states, header accents). The value is parsed by chroma-js, which supports multiple formats:
FormatExampleNotes
Hex (6-digit)#387dffMost common, recommended
Hex (3-digit)#38fShorthand hex
Hex with alpha#387dff808-digit hex with alpha
Named CSS colortomato, cornflowerblueCSS named colors
RGBrgb(56, 125, 255)CSS rgb() function
HSLhsl(220, 100%, 61%)CSS hsl() function
When color is provided without a palette, the widget auto-generates a full 10-shade palette using HSL interpolation. The color value becomes shade 500 (the base), and lighter/darker shades are computed by varying the lightness. Examples:
// Default blue
assistant: { color: '#387dff' }

// Brand red
assistant: { color: '#dc2626' }

// Using a named color
assistant: { color: 'tomato' }

// Dark green
assistant: { color: 'hsl(150, 60%, 30%)' }

palette

Type: Object with 10 required shade keys (all hex strings) Default: Auto-generated from color via HSL interpolation A full color palette that gives you precise control over every shade the widget uses. If provided, this overrides the auto-generated palette entirely. All 10 shades are required.
KeyRoleTypical Usage
50LightestSubtle backgrounds, hover states
100Very lightLight backgrounds
200LightSecondary backgrounds, borders
300Medium-lightInactive/disabled states
400MediumSecondary text, icons
500Base (primary)Primary buttons, links, active states
600Medium-darkHover states on primary elements
700DarkActive/pressed states
800Very darkHigh-contrast text
900DarkestMaximum contrast
Example — custom blue palette:
assistant: {
  color: '#387dff',
  palette: {
    50:  '#e7f5fd',
    100: '#c6e4fb',
    200: '#a2d2fa',
    300: '#87bffb',
    400: '#659ffd',
    500: '#387dff',
    600: '#2f68db',
    700: '#264eb4',
    800: '#1c368e',
    900: '#0f1e61',
  },
}
Example — warm coral palette:
assistant: {
  color: '#f97316',
  palette: {
    50:  '#fff7ed',
    100: '#ffedd5',
    200: '#fed7aa',
    300: '#fdba74',
    400: '#fb923c',
    500: '#f97316',
    600: '#ea580c',
    700: '#c2410c',
    800: '#9a3412',
    900: '#7c2d12',
  },
}
How auto-generation works: When only color is provided, createPalette(color) extracts the hue (H) and saturation (S) from the color, then generates shades by varying lightness from 95% (shade 50) down to 5% (shade 900). The original color is used as-is for shade 500.
Tip: When providing both color and palette, make sure palette[500] matches color for consistency. If they differ, palette takes priority for rendering while color is effectively ignored.

spacing

Type: { side?: string, bottom?: string } Default: { side: '30', bottom: '30' } (30 pixels each) Controls the position of the widget relative to the viewport edges. The value is a numeric string representing pixels The px unit is appended automatically. Only effective in overlay mode.
  • spacing.side Distance from the left or right edge (determined by the side property)
  • spacing.bottom Distance from the bottom edge
Examples:
// Default positioning (30px from side and bottom)
assistant: {
  spacing: { side: '30', bottom: '30' },
}

// Tight to the corner
assistant: {
  side: 'right',
  spacing: { side: '10', bottom: '10' },
}

// Large offset from the left
assistant: {
  side: 'left',
  spacing: { side: '60', bottom: '40' },
}

// Only override bottom spacing
assistant: {
  spacing: { bottom: '80' },
}

Applying custom CSS

To go beyond the built-in options, you can write CSS rules that override the widget’s default styles. Web chat elements use class names prefixed with .vfrc-. For example, to change the background color of agent messages:
.vfrc-system-response .vfrc-message {
  background-color: #000000;
  color: #FFFFFF;
}

Supported CSS classes

Voiceflow provides a list of supported CSS classes that you can modify. While the widget has additional classes beyond those listed below, modifying them is at your own risk. Only the classes listed below are officially supported.
Class NameElement
vfrc-widgetRoot widget container
vfrc-chatChat window
vfrc-launcherLauncher button
vfrc-headerChat header
vfrc-footerChat footer
vfrc-messageIndividual message
vfrc-system-responseSystem/agent response
vfrc-user-responseUser message
vfrc-chat-inputInput text area
vfrc-buttonAction button
vfrc-cardCard component
vfrc-carouselCarousel container
vfrc-imageImage component
vfrc-avatarAgent avatar
vfrc-typing-indicatorTyping dots
vfrc-proactiveProactive message container
vfrc-proactive__cardIndividual proactive message
vfrc-proactive__close-buttonProactive close button
vfrc-promptQuick reply prompt
vfrc-feedbackFeedback buttons
/*
PAGE ELEMENTS
Customize the page that the chat bubble lives on
*/

/* .voiceflow-chat {} */
/* .vfrc-launcher {} */
/* .vfrc-chat--overlay {} */
/* .vfrc-prompt {} */

/*
CHAT WIDGET HEADER
Customize the header content, controls, and assistant information
*/

/* .vfrc-header {} */
/* .vfrc-assistant-info {} */
/* .vfrc-assistant-info--title {} */
/* .vfrc-assistant-info--description {} */
/* .vfrc-avatar {} */
/* .vfrc-icon {} */

/*
CHAT MESSAGE BODY
Customize the chat body layout and conversation metadata
*/

/* .vfrc-chat {} */
/* .vfrc-chat--dialog {} */
/* .vfrc-chat--spacer {} */
/* .vfrc-chat--session-time {} */
/* .vfrc-chat--status {} */
/* .vfrc-message {} */
/* .vfrc-timestamp {} */

/*
ASSISTANT RESPONSES
Customize assistant response message styles
*/

/* .vfrc-system-response {} */
/* .vfrc-system-response--indicator {} */
/* .vfrc-system-response--controls {} */
/* .vfrc-system-response--list {} */
/* .vfrc-system-response--actions {} */
/* .vfrc-system-response .vfrc-message {} */
/* .vfrc-system-response .vfrc-button {} */
/* .vfrc-button {} */
/* .vfrc-image {} */
/* .vfrc-image--background {} */
/* .vfrc-card--content {} */
/* .vfrc-card--header {} */
/* .vfrc-card--description {} */
/* .vfrc-carousel {} */
/* .vfrc-carousel--button {} */

/*
USER RESPONSES
Customize user response message styles
*/

/* .vfrc-user-response {} */
/* .vfrc-user-response .vfrc-message {} */

/*
FOOTER
Customize the message input field and branding
*/

/* .vf-footer {} */
/* .vfrc-input {} */
/* .vfrc-chat-input--button {} */
/* .vfrc-footer--watermark {} */

Loading your stylesheet

Once you’ve written your CSS, you can pass it to the widget in two ways. Option 1: Link a hosted stylesheet Host your CSS file and provide the URL in the assistant.stylesheet property:
window.voiceflow.chat.load({
  verify: { projectID: 'YOUR_PROJECT_ID' },
  url: 'https://general-runtime.voiceflow.com',
  versionID: 'production',
  assistant: {
    stylesheet: 'https://example.com/custom-styles.css'
  }
});
Option 2: Embed CSS as a data URL If you don’t want to host a separate file, encode your CSS as a base64 data URL and include it inline:
window.voiceflow.chat.load({
  verify: { projectID: 'YOUR_PROJECT_ID' },
  url: 'https://general-runtime.voiceflow.com',
  versionID: 'production',
  assistant: {
    stylesheet: 'data:text/css;base64,LnZmcmMtc3lzdGVtLXJlc3BvbnNlIC52ZnJjLW1lc3NhZ2UgeyBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwMDAwOyBjb2xvcjogI0ZGRkZGRjsgfQ=='
  }
});
You can use an online tool or run btoa() in your browser console to encode your CSS to base64.