Overview of Flex UI programmability options


The Flex Manager is the access point for controlling your Flex instance and all of the underlying Twilio products used for communications and assigning tasks. This means that within your Flex project, you can access the TaskRouter or Chat client directly through the Flex manager.

Aside from Flex itself, Manager also gives you access to the Conversations, Sync, Voice, and TaskRouter SDKs.

How to obtain the Manager instance

You can access the manager as follows:

In the init method of your plugin

init(flex, manager) {
  // use manager here

In your app, by calling the `getInstance` method


or by calling the `create` method when initializing Flex

return Flex
  .provideLoginInfo(configuration, "#container")
  .then(() => Flex.Manager.create(configuration))
  .then(manager => {
    // use manager here
  .catch(error => handleError(error));

You can check out the sample project on how to initialize Flex.

For the detailed description of Manager visit our Flex UI API docs


Flex UI's configuration allows you to control the way the overall app loads, as well as the behavior of individual Flex Components.

Flex Components can be modified in two ways:

  • via componentProps in the Configuration Object
  • via the defaultProps API

Using componentProps in the Configuration Object


var appConfig = {

  componentProps: {
    CRMContainer: {
      uriCallback: (task) => task
        ? `${}`
        : ""

React defaultProps API

You may also configure default properties for components within a Plugin by using the React defaultprops API programmatically:

componentProps: { [Component Name]: { [Prop Name]: [PropValue] } }


  .uriCallback = (task) => task
    ? `${}`
    : "";

  .logoUrl = "";

Go to Flex UI API docs for the full desciption of Configuration objectConfiguration object and Component props.

Overriding language strings

You can override any string by editing the strings object on the Flex Manager instance:

flex.Manager.getInstance().strings.TaskLineCallAssigned = "I am a content string!";

5. Task list - String customization_slice.png

For the full list of UI strings, see Flex UI API docs

Templating support with Mustache-style syntax

Flex uses Handlebars for templating and supports Mustache-style syntax and expressions within content strings, ie. embedding value placeholders between double braces. Here’s an example:

manager.strings.TaskInfoPanelContent = `
<h2>Task type</h2>
<h2>Task created on</h2>
<h2>Task priority</h2>
<h2>Task queue</h2>
<hr />
<h2>Customer name / phone number</h2>

Within the context of each component and string, additional dynamic content is available: for example, by accessing Task properties or attributes.

Accessing Task context: properties and attributes

Within the context of each component and string, additional dynamic content is available: for example, by accessing Task properties or attributes.

Here’s an example of the use in a template of a couple of the properties and attributes listed above:

manager.strings.TaskExtraInfo = "My task {{}} was created on {{task.dateCreated}}";

Helper functions

Helper functions provide you with a way to customize text with dynamic information. Here is an example of the use of a helper function to enable dynamic updates:

manager.strings.TaskExtraInfo = "Time since last update: {{helper.durationSinceUpdate}}";

You can find all available helper functions in Flex UI API docs.

Theming Flex UI

Flex UI theming interface closely maps to the Twilio Paste design system. This structure is based on the concept of design tokens, a set of variables that you can modify. This structure promotes both consistency, customization and web accessibility.

Theme config

Theme configuration is done with config key called config.theme with the following structure

interface ThemeConfigProps {
   isLight?: boolean; // Represents if light or dark theme
   tokens?: Tokens; // Paste tokens
   componentThemeOverrides?: Object; // Object containing styles of the component which is to be overridden.

Component theming

For components which receive the theme as props, props.theme.tokens can be used. For more on each token information, please refer to Twilio Paste

Custom tokens

Use custom tokens to pass your own custom tokens to theme using the example below

  appconfig = {
    theme: {
      tokens: {
        custom: {
          myCustomToken: "#ccc"


An API Flex.setProviders() can be used to load providers on the root level once and don’t have to worry about wrapping again as the context will correctly set. Now to use Paste in Flex plugins, developers will not need to wrap Paste ThemeProvider over all its components. Eexplore the API in our Flex UI API docs.

When using Paste, developers need to pass the CustomizationProvider from Paste to Flex using Flex.setProviders api as below. By doing this, we’ll wrap Flex with the passed Provider and developers don’t have to worry about fetching the right theme and passing it on.

import { CustomizationProvider } from "@twilio-paste/core/customization";
   PasteThemeProvider: CustomizationProvider
   <PasteButton key="A" variant="primary">
       Paste Button
   <PasteButton key="B" variant="primary">
       Paste Button

Flex.setProviders() also allows developers to pass their own custom provider which is needed to be set on the root level. They can do it as below:

   CustomProvider: (RootComponent) => (props) => {
       return (
           <Context.Provider value={{ test: "123" }}>
               <RootComponent {...props} />
       {(value) => {
           <div>{}</div> // Will print 123

The below example shows how to use a custom provider for styling Material UI components

      CustomProvider: (RootComponent) => (props) => {
          return (
            <StylesProvider generateClassName={createGenerateClassName({
              productionPrefix: 'pluginXYZ',
                  <RootComponent {...props} />

Flex.AgentDesktopView.Panel1.Content.add(<StyledSampleComponent  key="no1"  />);

Using Twilio Paste

Not every component you build needs to start from scratch. Existing React component libraries can help you use components that have already been built with browser compatibility, responsive screen sizes, and accessibility in mind. Internally, flex-ui leverages Twilio Paste for many of its components. You can similarly use Paste to create components that start with a similar style to Flex's existing layout.

Although Paste is already a dependency of flex-ui, we recommend including it as an explicit dependency in your plugin project's package.json. You would add this within your dependencies:

// package.json

"dependencies": {
    "@twilio-paste/core": "^10.20.0",

Add / replace / remove components

Flex UI is a library of programmable or dynamic components that expose a Content property and allows you to add, replace and remove any component and its children. Each immediate child of a component has a key (required for the add and replace methods) and a set of properties that will be inherited by its children. To see the full list of components, find a component's key or explore component props, go to Flex UI API docs.

Content property and add / replace / remove methods

Below you can find a list of UI components and their details, that can be overridden programmatically. This can be done by using add, replace and remove methods with options. Note, All components must have a key declared.

Syntax for add/replace methods

Flex.Component.Content.add(<MyComponent key="demo-component"/>, {options});
Flex.Component.Content.replace(<MyComponent key="demo-component"/>, {options});

Below is an example of adding a component called AnotherMuteButton to the MainHeader component, aligned to the end (here left) and placed as the first component in front of native components MuteButton and UserControls:

Flex.MainHeader.Content.add(<AnotherMuteButton key="mute"/>, {
  sortOrder: -1, 
  align: “end”

Syntax for remove method

Remove method allows you to remove immediate children of a programmable component by their key.

Flex.Component.Content.remove(key, {options});

Below is an example of removing the AnotherMuteButton component from the MainHeader component. This component is removed by the key we set above, "mute".


Options for add/replace/remove methods

if (Expression)

Used in both add and replace methods to add conditions on which component is added or replaced. Example:

Flex.MainHeader.Content.add(<AnotherMuteButton key="mute"/>, {
  if : props => props.task.source.taskChannelUniqueName === "custom1" 

sortOrder (number)

Used in add method to specify the order in which the new component is placed in relation to other children of the parent component. Native children components take priority. Sort order counter starts with 0. To always place a new component at the very start or end of the order, use large numbers like -100 or 100, depending on the direction. Example:

Flex.MainHeader.Content.add(<AnotherMuteButton key="mute"/>, {
  sortOrder: -1

align ('start' | 'end')

Used in the add method to align new components either to the top/bottom or right/left, depending on the direction of the parent component. Possible values are:

  • start - Aligns new component on the left or top
  • end - aligns new component on the right or bottom


Flex.MainHeader.Content.add(<AnotherMuteButton key="mute"/>, {
  align: “end”

UI actions and Flex Events

The Flex UI is constantly emitting event data that describes how the user is interacting with the Flex UI. As you write Plugins, the Actions Framework allows you to harness these events and define your own logic to describe how you want the Flex UI, CRM Data, or any other data, to change. You can register events before or after an action fires, or even replace the behavior of an Action.

Register and Invoke an Action

Actions.registerAction(name: string, action: ActionFunction, payloadUpdate?: PayloadUpdateFunction)

Registers a named action and provides the code that should be run when invoking it. The payloadUpdate method is optional, and used for triggering a callback to modify the payload given to action invocation.

Actions.invokeAction(name: string)

Invokes a named action. Returns a Promise.

Actions.addListener(name: string, action: ActionFunction)

Implements a custom function to be triggered either Before or After a specific Action.

Add and Remove Event Listeners

You can add and remove event listeners.

Events supported:

before[eventName] (for example "beforeAcceptTask")
Called when a named action is triggered, but before the action body is run. You can abort the action that is provided with event body.

Called after the action has stopped executing.

Note, the afterLogout event is not supported. Once the Worker has logged out, they will be returned to the Flex login screen.

Replace an Action

Actions.replaceAction(name: string, action: ReplacedActionFunction)

Replaces the default implementation of a named action and provides an alternative implementation. The replaced action will be called with the same parameters as the original implementation, so you can add additional logic, then invoke the original action in your replacement function.

In-app and browser notifications

Flex UI provides a client-side API to manage notifications in Flex UI.

What is a notification in Flex?

A notification is an alert that tells the user what state change or error has occurred to a component or page when they are no longer viewing that component or page

Users can be notified in Flex using a Notification Bar or Browser notifications or both.

What are notification framework capabilities?

  • Register custom notifications including browser notifications
  • Customize standard notifications
  • Turn off standard UI notifications
  • Override standard notifications per Task Channel Definition
  • Customize how standard UI notifications are displayed
  • Register your custom UI notifications and specify how to render them


NotificationBar is an in-app way to alert user. NotificationBar has a variety of options like icon, actions, timeout.

Screenshot 2022-05-26 at 12.52.12.png

Screenshot 2022-05-26 at 12.52.12.png

Browser Notifications

Flex uses the standard Browser Notification API as the basis for its browser notifications implementation. Browser notifications can be enabled in the Admin Dashboard of the Flex UI.

Browser notifications are shown if Flex is minimized.

Note, due to security constraints across browsers, Browser Notifications are not supported when Flex is iframed within a cross-domain webpage. This includes the Salesforce and Zendesk integrations.

Requesting permissions

To start showing browser notifications, the user needs to first grant permissions. By default, Flex will request user for permissions if they are not granted or blocked.

If you want to add custom logic around requesting permissions, like request it based on some user action in the UI, then you can dispatch Notification.requestPermission() from your custom code.

Browser notifications handler

To display a browser notification, use the options key with a browser tag in it. Flex API docs contain an exhaustive list of available properties. If no browser key is passed to options, Flex will not show any browser notifications.

NotificationBar actions

A helper component NotificationBar.Action, that can be used for providing clickable action to notification definition.

interface NotificationBarActionProps {
    label: React.ReactText; // Can be simple text string or a template string
    onClick: NotificationBarActionCallback;
    icon?: string;

The full reference for the Notification Manager and a list of standard notifications are available in Flex API docs.

State Management

Flex UI 2.0 includes the Redux Toolkit and some new APIs for managing your internal state. These tools provide some guardrails for your state management, helping you set up boilerplate code more easily and with better defaults.

We recommend using single Redux store, either let Flex UI create its own store or pass a custom store using Manager.create() API


A wrapper around Redux's useSelector method. It exposes the same API but adds some Flex validations to ensure Flex's internal state is usable. This selector is specific for working with Flex state itself. Outside of accessing Flex state, we recommend using the default useSelector.

const MyComponent = (props) => {
   const viewState = useFlexSelector(state => state.view);
   return (
       {viewState.isSideNavOpen &&
           <div>My Custom Code</div>

The selector takes the current view state for the custom component. The selector guarantees that the state being selected is safe to read and can be used in the application without side effects. This couldn’t be guaranteed with useSelector.


A wrapper around Redux's useDispatch method. It prevents dispatches from causing side effects to Flex's state, ensuring your changes work as expected. Use this hook for interacting with Flex's state. You can use the native useDispatch method outside of Flex’s state.

const MyComponent = (props) => {
   const viewState = useFlexSelector(state => state.view);
   const dispatch = useFlexDispatch();
   return (
       {viewState.isSideNavOpen &&
               <button onClick={() => dispatch({type: 'close_side_bar'})}>My Custom Code</button>

Learn more about using Redux in our developer guides

Task Channel Definitions

Flex is a multichannel contact center. We support a number of channels out-of-the-box, and are constantly adding more. As of version 1.0 we support the following native channels:

  • Voice
  • SMS with MMS support
  • WebChat with Media Attachments
  • WhatsApp

With the Task Channel Definition API you can also add custom channels and override the behavior of existing ones.

Task Channel Definition API

All task channels that Flex UI handles are defined and registered with Task Channels API. Flex registers its default Task Channel definitions, but users and plugins can register their own. When task-based components are rendered, the first matching channel definition for a given task will be used. If there is more than one channel definition match for a task, then the most recently registered definition will be used. This allows you to register a more specific channel definition to override the behavior of a general one.

In a task channel definition you can specify:

  • callback to determine which tasks are applicable
  • strings (templates) to use in different Flex components based on task status
  • colors to be used in task list based on task status
  • icons to be shown in task list, tabs, and canvases based on task status
  • custom components to be added to task based components if channel is applicable
  • custom components to be replaced in task based components if channel is applicable

Learn more about working with Task Channel Definitions in the developer guides.


The insightsClient provide access to the Twilio Sync SDK. For Flex accounts, this gives access to workers and tasks data through the use of two classes:

  • LiveQuery class: to query Flex data and receives pushed updates whenever new (or updated) records would match the given expression
  • InstantQuery class: to get a static snapshot of Flex data

Both classes needs two arguments:

  • Index name: data set the query is executed against. Currently supported index names for Flex are: tr-task, tr-worker, tr-reservation, tr-queue.
  • Query: this is the query used to filter the data from the index. The syntax for the query is documented here. The query can be an empty string: in this case the whole data set is returned (e.g. all workers.)

LiveQuery example

In this example, the insightsClient is used to query the workers with activity_name set to Available and subscribe to changes. That means that every time a worker change its status to Available, an event itemUpdated is fired. If a worker changes its status from Available to any other status, the itemRemoved event is fired.

  .liveQuery('tr-worker', 'data.activity_name == "Available"')
  .then(function (args) {
      'Subscribed to live data updates for worker in "Available" activity'
    args.on('itemRemoved', function (args) {
      console.log('Worker ' + args.key + ' is no longer "Available"');
    args.on('itemUpdated', function (args) {
      console.log('Worker ' + args.key + ' is now "Available"');
  .catch(function (err) {
    console.log('Error when subscribing to live updates', err);

InstantQuery example

In this example, the insightsClient is used to query the workers with specific skills inside its attributes. This returns an array of workers that can be used to provide static data.

manager.insightsClient.instantQuery('tr-worker').then((q) => {
  q.on('searchResult', (items) => {
    // Do something with the results
  });'data.attributes.languages contains "english"');
Rate this page:

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd by visiting Twilio's Stack Overflow Collective or browsing the Twilio tag on Stack Overflow.

Thank you for your feedback!

Please select the reason(s) for your feedback. The additional information you provide helps us improve our documentation:

Sending your feedback...
🎉 Thank you for your feedback!
Something went wrong. Please try again.

Thanks for your feedback!

Refer us and get $10 in 3 simple steps!

Step 1

Get link

Get a free personal referral link here

Step 2

Give $10

Your user signs up and upgrade using link

Step 3

Get $10

1,250 free SMSes
OR 1,000 free voice mins
OR 12,000 chats
OR more