JSX in Kustomer

Learn how to use JSX components and helpers available in Kustomer to create advanced custom Klass Views.

Klass Views (or KViews) provide a simple way to extend the Kustomer interface without the need for developers to host any code. Klass Views use JSX-based templates to allow developers an to define lightweight UIs easily. This page covers the JSX components and helpers available in Kustomer.

What is JSX?

JSX is a templating language first built as a part of the React JavaScript Framework. If you are completely new to JSX, we recommend that you start with the React JSX docs to learn JSX syntax.

🚧

Important

JSX is neither a subset nor a superset of HTML. Even though the syntax may look similar to HTML syntax, there are many meaningful differences between them. For example, to add a div with a class in JSX, the syntax is <div className="test-class">Test</div>. In HTML, the syntax would be <div class="test-class">Test</div>.

Because the syntax for JSX is different from HTML syntax, make sure to modify any HTML into the appropriate JSX syntax.

Kustomer JSX components

Kustomer provides a number of components that can be used in JSX contexts. You can find the relevant JSX components below grouped based on expected application.

📘

Note

All code samples are in JSX format unless otherwise noted.

Layout JSX components

Use Layout components to add structure to a Klass View.

Segment

Use the Segment component to separate and to pad content.

<Segment style={{ border: '1px solid #6f5499' }}>
    <div style={{ background: '#6f5499', color: '#FFF' }}>
        Padded Content goes here
    </div>
</Segment>
PropValues
stylestyle object

Grid

Use the Grid component to wrap rows of Columns. Each row consists of columns adding up to 16.

<Grid>
    // Columns
</Grid>
PropValues
stylestyle object

Column

Use the Column component (must be a child of Grid) to set the width of a column inside a Grid. Column values are 1-16. All columns in a Grid must add up to 16.

<Column
    size="eight"
    style={{ border: "1px solid red", padding: '5px' }}
>
    Column A
</Column>
<Column
    size="eight"
    style={{ border: "1px solid blue", padding: '5px' }}
>
    Column B
</Column>
PropValues
sizeone, two, three, four, ... , fifteen, sixteen
stylestyle object

Container

The Container wraps its children with standard padding. In order to match our visual styles, most KViews should wrap all of their content in a single <Container usePadding>. Any KView that does not do this will be responsible for defining all of its own padding; if it defines none, its content will extend all the way to the edge of the KView.

<Container usePadding>
  All other KView content goes in here.
</Container>

Prop

Values
usePaddingtrue, false

Expanded Timeline components

Use Expanded Timeline components for Klass Views in the timeline.

BasicField

Use the BasicField component to render a field in the expanded timeline with a label and value. The component ignores all metadata for the field and renders the passed label and value.

<BasicField
    label="Order Number Basic"
    value={object.custom.orderNumberStr}
/>
PropValues
labelthe hardcoded label of the field
valuethe label of the field

Field

Use the Field component to render a field in the expanded timeline with a label and a value. This component is associated with the metadata of the object. From the object metadata, the component uses the displayName for the label (if set) and renders the correct type (for example, dates).

<Field
    field="orderNumberStr"
    key="orderNumberStr"
    type="kobject.order"
    value={object.custom.orderNumberStr}
/>
PropValues
fieldname of the custom field
keyunique identifier of the field (usually same as the field)
typethe type of the object the field is for (used for mapping to metadata)
valuethe value of the field

CustomerField

Use the CustomerField component to render a label and a Customer relationship card in the expanded timeline.

<Customer label="Rider" id={object.custom.riderRel} />
PropValues
idid of Customer object
labellabel for the field

ConversationField

Use the ConversationField component to render a label and a Conversation relationship card in the expanded timeline.

<ConversationField label="Convo" id={object.custom.convoRel} />
PropValues
idid of Conversation object
labellabel for the field

KobjectField

Use the KobjectField component to render a label and a KObject relationship card in the expanded timeline.

<KobjectField label="Car" type="car" id={object.custom.carRel} />
PropValues
idid of KObject
typetype of kObject
labellabel for field

Images

<Images
    style={{ border: "1px solid red", padding: '5px' }}
    images={
        [
            "https://placehold.it/400/ffffff/000000",
            "https://placehold.it/400/000000/ffffff"
        ]
    }
/>
PropValues
imagesarray of image URL strings
stylestyle object

Smartbar Detail components

Use Smartbar Detail components to extend the details panel at the top right of the Kustomer UI.

BasicRow

Use the BasicRow component to render a row in the smartbar details with a label and a value. This component ignores all metadata for the field and renders the passed label and value.

<BasicRow
    label="Name"
    value={object.custom.nameStr}
/>
PropValues
labelthe hardcoded label of the field
valuethe value of the field

Row

Use the Row component to render a row in the smartbar details with a label and avalue. This component is associated with the metadata of the object. From the object metadata, the component uses the displayName for the label (if set) and renders the correct type (for example, dates).

Additionally, you can edit row components inline (where supported).

<Row
    field={'name'}
    value={object.name}
    object="customer"
/>
PropValues
fieldthe metadata property name of the field
objectthe type of the object: customer, conversation, kobject
valuethe value of the field

Row Assignment User

Use the RowAssignmentUser to render a user assignment picker.

<RowAssignmentUser />

Row Assignment Team

Use the RowAssignmentTeam component to render a team assignment picker.

<RowAssignmentTeam />

Row Priority

Use the RowPriority to render a priority picker.

<RowPriority />

Relationship components

Use Relationship components to render relationships in the timeline.

Customer

Use the Customer component to render a customer relationship card for the given customer ID.

<Customer id={object.custom.riderRel} />
PropValues
idId of customer

Conversation

Use the Conversation component to render a conversation relationship card for the given conversation ID.

<Conversation id={object.custom.riderRel} />
PropValues
idid of conversation

Kobject

Use the Kobject component to render a KObject relationship card for the given KObject type and ID.

<Kobject type="car" id={object.custom.carRel} />
PropValues
idid of Kobject
typetype of Kobject

Button components

Use Button components to define different types of Buttons that can trigger actions from the UI.

Button

Use the Button component to render the Kustomer primary button style with the ability to pass a size and to pass either an onClick handler or a link.

<Button 
  size="small"
  onClick={() => { alert('clicked') }}
>
  Button Text
</Button>

<Button 
  size="small"
  link="https://www.kustomer.com/"
>
  Button Link Text
</Button>
PropValues
sizexsmall, small, medium
onClickJavaScript executed when button clicked
linkURL to open on click

ButtonSecondary

Use the ButtonSecondary component to render the Kustomer secondary button style with the ability to pass a size and to pass either an onClick handler or a link.

<ButtonSecondary 
  size="small"
  onClick={() => { alert('clicked') }}
>
  Button Text
</ButtonSecondary>

<ButtonSecondary
  size="small"
  link="https://www.kustomer.com/"
>
  Button Link Text
</ButtonSecondary>
PropValues
sizexsmall, small, medium
onClickJavaScript executed when button clicked
linkURL to open on click

ButtonDanger

Use the ButtonDanger component to render the Kustomer danger button style with the ability to pass a size and to pass either an onClick handler or a link.

<ButtonDanger 
  size="small"
  onClick={() => { alert('clicked') }}
>
  Button Text
</ButtonDanger>

<ButtonDanger
  size="small"
  link="https://www.kustomer.com/"
>
  Button Link Text
</ButtonDanger>
PropValues
sizexsmall, small, medium
onClickJavaScript executed when button clicked
linkURL to open on click

ButtonGroup

Use the ButtonGroup component to wrap two or more Button components to give the same spacing between buttons used in Kustomer.

<ButtonGroup>
  <Button onClick={() => {}}>
    Save
  </Button>
  <ButtonSecondary onClick={() => {}}>
    Cancel
  </Button>
</ButtonGroup>

CardJS components

Use CardJS components with iFrames and the Kustomer CardJS library. These components allow you to extend KViews to create more powerful UI experiences. With CardJS components you can embed content formed from multiple frontend files or embed content that contains a backend server experience. Developers using these CardJS components are responsible for hosting the code referenced in the iFrame.

Card

Use the Card component to render an iFrame to the provided source with a static size. The default modal size is 300x500. After loading the modal, you can use modalHeight and modalWidth as optional properties to set the modal dimensions from within the iFrame.

<Card
  src="https://your.url"
  modalHeight={300}
  modalWidth={500}
  context={context}
/>
PropValues
srcURL for page to be embedded
modalHeightthe static height of the frame (optional)
modalWidththe static width of the frame (optional)
contextthe context to pass to CardJS
heightthe height of the card, optionally including a unit (px, em, etc.)

DynamicCard

Use the DynamicCard component to render an iFrame to the provided source. The component resizes to fit content when used with CardJS Kustomer.resize(). The default modal size is 300x500. After loading the modal, you can use Kustomer.modal.resize({ height: 1000, width: 1000 }) with optional object that contains a height and a width to resize your modal.

<DynamicCard
  src="https://your.url"
  context={context}
/>
PropValues
srcURL for page to be embedded
contextthe context to pass to CardJS

Data components

Use Data components to insert additional information into the JSX

WithUser

Use the WithUser component to provide a user to children as a render prop. Render props are a React concept in which a component takes a function to render and passes the function a set of values.

<WithUser id="your-user-id">
 {user => <div>{user.displayName}</div>}
</WithUser>
PropValues
idID of a user in your org

Helper functions

In addition to components, Kustomer also provides two groups of helper functions in JSX contexts: a subset of lodash methods and moment.js.

lodash

You can use any lodash methods for JSX code in Kustomer. Some of the more popular ones are:

Example:

<div>{_.get(context, 'custom.accountOwner')}</div>

moment

You can use moment.js for Klass Views to format dates. Example code to show a "relative time" created date, such as "5 minutes ago":

<div> {moment(conversation.createdAt).fromNow()} </div>

KustomerRequest

You can use KustomerRequest to make requests to the Kustomer API. This helper is similar to Kustomer.request in the Cards SDK. You can optionally add a callback to return a string used for a notification displayed to users after the request is made.

Example code to show a button that creates a Customer:

<Button onClick={async () => {
   await KustomerRequest({ 
      url: '/v1/customers', 
      method: 'POST', 
      body: { name: 'Foo bar' } }, 
      (err, response) => {
        if(err) {
          return 'Failed to create Customer';
        }
        return `Successfully created Customer ${response.name}`;
      });
}}>
    Create Customer
</Button>

runCommand

You can use runCommand to run commands that your app has defined.

Data Masking Compatibility

If your organization uses Kustomer's Data Masking feature, agents may be unable to see the contents of some fields if those fields are masked. This masking happens all over the application, including inside Klass Views. When you use JSX to construct your own Klass Views, you may need to handle masked data yourself.

Whenever a field is masked, the code will see an object with this shape instead of the normal value:

{ isMasked: true, maskedPresentation: "********" }

The isMasked boolean lets you know that the field contents are masked, and the maskedPresentation string gives a display representation of that field. Depending on your data masking settings, maskedPresentation might show a partially masked representation of the underlying data, or it might be all asterisks.

When you use our JSX field components and pass a masked value to their value, they will automatically react to masked values, disabling editing and showing the maskedPresentation. But if you provide your own logic, you may need to check for masked values yourself.

For instance, if you have a Klass View that uses the customer email to build a link to an internal integration:

<div>
  <Button
    link={`https://my-internal-integration.example.com/customer?email=${encodeURIComponent(customer?.emails?.[0]?.email)}`}
  >
    Look up customer on internal system
  </Button>
</div>

And you later choose to mask the customer email address field, you would have to change that button to either disable or hide if the email address is masked:

<div>
  {(() => {
    const email = customer?.emails?.[0]?.email;
    if (email?.isMasked) {
      return (
        <Button disabled>
          Can't access internal integration for {email.maskedPresentation}
        </Button>
      );
    } else {
      return (
        <Button
          link={`https://my-internal-integration.example.com/customer?email=${encodeURIComponent(email)}`}
        >
          Look up customer on internal system
        </Button>
      );
    }
  })()}
</div>

On the other hand, if you just use a <Field /> and pass it a masked value:

<Row 
  field={"externalId"}
  value={_.get(customer, 'externalId')}
  object="customer" 
/>

Then you don't need to make any changes to that Klass View. In this example, if customer externalID is masked, the Klass View will show a masked representation and disable editing that field.

Conditional Attributes and Conditionally Required Attributes

You can show or hide all custom and most standard attributes for all standard and custom klasses based on certain criteria, such as the existence of the value of an attribute, or when the value is updated to one you've specified.

Conditional Attributes

To set an attribute as conditional, add both the dependent and controlling attribute as components in your code. For example, if you wanted to create a certain set of conditions to only show the custom attribute returnReasonStr if contactReasonStr is "return," the code for that would look like:

<CollapsedRows>
  <Row
    field={"contactReasonStr"}
    value={_.get(conversation, 'custom.contactReasonStr')}
    object="conversation"
  />
  <Row
    field={"returnReasonStr"}
    value={_.get(conversation, 'custom.returnReasonStr')}
    object="conversation"
  />
</CollapsedRows>

Then, in the Conditions tab, you could add conditional rendering for the returnReasonStr row:

{
  "conversation_custom_returnReasonStr": {
    "condition": {
      "op": "or",
      "values": [
        {
          "op": "eq",
          "property": "conversation_custom_contactReasonStr",
          "values": [
            "return"
          ]
        }
      ]
    }
  }
}

Let's break this down line by line. First, we have the conversation_custom_returnReasonStr key, a custom attribute on the conversation klass. This dependent attribute points to an object that contains conditions that will cause the dependent attribute to be shown or hidden. For a standard attribute, we'd simply remove the custom word from the key (e.g. conversation_channels). Next, we have a condition option that contains two keys:

  • op: Refers to "operator" and is a static value.
  • values: This is an array of condition objects. Each object denotes to a separate condition. For example, if you wanted to have multiple conditions that would cause returnReasonStr to be displayed, you could include multiple objects. Building on top of the former example, this would look like:
{
  "conversation_custom_returnReasonStr": {
    "condition": {
      "op": "or",
      "values": [
        {
          "op": "eq",
          "property": "conversation_custom_contactReasonStr",
          "values": [
            "return"
          ]
        },
        {
          "op": "eq",
          "property": "conversation_custom_contactReasonStr",
          "values": [
            "exchange"
          ]
        }
      ]
    }
  }
}

This means the returnReasonStr row would be displayed if contactReasonStr is either "return" or "exchange."

In each condition object, there are three keys:

  • op: Refers to "operator." Can either "eq" (equals), "exists", or "startsWith".
  • property: The controlling attribute.
  • values:
    • When the op is "eq", this array refers to the possible values the attribute could be equal to in order for the condition to be satisfied.
    • When the op is "exists", this array should be empty.
    • When the op is "startsWith", this array refers to the possible values that the attribute could start with for the condition to be satisifed. This is supported for multi-level-lists (where it's called "is within" in the UI), and string values.

Conditionally Required Attributes

On top of conditionally rendering components, you can make any custom attribute conditionally required. This is different from globally required attributes that are required to mark any conversation as done -- instead, if the conditions for a given attribute have been met, the dependent attribute will then be required for a conversation to be marked as done. To configure this, you would add dependentAttributeRequired: true to the condition object:

{
  "conversation_custom_returnReasonStr": {
    "condition": {
      "op": "or",
      "values": [
        {
          "op": "eq",
          "property": "conversation_custom_contactReasonStr",
          "values": [
            "return"
          ],
          "dependentFieldRequired": true
        }
      ]
    }
  }
}

Now, returnReasonStr will be required, but only when the value of returnReasonStr is set to "return". In the event that a conditionally required attribute has not been filled out, and an agent marks a conversation as done, the required fields modal will launch, prompting the agent to input a value for that field.

Self-Pointing Conditional Attributes

You could also create self-pointing conditional attributes, where the presence of a given component relies either on the existence of a value, or a value you specify. Example:

{
  "conversation_brand": {
    "condition": {
      "op": "or",
      "values": [
        {
          "op": "eq",
          "property": "conversation_brand",
          "values": [
            "abc"
          ]
        }
      ]
    }
  }
}

In this case, the conversation brand row would only be displayed if the brand value for a conversation was "abc."