Embedding a Video Into a Card

Requirements

  • You should have a video publicly uploaded to a content website. We’ll use YouTube and Vimeo as examples, but this solution is generic to content websites.
  • The URL should be the embeddable URL. You can find this URL by going to share and clicking the embed option on the respective website. See Gotchas for more information.
  • The URL should be HTTPS. An HTTP link will not work if you try to view the video in an HTTPS link.
  • This is assuming you already have an Answers Experience and a Jambo site that is viewable in Live Preview.
  • Let’s assume that the YouTube video URL is in a video list field videos and the Vimeo video URL is in a vimeo URL custom field c_vimeo.

Step-by-step:

  1. Add a new card. For this, I will be referring to a new card called multilang-product-prominentvideo, a card that is branched from the multilang-product-prominentimage card. You can branch whatever card you want and name it whatever you want. See more at Adding & Updating Cards | Hitchhikers

  2. Direct our desired vertical to use this new card. In my case, I changed config/locations.json to have "cardType": "multilang-product-prominentvideo",.

  3. Update cards/multilang-product-prominentvideo/component.js

Within our dataForRender(profile) function, we will want to get our video URL with null-checking. Note that depending on the field structure, you may have a different field to check for.

Example 1

const youtubeUrl = (profile.videos && profile.videos[0] && profile.videos[0].video && profile.videos[0].video.url) ? profile.videos[0].video.url : 'https://www.youtube.com/embed/Ww4EqJ1YUmw';

Example 2

const vimeoUrl = profile.c_vimeo ? profile.c_vimeo : 'https://player.vimeo.com/video/436602215';

In the return statement of the same file, we will add the video URL. Your function may look something like this:

...
  dataForRender(profile) {
  	...

    const vimeoUrl = profile.c_vimeo ? profile.c_vimeo : 'https://player.vimeo.com/video/436602215';

    return {
      title: profile.name, // The header text of the card
      url: profile.landingPageUrl, // If the card title is a clickable link, set URL here
      ...
      videoUrl: vimeoUrl, // The embeddable URL of the video to display on the card
      ...
    };
...
  1. Update cards/multilang-product-prominentvideo/template.hbs

Now we want to use the URL in an iframe to show the embedded video player. Put this HTML wherever you would expect the video to show on the card. In my example, I am replacing the image in the prominentimage card with the video.

  {{#if card.videoUrl}}
  <div class="HitchhikerProductProminentVideo-videoWrapper">
    <iframe class="HitchhikerProductProminentVideo-video"
      width="100%"
      height="100%"
      src="{{card.videoUrl}}"
      allowfullscreen>
    </iframe>
  </div>
  {{/if}}

So your template might look like this:

<div class="HitchhikerProductProminentVideo {{cardName}}">
  {{#if card.videoUrl}}
  <div class="HitchhikerProductProminentVideo-videoWrapper">
    <iframe class="HitchhikerProductProminentVideo-video"
      width="100%"
      height="100%"
      src="{{card.videoUrl}}"
      allowfullscreen>
    </iframe>
  </div>
  {{/if}}
  <div class="HitchhikerProductProminentVideo-body">
    {{> title }}
    {{> subtitle }}
    <div class="HitchhikerProductProminentVideo-contentWrapper">
      {{> details }}
      {{> ctas }}
    </div>
  </div>
</div>
  1. Update theme.scss

Now we will want to style the height/width of the video player. In general, you may want the height/width to take up as much of the wrapper as it can. So, you can add the CSS below. The class is specific to your card class.

  .HitchhikerProductProminentVideo-video {
    width: 100%;
    height: 100%;
  }

Gotchas

  • If you get the error (below), then you are trying to load an HTTP video on an HTTPS site. You should change your video URL to an HTTPS URL.
Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure frame '<URL>'. This request has been blocked; the content must be served over HTTPS.
  • If you get the error (below), then you are trying to use a video URL that is not embeddable via iFrame (determined by the website hosting the video). You should find the link that is embeddable.
Refused to display '<URL>' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
7 Likes

Is there a way that we can leverage the asset feature in the KG to include a video in a card? Would that involve tying that asset to a field for another entity and then referencing that field in the data for render function?

Hi @Sarah_Dougherty,

Great question. When leveraging the asset feature, it looks like you’re getting a video URL that is not embeddable (aka the “watch” URL) instead of an embeddable URL.

To replace the URL, you will want to make a slight adjustment under the dataForRender(profile). First, you can set videoURL to the KG Asset field like so with a null check:

const videoUrl = profile.videos && profile.videos[0] && profile.videos[0].video && profile.videos[0].video.url;

Second, you can use the split method to adjust the youtube URL of “watch?v=” to “embed” like so:

const videoId = videoUrl ? videoUrl.split('watch?v=')[1] : '';

Then lastly, you will add the following check of videoId. You can insert a specific video URL in the quotes if you would like to add a video to fall back on.

let youtubeUrl = '';
if (videoId) {
 youtubeUrl = 'https://www.youtube.com/embed/' + videoId;
}

Let me know if you have any questions or concerns there.

Best,
Alyssa

What a great resource! Is there any way to have a video appear in a modal popup when a CTA is clicked?

Hey @christian do you have a demo account or some example where we can see what this looks like in action? Thanks for posting this. Super helpful!

Thanks for the step-by-step instructions! I have just added a video vertical following this post, but some of the videos are autoplaying & I’m not sure how I can adjust the code to stop the autoplay feature. I believe only some of the videos are autoplaying due to the embeddable link I’m grabbing from Wistia, controlled on their end.

Here is the staging link with an example query: https://search_parkmobile_io.yextpages.net/video.html?query=parking&referrerPageUrl=&tabOrder=.%2Findex.html%2Ccity%2Clocations%2Cvideos%2Clinks%2Cfaqs%2Cblog

Is it possible to embed a click-through experience as well? A client has a demo experience on their main site that a user can click through and they want to embed or iframe this into a vertical result page in their answer’s experience so that a user can click through these demos in our vertical and not have to be redirected to their main site.

Hi @Catalina_Ruiz-Jimene

Yep! Following the outline that Christian laid out would be a possibility here for you – this is more in line with the second option I outlined in the other post where you add the iframe code to the .hbs file…

<div class="CardTypeDemoExperience {{cardName}}">
  {{#if card.demoExperience}}
  <div class="CardTypeDemoExperience-Wrapper">
 <iframe snippet.
    </iframe>
  </div>
  {{/if}}

Hi!

I am trying to add videos to my Videos vertical using Vidyard videos (not Youtube or Vimeo). Would you mind taking a look at my hbs file below? Attaching my component.js file as well.


The error I’m getting on the page is as follows:

Thinking this is an issue with the way I configured my handlebars or the const formatter, but would love any help with this!

UPDATE - I resolved this! I needed to update the way the const was defined for videoUrl. See below:

dataForRender(profile) {
// this.youtubeUrl = Formatter.getYoutubeUrl(profile.videos || );
// this.vimeoUrl = profile.c_vimeo;
const videoUrl = profile.c_videoURL ? profile.c_videoURL : profile.landingPageUrl;

const linkTarget = AnswersExperience.runtimeConfig.get('linkTarget') || '_top';

return {
  title: profile.name, // The header text of the card
  url: profile.landingPageUrl, // If the card title is a clickable link, set URL here
  target: linkTarget, // If the title's URL should open in a new tab, etc.
  titleEventOptions: this.addDefaultEventOptions(),
  subtitle: profile.featuredMessage?.description, // The sub-header text of the card
  videoUrl: profile.c_videoURL,
  // videoUrl: this.youtubeUrl || this.vimeoUrl,
  details: profile.richTextDescription ? ANSWERS.formatRichText(profile.richTextDescription, 'richTextDescription', linkTarget) : null, // The text in the body of the card
  // If the card's details are longer than a certain character count, you can truncate the
  // text. A toggle will be supplied that can show or hide the truncated text.
1 Like