Repository: expo/expo
File: docs/ui/components/VideoBoxLink/index.tsx (TypeScript)

Code before:
import { CALLOUT, LABEL } from '~/ui/components/Text';

type VideoBoxLinkProps = {
  title: string;
  description: ReactNode;
  videoId: string;
  time?: number;
  className?: string;
};

export function VideoBoxLink({ title, description, videoId, time, className }: VideoBoxLinkProps) {
  const videoStructuredData = {
    '@context': 'https://schema.org',
    '@type': 'VideoObject',
    name: title,
    description: title,
    thumbnailUrl: `https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`,
    embedUrl: `https://www.youtube.com/embed/${videoId}`,
    contentUrl: `https://www.youtube.com/watch?v=${videoId}`,
  };

  return (
    <>
      <StructuredData data={videoStructuredData} id={`video-${videoId}`} />
      <LinkBase
        openInNewTab
        href={`https://www.youtube.com/watch?v=${videoId}${time ? `&t=${time}` : ''}`}
        className={mergeClasses(
          'group relative flex items-stretch overflow-hidden rounded-lg border border-default bg-default shadow-xs transition',
          'hocus:bg-subtle hocus:shadow-sm',
          'max-sm-gutters:flex-col',
          '[&+hr]:!mt-6',
          className
        )}
        aria-label={`Watch video: ${title} (opens in new tab)`}>
        <div
          className={mergeClasses(
            'relative flex max-w-[200px] items-center justify-center overflow-hidden border-r border-secondary bg-element',
            'max-sm-gutters:max-w-full max-sm-gutters:border-b max-sm-gutters:border-r-0'
          )}>
          <img
            src={`https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
            className="aspect-video transition duration-300 group-hover:scale-105 group-focus-visible:scale-105"
            alt={title}
            aria-label={`Video thumbnail for ${title}`}
          />
          <div
            className="absolute right-[calc(50%-22px)] top-[calc(50%-22px)] flex size-[44px] items-center justify-center rounded-full bg-[#000a]"
            role="presentation"
            aria-hidden="true">
            <PlaySolidIcon className="icon-lg ml-0.5 text-palette-white" />

Review comment (refactor):
The `id` isn't used by the `StructuredData` component - should that be used as the key? Otherwise with pages with multiple videos they'll all have the same key in the script tag and they are deduplicated by the key.

Code after:
import { StructuredData } from '~/ui/components/StructuredData';
import { CALLOUT, LABEL } from '~/ui/components/Text';

type VideoBoxLinkProps = {
  title: string;
  description: ReactNode;
  videoId: string;
  time?: number;
  className?: string;
};

export function VideoBoxLink({ title, description, videoId, time, className }: VideoBoxLinkProps) {
  const uploadDate = getVideoUploadDate(videoId);
  const videoStructuredData = {
    '@context': 'https://schema.org',
    '@type': 'VideoObject',
    name: title,
    thumbnailUrl: `https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`,
    embedUrl: `https://www.youtube.com/embed/${videoId}`,
    contentUrl: `https://www.youtube.com/watch?v=${videoId}`,
    ...(uploadDate && { uploadDate }),
  };

  return (
    <>
      <StructuredData data={videoStructuredData} id={`video-${videoId}`} />
      <LinkBase
        openInNewTab
        href={`https://www.youtube.com/watch?v=${videoId}${time ? `&t=${time}` : ''}`}
        className={mergeClasses(
          'group relative flex items-stretch overflow-hidden rounded-lg border border-default bg-default shadow-xs transition',
          'hocus:bg-subtle hocus:shadow-sm',
          'max-sm-gutters:flex-col',
          '[&+hr]:!mt-6',
          className
        )}
        aria-label={`Watch video: ${title} (opens in new tab)`}>
        <div
          className={mergeClasses(
            'relative flex max-w-[200px] items-center justify-center overflow-hidden border-r border-secondary bg-element',
            'max-sm-gutters:max-w-full max-sm-gutters:border-b max-sm-gutters:border-r-0'
          )}>
          <img
            src={`https://i3.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
            className="aspect-video transition duration-300 group-hover:scale-105 group-focus-visible:scale-105"
            alt={title}
            aria-label={`Video thumbnail for ${title}`}
          />
          <div
            className="absolute right-[calc(50%-22px)] top-[calc(50%-22px)] flex size-[44px] items-center justify-center rounded-full bg-[#000a]"
            role="presentation"