KView components
Every component and helper Kustomer gives KView templates, with live previews, a map of where each one fits, a playground, and the props for each.
KView templates build their UI from components Kustomer provides. They match the agent desktop, follow the agent's light or dark theme, and handle masked data for you. This page shows where each one fits, lets you try them, and lists every prop.
Components and helpers are global in a template: write <Field … /> or _.get(…) without importing anything.
Where components go
Each KView renders in one context, and some components are built for a specific one: Row edits attributes in the insights panel, Field lays out a KObject's body in the timeline, and CreateFormRow belongs in create forms.
The body of a KObject when an agent expands it in the timeline.
Browse the components
Pick a component to see it render, change its props, and copy the template.
Try it
Write a template and see it render in any context. The Data tab holds the variables your template reads, as in the Data tab of Kustomer's KView editor; edit them to test missing or unusual values. Turn on Mask to see what an agent who can't view the customer's email and phone gets, and switch the preview to dark mode to check your colors.
template of your app's KView.The playground follows Kustomer's rules: context variables are global, the template's last JSX expression is what renders, and syntax errors show the line and column. API calls, commands, links and edits are logged instead of sent, and relationship cards only find the records in the sample data.
Show rows only when they apply
In the insights panel and in create forms, a row can depend on other attributes: show Return reason only when the conversation's Contact reason is return, for example. Set this in the KView editor's Conditions tab, or in the visual builder with Only show this attribute if…. Conditions aren't available for expanded-timeline views.
{ "conversation_custom_returnReasonStr": { "condition": { "op": "or", "values": [ { "op": "eq", "property": "conversation_custom_contactReasonStr", "values": [ "return" ], "dependentFieldRequired": true }, { "op": "startsWith", "property": "conversation_custom_categoryTree", "values": [ "orders.returns" ] } ] } } }
conversationresource_custom_customreturnReasonStrattributeconversationresource_custom_customcontactReasonStrattributeconversationresource_custom_customcategoryTreeattributeRequired to mark the conversation done
- Contact reason equals "return": Contact reason is "return"
- Category is within "orders.returns": "orders.returns.damaged" is within "orders.returns"
At least one condition matches, so the row shows.
dependentFieldRequired is allowed. Change the sample record in the preview to see the row show and hide. Paste the JSON into the editor’s Conditions tab; a view can hold one key per row.The Conditions tab holds a JSON object with a key for each row that has conditions:
{
"conversation_custom_returnReasonStr": {
"condition": {
"op": "or",
"values": [
{ "op": "eq", "property": "conversation_custom_contactReasonStr", "values": ["return"], "dependentFieldRequired": true },
{ "op": "exists", "property": "conversation_custom_trackingNumberStr", "values": [] }
]
}
}
}When a row's conditions stop matching, Kustomer hides it and clears its value if the agent can edit the record, so a stale answer doesn't stay behind. List attributes such as conversation_channels match eq when they include the value, and exists when they aren't empty.
Masked data and dark mode
Field, BasicField, Row, BasicRow and the other field components show masked values the way the agent is allowed to see them, and Kustomer turns a masked value placed directly inside an element, such as <div>{email}</div>, into its masked text. Anywhere else, such as inside a template string or a URL, you get the masked object itself. See Handle masked data.
Every component follows the agent's theme. For your own markup, use the theme variables instead of fixed colors.
Layout
Container
An outer wrapper that can add Kustomer's standard padding. Works in every context.
<Container usePadding>
<BasicField label="Order" value={object.title} />
</Container>Segment
Groups related content and leaves 28px of space below it. Works in every context.
<div>
<Segment>
<BasicField label="Status" value={object.custom.statusStr} />
</Segment>
<Segment>
<BasicField label="Notes" value={object.custom.notesTxt} />
</Segment>
</div>Grid
Lays out Columns in a row 16 units wide. Columns past 16 wrap to the next line; nothing checks the sum. Designed for expanded-timeline and smartbar-card.
<Grid>
<Column size="ten">
<Field type="kobject.order" field="orderNumberStr" value={object.custom.orderNumberStr} />
<Field type="kobject.order" field="placedAt" value={object.custom.placedAt} />
</Column>
<Column size="six">
<Field type="kobject.order" field="totalNum" value={object.custom.totalNum} />
</Column>
</Grid>Column
One column of a Grid, sized in sixteenths of the row. Designed for expanded-timeline and smartbar-card.
<Grid>
<Column size="eight" style={{ background: 'var(--background-secondary)' }}>
<BasicField label="Size" value="eight" />
</Column>
</Grid>Timeline fields
Field
A labeled value, formatted from the attribute's metadata, so dates, numbers, booleans and links look the way they do elsewhere in Kustomer. Designed for expanded-timeline and smartbar-card.
<Field type="kobject.order" field="placedAt" value={_.get(object, 'custom.placedAt')} />BasicField
A label and a value as you pass them, in the field layout. It doesn't read metadata or format values. Designed for expanded-timeline and smartbar-card.
<BasicField label="Carrier" value="UPS Ground" />Images
An image carousel with a dot for each image, up to 450px tall. Renders nothing for an empty array. Designed for expanded-timeline and smartbar-card.
<Images images={object.images} />Detail rows
Row
An attribute row for the insights panel. Agents click the value to edit it, and Kustomer saves it to the record. Designed for smartbar-card and smartbar-details.
<Row
object="conversation"
field="returnReasonStr"
value={_.get(conversation, 'custom.returnReasonStr')}
/>BasicRow
A label and a value in the row layout. It isn't editable and doesn't read metadata. Designed for smartbar-card and smartbar-details.
<BasicRow label="Loyalty tier" value={_.get(customer, 'custom.loyaltyTierStr', 'none')} />CreateFormRow
One field of a KObject create form, with the input for its type. Designed for create-form.
<div>
<CreateFormRow klass="kobject.order" field="orderNumberStr" />
<CreateFormRow klass="kobject.order" field="placedAt" />
</div>RowAssignmentUser
The conversation's assignee, with Kustomer's picker for changing it. Designed for smartbar-details.
<RowAssignmentUser />RowAssignmentTeam
The conversation's team. Renders nothing when the org has no teams. Designed for smartbar-details.
<RowAssignmentTeam />RowPriority
The conversation's priority, 1 to 5. Agents click to change it. Designed for smartbar-details.
<RowPriority />CollapsedRows
A wrapper that older generated layouts put around rows. It no longer collapses anything. Designed for smartbar-details.
<CollapsedRows>
<RowPriority />
<RowAssignmentUser />
</CollapsedRows>Relationship cards
Customer
A link to a customer: avatar and name. Renders nothing until the customer loads. Works in every context.
<Customer id={customer.id} />CustomerField
Customer with a label, in the field layout. Designed for expanded-timeline and smartbar-card.
<CustomerField id={customer.id} label="Customer" />Company
A link to a company. Renders nothing until the company loads. Works in every context.
<Company id={company.id} />CompanyField
Company with a label, in the field layout. Designed for expanded-timeline and smartbar-card.
<CompanyField id={company.id} label="Company" />Conversation
A link to a conversation, with its channel's icon. Works in every context.
<Conversation id={'64f0c1a2e7b9d1001f3a3001'} />ConversationField
Conversation with a label, in the field layout. Designed for expanded-timeline and smartbar-card.
<ConversationField id={'64f0c1a2e7b9d1001f3a3001'} label="Conversation" />Kobject
A link to a KObject, with its title. Works in every context.
<Kobject id={'64f0c1a2e7b9d1001f3a4002'} type="kobject.order" />KobjectField
Kobject with a label, in the field layout. Designed for expanded-timeline and smartbar-card.
<KobjectField id={'64f0c1a2e7b9d1001f3a4002'} type="kobject.order" label="Kobject" />Buttons and pills
Button
The primary action in a view. Works in every context.
<Button icon="refresh"
onClick={() => KustomerRequest({ url: `/v1/customers/${customer.id}` }, (err) => (err ? 'Refresh failed' : 'Customer refreshed'))}
>
Refresh
</Button>ButtonSecondary
An outlined button for actions beside a primary one. Works in every context.
<ButtonSecondary icon="refresh"
onClick={() => KustomerRequest({ url: `/v1/customers/${customer.id}` }, (err) => (err ? 'Refresh failed' : 'Customer refreshed'))}
>
Refresh
</ButtonSecondary>ButtonDanger
An outlined red button for destructive actions, such as cancelling an order. Works in every context.
<ButtonDanger icon="refresh"
onClick={() => KustomerRequest({ url: `/v1/customers/${customer.id}` }, (err) => (err ? 'Refresh failed' : 'Customer refreshed'))}
>
Refresh
</ButtonDanger>ButtonText
A button without a border, for low-emphasis actions. Works in every context.
<ButtonText
onClick={() => KustomerRequest({ url: `/v1/customers/${customer.id}` }, (err) => (err ? 'Refresh failed' : 'Customer refreshed'))}
>
Refresh
</ButtonText>ButtonGroup
A row of buttons with 8px between them. Works in every context.
<ButtonGroup>
<Button onClick={() => runCommand('acme_orders.app.resend-receipt', { args: { orderId: '4417' } }, (err) => (err ? 'Not sent' : 'Receipt sent'))}>Resend receipt</Button>
<ButtonSecondary link="https://shop.acme.example/orders/4417">Open in Acme</ButtonSecondary>
</ButtonGroup>Pill
A small rounded status label. Each type has its own colors, and some add an icon. Works in every context.
<Pill type="success" message={_.startCase(object.custom.statusStr)} />Embedding
DynamicCard
An iframe of a page you host, sized to fit as the page calls Kustomer.resize() from the Cards SDK. Works in every context.
<DynamicCard src={`https://kustomer.acme.example/card?customer=${customer.id}`} context={context} fillToWidth />Card
An iframe of a page you host, at a fixed size. Works in every context.
<Card src="https://kustomer.acme.example/returns" height={200} fillToWidth />Data
WithUser
Loads a Kustomer user and passes it to a function you write. Renders nothing until the user loads. Works in every context.
<WithUser id={conversation.assignedUsers[0]}>
{(user) => <BasicRow label="Assigned to" value={user.displayName} />}
</WithUser>Helpers
_
Lodash. _.get reads nested values without throwing when a parent is missing. Works in every context.
<BasicRow label="Lifetime value" value={_.get(customer, 'custom.lifetimeValueNum', 0).toFixed(2)} />moment
Moment.js, for formatting and comparing dates. Works in every context.
<BasicField label="Placed" value={moment(object.custom.placedAt).format('MMM D, YYYY')} />KustomerRequest
Calls the Kustomer API as the agent, with the agent's permissions. Returns a promise that resolves to the response, or to the error: it never rejects. Works in every context.
<Button
onClick={() =>
KustomerRequest(
{ url: `/v1/customers/${customer.id}`, method: 'PATCH', body: { custom: { loyaltyTierStr: 'platinum' } } },
(err, attributes) => (err ? 'Update failed' : `Tier set to ${attributes.custom.loyaltyTierStr}`),
)
}
>
Upgrade to platinum
</Button>runCommand
Runs one of your app's commands, so Kustomer's servers call your API with the app's secrets. Works in every context.
<Button
onClick={() =>
runCommand('acme_orders.app.lookup-order', { args: { orderId: object.externalId } }, (err, run) =>
err ? 'Acme is unreachable' : `Acme answered ${run.statusCode}`,
)
}
>
Check with Acme
</Button>renderHandlebars
Fills in merge fields, such as {{customer.email}}, from the view's data. Returns the fallback when any of them is empty. Works in every context.
<ButtonSecondary link={renderHandlebars('https://shop.acme.example/admin?email={{customer.email}}', 'https://shop.acme.example/admin', data)}>
Find in Acme
</ButtonSecondary>internationalizeStandardAttribute
Returns a standard attribute's display name in the agent's language, or the attribute name if it isn't a standard attribute. Works in every context.
<BasicRow label={internationalizeStandardAttribute('createdAt', 'customer')} value={moment(customer.createdAt).format('LL')} />renderSnippet
Renders one of the org's snippets in the agent's language, or the key if there's no such snippet. Works in every context.
<BasicRow label="Returns" value={renderSnippet('returns_policy')} />