Skip to main content

Overview

The PrivacyEmbed component is a reusable wrapper that implements the “facade pattern” for embedding third-party content. It displays a poster image with a load button instead of immediately loading external iframes, preventing cookies and tracking until the user explicitly consents.
This component is used internally by YouTubePlayer but can be used directly to wrap any external embeddable content.

Props

url
string
required
The iframe URL to load when the user clicks the load button. For YouTube URLs, automatically converts to youtube-nocookie.com.
title
string
required
Descriptive title for the embedded content, used for accessibility labels.
poster
string
Optional image URL to display before loading. Shows as a background image behind the load button.
class
string
Additional CSS classes to apply to the container element for custom styling.

Features

Automatic YouTube Privacy Enhancement

The component automatically detects and converts YouTube URLs:
const safeUrl = url.includes("youtube.com/embed")
  ? url.replace("youtube.com", "youtube-nocookie.com")
  : url;
This ensures privacy-enhanced mode even if a standard YouTube URL is provided.
No iframe is rendered on initial page load. The external content URL is stored in a data-src attribute and only loaded on user interaction.
Benefits:
  • No cookies set on page load
  • No third-party scripts executed
  • No tracking pixels fired
  • Minimal external requests

Interactive Facade

The component displays a clickable facade with:
  • Optional poster image background
  • Play button icon (SVG)
  • “Load Content” text label
  • Hover effects for interactivity

Usage Examples

YouTube Video

---
import PrivacyEmbed from '@/components/PrivacyEmbed.astro';
---

<PrivacyEmbed 
  url="https://www.youtube.com/embed/dQw4w9WgXcQ"
  title="Rick Astley - Never Gonna Give You Up"
  poster="https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"
  class="aspect-video rounded-lg"
/>

Vimeo Video

<PrivacyEmbed 
  url="https://player.vimeo.com/video/148751763"
  title="Stunning Nature Documentary"
  poster="/images/vimeo-poster.jpg"
  class="aspect-video h-[500px] rounded-xl"
/>

Google Maps Embed

<PrivacyEmbed 
  url="https://www.google.com/maps/embed?pb=!1m18!1m12..."
  title="Office Location"
  poster="/images/map-placeholder.jpg"
  class="h-96 w-full rounded-lg"
/>

Without Poster Image

<PrivacyEmbed 
  url="https://embed.example.com/content"
  title="Embedded Content"
  class="aspect-video rounded-lg"
/>

Component Behavior

Initial State

  1. Renders a facade <div> with:
    • Poster image (if provided) with opacity effects
    • Play button SVG icon
    • “Load Content” text
    • Cursor pointer for clickability
  2. Hidden iframe container stores URL in data-src attribute

After Click

  1. JavaScript event listener detects click on facade
  2. Reads data-src and data-title from iframe container
  3. Creates <iframe> element dynamically
  4. Sets iframe attributes:
    iframe.src = src;
    iframe.title = title;
    iframe.style.width = "100%";
    iframe.style.height = "100%";
    iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
    iframe.allowFullscreen = true;
    
  5. Appends iframe to container
  6. Hides facade with display: none

Styling

Default Container Classes

<div class="privacy-embed group relative w-full overflow-hidden bg-gray-900 {className}">
  • privacy-embed: Unique identifier for JavaScript targeting
  • group: Tailwind group for hover effects on children
  • relative: Positions facade absolutely within
  • w-full: Full width of parent
  • overflow-hidden: Clips rounded corners
  • bg-gray-900: Dark background before content loads

Facade Styling

The facade includes hover effects:
<button class="load-btn ... group-hover:scale-110 group-hover:bg-white/30">
Hover interactions:
  • Play button scales up 110%
  • Background becomes more opaque
  • Poster image opacity reduces for emphasis

Custom Styling Example

<PrivacyEmbed 
  url="https://example.com/embed"
  title="Content"
  class="aspect-square max-w-md shadow-2xl border-4 border-white"
/>

Accessibility

Semantic HTML

  • Uses <button> for the load trigger (keyboard accessible)
  • Includes descriptive aria-label
  • Proper iframe title attribute for screen readers

Keyboard Support

The facade button responds to:
  • Enter key
  • Space key
  • Mouse click

ARIA Labels

<button aria-label="Load {title}">

Astro Integration

View Transitions Support

The component uses astro:page-load event listener to reinitialize after Astro view transitions.
document.addEventListener("astro:page-load", () => {
  // Reinitialize all privacy embeds on the new page
});
This ensures the component works correctly with Astro’s View Transitions API.

Privacy & Performance

Privacy Benefits

AspectWithout PrivacyEmbedWith PrivacyEmbed
Cookies on loadYes (immediate)No (only after consent)
Third-party scriptsLoaded immediatelyLoaded on demand
User trackingAutomaticOpt-in only
External requests10-20+ per embed1 (poster image)

Performance Impact

Savings per embed:
  • ~500KB-1MB prevented load per iframe
  • 10-20 fewer HTTP requests
  • Reduced JavaScript execution on page load
  • Faster Time to Interactive (TTI)
Page Load Comparison (5 embeds):
  • Without: ~5MB initial load
  • With PrivacyEmbed: ~500KB initial load

Technical Implementation

Data Attributes

The component uses HTML5 data attributes for deferred loading:
<div 
  class="iframe-container"
  data-src="{safeUrl}"
  data-title="{title}"
>
</div>

JavaScript Lifecycle

  1. Page Load: Event listener attached to all .privacy-embed elements
  2. User Click: Facade click event fires
  3. Validation: Check if iframe already exists (prevent double-loading)
  4. Creation: Dynamically create and configure iframe
  5. Injection: Append iframe to container
  6. Cleanup: Hide facade

Prevention of Double-Loading

if (src && !container.querySelector("iframe")) {
  // Only create iframe if one doesn't exist
}

Advanced Use Cases

Multiple Embeds on Page

<div class="grid gap-6 md:grid-cols-2">
  <PrivacyEmbed 
    url="https://youtube.com/embed/video1"
    title="Video 1"
    poster="/poster1.jpg"
    class="aspect-video"
  />
  <PrivacyEmbed 
    url="https://youtube.com/embed/video2"
    title="Video 2"
    poster="/poster2.jpg"
    class="aspect-video"
  />
</div>

Custom Play Button

The SVG play icon can be customized by modifying the component source:
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="currentColor">
  <path d="M8 5v14l11-7z"></path>
</svg>

Build docs developers (and LLMs) love