Advanced Search Tier Feature: Direct answers are only available on the Advanced Search tier. Check out the Search Tiers reference doc to learn more about the features that are available in each tier.
The DirectAnswer component shows a Direct Answer to a query. When we are confident that a search has one answer, we will surface a Direct Answer box at the top of the results.

How Direct Answer Formatting Works by Default
Yext Search has two types of Direct Answers: Featured Snippet and Field Value. Each of these has its own cardType since they each surface different types of content.
In the Theme, we have the following card files:
Field Value Direct Answers (surfacing from structured data in the Knowledge Graph)
- directanswers-card > allfields-standard > component.js
- directanswers-card > allfields-standard > template.hbs
Featured Snippet Direct Answers (surfacing from unstructured data in the Knowledge Graph)
- directanswers-card > documentsearch-standard > component.js
- directanswers-card > documentsearch-standard > template.hbs
Note that for the Field Value Direct Answers, in Jambo, we provide you with some default styling by field type. In the file, we will outline all field types and their corresponding default formatting and direct answer card layout. This means the files are long and can be a bit intimidating - we recommend using ctrl-find to find what you're looking for more easily.
Note: You must be on Theme v1.20 or above to use featured snippets and Theme v1.22 or above to use rich text featured snippets.
Overriding Direct Answer Formatting
To modify the settings of Direct Answers, add the DirectAnswer component in ComponentSettings for your Search page and change the cardType to your forked card.
"DirectAnswer": {
"types": {
"FEATURED_SNIPPET": {
"cardType": "documentsearch-standard"
},
"FIELD_VALUE": {
"cardType": "allfields-standard"
}
}
},
To modify the styling of Direct Answers, use Jambo Commands > Add Direct Answer Card. This will fork the default card in the theme and create a new set of files in a new top-level directory.
In general, we don't recommend forking the default formatting unless you really need to.
Featured Snippet Direct Answers
The most common use case for forking the documentsearch-standard card is adding a CTA. Like with result cards, you can adjust the data mappings of a CTA button to be either hardcoded or entity field values.
When referencing an entity field value on a regular result card, we map to the entity's desired field using the syntax profile.fieldName. Due to differences in the content returned from the API for Featured Snippets, the syntax for mapping to a field changes to relatedItemData.fieldValues.fieldName.
Below is an example of a CTA added to the component.js file for a forked Featured Snippet Direct Answer Card, mapped to the Primary CTA field:
CTA: {
label: relatedItemData.fieldValues.c_primaryCTA ? relatedItemData.fieldValues.c_primaryCTA.label : null, // The CTA's label
url: Formatter.generateCTAFieldTypeLink(relatedItemData.fieldValues.c_primaryCTA), // The URL a user will be directed to when clicking
target: linkTarget, // Where the new URL will be opened
eventType: 'CTA_CLICK', // Type of Analytics event fired when clicking the CTA
eventOptions: this.addDefaultEventOptions({ fieldName: 'snippet' }) // The event options for CTA click analytics
}
Field Value Direct Answers
Note: Field value direct answers is available on vertical search on Theme v1.27 and above.
When you fork the allfields-standard card, you can choose to specify the formatting by field type (default) OR by specific field. This is made possible by using a switch function in the javascript.
component.js file
switch (answer.fieldType) {
case 'url':
case 'complex_url':
case 'ios_app_url':
case 'android_app_url':
case 'facebook_url':
if (isArray) {
arrayValue = answer.value.map((value) => ({
url: value,
label: value
}
));
} else {
regularValue = {
url: answer.value,
label: answer.value
};
}
value = isArray ? arrayValue : regularValue;
break;
case 'email':
if (isArray) {
arrayValue = answer.value.map((value) => ({
url: `mailto:${value}`,
label: value,
}
));
} else {
regularValue = {
url: `mailto:${answer.value}`,
label: answer.value,
};
}
value = isArray ? arrayValue : regularValue;
break;
case 'instagram_handle':
if (isArray) {
arrayValue = answer.value.map((value) => ({
url: `https://instagram.com/${value}`,
label: `@${value}`,
}
));
} else {
regularValue = {
url: `https://instagram.com/${answer.value}`,
label: `@${answer.value}`,
};
}
value = isArray ? arrayValue : regularValue;
break;
case 'twitter_handle':
if (isArray) {
arrayValue = answer.value.map((value) => ({
url: `https://twitter.com/${value}`,
label: `@${value}`,
}
));
} else {
regularValue = {
url: `https://twitter.com/${answer.value}`,
label: `@${answer.value}`,
};
}
value = isArray ? arrayValue : regularValue;
break;
case 'phone':
if (isArray) {
arrayValue = answer.value.map((value) => ({
url: Formatter.phoneLink({mainPhone: value}),
label: Formatter.nationalizedPhoneDisplay({mainPhone: value}),
}
));
} else {
regularValue = {
url: Formatter.phoneLink({mainPhone: answer.value}),
label: Formatter.nationalizedPhoneDisplay({mainPhone: answer.value}),
};
}
value = isArray ? arrayValue : regularValue;
break;
case 'address':
if (isArray) {
arrayValue = answer.value.map((value) => Formatter.address({address: value}));
} else {
regularValue = Formatter.address({address: answer.value});
}
value = isArray ? arrayValue : regularValue;
break;
case 'hours':
if (isArray) {
arrayValue = answer.value.map((value) => `<div>${Formatter.openStatus({hours: value})}</div>`);
} else {
regularValue = `<div>${Formatter.openStatus({hours: answer.value})}</div>`;
}
value = isArray ? arrayValue : regularValue;
break;
case 'decimal':
if (isArray) {
arrayValue = answer.value.map((value) => value.toLocaleString());
} else {
regularValue = answer.value.toLocaleString();
}
value = isArray ? arrayValue : regularValue;
break;
case 'rich_text':
if (isArray) {
arrayValue = answer.value.map((value) => ANSWERS.formatRichText(value));
} else {
regularValue = ANSWERS.formatRichText(answer.value);
}
value = isArray ? arrayValue : regularValue;
case 'single_line_text':
case 'multi_line_text':
default:
value = answer.value;
break;
}
To add a new field type, add another case statement above the 'default' case:
case 'fieldtype': // functions go here value = "value you want to return" break;
To update a specific field, navigate to the bottom of the file and uncomment the section. Below is an example of specific formatting for the mainPhone field:
switch (answer.fieldApiName) {
case 'mainPhone': // The Field API name
if (isArray) {
arrayValue = answer.value.map((value) => ({
url: Formatter.phoneLink({mainPhone: value}),
label: Formatter.nationalizedPhoneDisplay({mainPhone: value})
}
));
} else {
regularValue = {
url: Formatter.phoneLink({mainPhone: answer.value}),
label: Formatter.nationalizedPhoneDisplay({mainPhone: answer.value})
};
}
value = isArray ? arrayValue : regularValue;
break;
}