Formatting Direct Answers | Yext Hitchhikers Platform

What You’ll Learn

In this section, you will learn:

  • How default formatting works for Direct Answers
  • How you would override that formatting

Review of Direct Answers

When we are confident that a search has one answer, we will surface a Direct Answer box at the top of the results. We will also surface any other results that may be relevant below the direct answer.

For example, if someone searches for “Phone number NYC office” on Yext.com, we know that they are looking for:

  • The Yext NYC office
  • The phone number

The result would look like this:

direct answer example

To enable direct answers, you just need to add the fields you want to be eligible in the Search Configuration in the searchableFields object for each vertical.

But, how do you change what the results look like? What if you want the Phone Number fields to use the phone formatter to make it look better? What if you want a specific field to be formatted differently? What if I want to add a CTA to my Featured Snippet?

This is all possible!

How Direct Answer Formatting Works by Default

As we went over in the Search Configuration unit on Direct Answers, Yext Search has two types of Direct Answers: Featured Snippet and Field Value. Each of these has its own cardType as you can see below since they each surface different types of content.

"DirectAnswer": {
      "types": {
        "FEATURED_SNIPPET": {
          "cardType": "documentsearch-standard"
        },
        "FIELD_VALUE": {
          "cardType": "allfields-standard"
        }
      }
    },

In the Answers Hitchhiker theme, we have the following files:

Field Value Direct Answers (surfacing from structured Knowledge Graph data)

  • directanswers-card > allfields-standard > component.js
  • directanswers-card > allfields-standard > template.hbs

Featured Snippet Direct Answers (surfacing from unstructured Knowledge Graph data)

  • 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.

Overriding Direct Answer Formatting

We’ve applied formatting and settings that we think works best so that you don’t have to do the heavy-lifting. That said, sometimes you just want something different and we provide that flexibility too.

To modify the settings of Direct Answers, you would 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, you would 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 for you called:

  • directanswers-card > allfields-standard > component.js
  • directanswers-card > allfields-standard > template.hbs

or if you override the document search:

  • directanswers-card > documentsearch-standard > component.js
  • directanswers-card > documentsearch-standard > template.hbs

This behaves very similarly to the result cards:

  • You can modify the formatters by field type, or optionally by specific field in the component.js file for Field Value Direct Answers
  • You can modify the structure of the direct answer results in the template.hbs file
  • You can modify the css for the direct answer cards in the answers.scss file (which you’ll learn about later)

In general, we don’t recommend forking the default formatting unless you really need to. This is because as new field types are introduced by Yext, we will update the theme accordingly. As soon as you fork the file, you are now responsible for adding the new formatting to your file. You can always reference the theme file to see what we added, but it’s an extra step you’ll need to take.

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 that has been added to the component.js file for a forked Featured Snippet Direct Answer Card and 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

When you fork the allfields-standard card, as mentioned, you can choose to specify the formatting by field type (default) OR by specific field. We make this 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, formatted like the below.

case 'fieldtype':
  // functions go here
  value = "value you want to return"
  break;

To update a specific field, you will navigate to the bottom of the file. Uncomment this section by selecting the code block and hitting ctrl + / or command + /. 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;
}

Common Use Cases

When might you want to override the default Direct Answer cards?

You might want to do this when:

  • You want to add CTAs to Direct Answer cards. This would likely be done by field and not by field type. You would need to update both the component.js file to indicate what the CTA label and link should be, as well as the HBS file to make sure the card layout is expecting a CTA.
  • You want to change the structure of the card, like return a mini-map with any address fields. You would update the hbs file in this case.
unit Quiz
+20 points
Daily Quiz Streak Daily Quiz Streak: 0
Quiz Accuracy Streak Quiz Accuracy Streak: 0
    Error Success Question 1 of 2

    What is the default way we format Field Value Direct Answer results?

    Error Success Question 2 of 2

    Should you always override the default Direct Answers card?

    Way to go, you passed! 🏁

    You've already completed this quiz, so you can't earn more points.You completed this quiz in 1 attempt and earned 0 points! Feel free to review your answers and move on when you're ready.
1st attempt
0 incorrect
Feedback