Embed YouTube videos without tracking cookies
@slashclone/privacy-friendly-youtube-embed
Embed YouTube videos on your site without loading anything from Google until the visitor presses Play or allows videos. A click-to-play placeholder with a local poster swaps in the youtube-nocookie.com player, with a keyboard-friendly button, no layout shift, CSP entries, and a Playwright test that proves no YouTube request happens first. React and HTML templates. Not legal advice.
SkillDevOpsv0.1.1 (latest)
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
- 1// Adds the YouTube player to a Content Security Policy only when the site uses
- 2// embeds. A site with no videos should not allow the host at all.
- 3//
- 4// Usage, wherever you build your CSP header:
- 5// const csp = withYouTube(baseDirectives, { enabled: siteHasVideoEmbeds });
- 6// const header = serializeCsp(csp);
- 8export type CspDirectives = Record<string, string[]>;
- 10export type YouTubeCspOptions = {
- 11 /** True when at least one page renders a YouTube embed. */
- 12 enabled: boolean;
- 13 /**
- 14 * True only if you show YouTube thumbnails after consent. The templates in this
- 15 * kit use a local poster, so the default is false and img-src is untouched.
- 16 */
- 17 thumbnailsAfterConsent?: boolean;
- 18};
- 20const PLAYER = "https://www.youtube-nocookie.com";
- 21const THUMBNAILS = "https://i.ytimg.com";
- 23function add(csp: CspDirectives, directive: string, source: string): void {
- 24 const list = csp[directive] ?? ["'self'"];
- 25 if (!list.includes(source)) list.push(source);
- 26 csp[directive] = list;
- 27}
- 29/** Returns a copy of `base` with the YouTube hosts added when enabled. */
- 30export function withYouTube(base: CspDirectives, options: YouTubeCspOptions): CspDirectives {
- 31 const csp: CspDirectives = Object.fromEntries(Object.entries(base).map(([k, v]) => [k, [...v]]));
- 32 if (!options.enabled) return csp;
- 33 // The iframe is the only thing the page itself loads. Scripts, media, and
- 34 // connections inside the player run under YouTube's origin, not your CSP.
- 35 add(csp, "frame-src", PLAYER);
- 36 if (options.thumbnailsAfterConsent) add(csp, "img-src", THUMBNAILS);
- 37 return csp;
- 38}
- 40export function serializeCsp(csp: CspDirectives): string {
- 41 return Object.entries(csp)
- 42 .map(([directive, sources]) => [directive, ...sources].join(" "))
- 43 .join("; ");
- 44}
