Search code examples
reactjsapiyoutubegatsbyx-frame-options

Only some embedded YouTube videos fetched from SpaceX API do not load in browser


I have a webpage where I'm displaying data pulled from a SpaceX API: SpaceX API

Published site: Gatsby SpaceX data

I'm trying to display a youtube embed for each of the launches in the pastLaunches component. I'm receiving the following error in the browser: 'Refused to display '' in a frame because it set 'X-Frame-Options' to 'sameorigin'.' I've tried adjusting my configuration file (netlify.toml) but nothing has worked so far. I'm also only receiving this error for the videos that refuse to load. Again, some of the videos DO load. It also looks like the videos that aren't working are not converting from watch/ to embed/. I'm somewhat new to React and haven't dealt with this level of functionality yet, any tips while I continue to research are helpful!

Here is the section where I'm using the youtube link:

{links.video_link
                ? <iframe
                className="h-48"
            src={links.video_link && links.video_link.replace('watch?v=', 'embed/')}
            title={mission_name}
            frameBorder={0}
            allow={"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"}
            allowFullScreen={true}
            webkitallowfullscreen="true"
            mozallowfullscreen="true"
          />
          : <p>Sorry, no Youtube video is available</p>
            }

Here is the full component:

import React, { useState, useEffect }  from 'react'
import moment from "moment"
import "../styles/tailwind.css"

const getPastLaunches = () => {
  return fetch('https://api.spacexdata.com/v3/launches/past?limit=50&sort=flight_number&order=desc')
  .then((res) => res.json())
}

const PastLaunches = () => {
  const [launches, setLaunches] = useState(null)


  useEffect(() => {
    getPastLaunches()
      .then(setLaunches)
  }, [])

  if (launches === null) {
    return <p>Loading past launches...</p>
  }

  return (
    <>
    <div className="flex flex-row justify-center items-end mx-auto mt-20 mb-4 overflow-x-visible">
        <h1 className="text-white text-2xl font-mono font-bold border-b-8 border-blue-600 mr-2" style={{lineHeight: .45}}>Recent Launches  </h1>
                <svg width="20" height="20" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="#ffffff">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={4} d="M19 14l-7 7m0 0l-7-7m7 7V3"  />
        </svg>
    </div>


    <ul className="grid py-2 lg:grid-cols-3 md:grid-cols-2 md:gap-2 mx-auto">
    {launches.map(launch => {
        const {flight_number, details, mission_name, launch_date_local, links, rocket, launch_site} = launch;

      return (
        <li className="max-w-sm max-h-100 flex flex-auto flex-col p-2 rounded overflow-hidden shadow-lg bg-gray-700 bg-opacity-50 text-white">
            {links.video_link
                ? <iframe
                className="h-48"
            src={links.video_link && links.video_link.replace('watch?v=', 'embed/')}
            title={mission_name}
            frameBorder={0}
            allow={"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"}
            allowFullScreen={true}
            webkitallowfullscreen="true"
            mozallowfullscreen="true"
          />
          : <p>Sorry, no Youtube video is available</p>
            }
            
            
        <div className="flex flex-col p-4 text-gray-100">
            <div className="flex flex-row justify-between items-start space-x-8 pb-2">
                <h1 className="font-bold text-lg">Rocket: {rocket.rocket_name}</h1>
                  
              <strong className="text-lg">Flight # {flight_number}</strong>
              
            </div>
             
              <p><strong>Mission: </strong>{mission_name}</p>
              <p><strong>Location: </strong>{launch_site.site_name_long}</p>
               

              <p><strong>Launch Date: </strong>{moment(launch_date_local).format("dddd, MMMM Do YYYY, h:mm:ss a")}</p>

              {details
                ? <p className="pt-2">{details}</p>
          : <em className="text-sm pt-2">I'm sorry, no details are available for this launch</em>
            }
            
      </div>
        </li>
      );
    })}

  </ul>
</>

  )
}

export default PastLaunches

And my current netlify.toml:

[build]
  functions = "/root"
  
[[headers]]
  # Define which paths this specific [[headers]] block will cover.
  for = "/*"
    [headers.values]
    Access-Control-Allow-Origin = "*"
    cache-control = '''
    max-age=0,
    no-cache,
    no-store,
    must-revalidate'''

Here are is a screenshot for the error:

Console errors

Error message for broken video: Subframe error

Successful video: successful video


Solution

  • I suspect SpaceX has disabled embedding for some of their videos. The X-Frame-Options HTTP header tells the browser where the content can be embedded via framing. The sameorigin value prevents embedding on domain names other than the one the content is being retrieved from.

    screenshot of the “Allow embedding” option on a YouTube video settings