Features

Video and audio embeds

Turn YouTube, Vimeo and Spotify links into safe players, with the preview card, activateEmbeds, server checks and CSP settings.

@viveeditor/ext-embed turns YouTube, Vimeo and Spotify links into players. The saved HTML never keeps the address it was given; it stores only the provider and the video id, and every player address is rebuilt from them. This page covers setup, loading players on the published page, and the server and Content Security Policy settings.

Setup

import { Editor } from '@viveeditor/core'
import { Embed } from '@viveeditor/ext-embed'
import '@viveeditor/ext-embed/style.css'

const editor = await Editor.create(document.querySelector('#editor'), {
  extensions: [Embed.configure({ providers: ['youtube', 'vimeo'], facade: true })],
})

The one-script build already includes the extension with all three providers.

Option Default Description
providers ['youtube', 'vimeo', 'spotify'] The enabled providers.
facade false With true, the saved HTML holds a link instead of a player.
pasteToEmbed true Turns a link pasted on an empty line into a player.
maxEmbeds 50 The maximum number of players in one document.
labels English Labels for the play button and for unsupported content.

Add a video

  • Paste just the link on an empty line. It turns into a player; one undo turns it back into a link.
  • Press the video button in the toolbar or the “Video” entry in the / menu. Enter the link and a title in the panel; screen readers announce that title.
  • In code, use editor.commands.insertEmbed(url) to insert and editor.commands.setEmbedTitle(title) to change the title.

Inside the editor a card stands in for the player, with the provider name, the title and a play button. Nothing loads from the provider until play is pressed.

Provider Accepted links Player address
YouTube youtube.com/watch?v=…, youtu.be/…, /shorts/, /live/ and /embed/ addresses; start time from t or start www.youtube-nocookie.com
Vimeo vimeo.com/…, addresses with the private key of unlisted videos, player.vimeo.com/video/…; start time from #t= player.vimeo.com, with dnt=1
Spotify Track, album, playlist, episode, show and artist addresses on open.spotify.com open.spotify.com/embed

Links are parsed as addresses and the host name is compared exactly. Short links and redirects are never followed, and nothing is requested from the provider.

Saved HTML

The player is always rebuilt from the provider and id; sandbox, allow and referrerpolicy are fixed and never read from the content. The saved HTML holds a div[data-embed] wrapper in one of two forms:

  • facade: false (default): the wrapper holds the player iframe. The player loads from the provider when the reader opens the page.
  • facade: true: the wrapper holds a link to the video’s page at the provider. The player loads when the reader clicks.

When you save as Word or Markdown, players are written as links.

Publish facade output

On the page that shows content saved with facade: true, call activateEmbeds(). Clicking the link replaces it with the player and starts playback:

import { activateEmbeds } from '@viveeditor/ext-embed/activate'

activateEmbeds(document.querySelector('article'), { providers: ['youtube', 'vimeo'] })

The first argument is the root element to search (the whole document if left out), and providers lists the allowed providers. The player address is built from the provider and id on the wrapper, never from the link’s href. Without activateEmbeds, the link opens the video on the provider’s site.

On the server

  • Clean the HTML on the server too, but never allow every iframe. Pass embeds: true to @viveeditor/sanitize; players are rebuilt from the provider and id. See Server sanitizing.
  • With your own sanitizer, rebuild players from div[data-embed], or accept only addresses for which isAllowedEmbedSrc() returns true.
import { isAllowedEmbedSrc } from '@viveeditor/ext-embed'

const allowed = isAllowedEmbedSrc(src, ['youtube', 'vimeo'])

isAllowedEmbedSrc checks that the address is exactly the player address the editor builds. In the same package, matchEmbedUrl(url) reads the provider and id from a link, and buildEmbedSrc(attrs) builds the player address. None of the three needs a browser DOM.

Content Security Policy and privacy

  • List only the providers you enabled in your CSP frame-src. With all three: frame-src https://www.youtube-nocookie.com https://player.vimeo.com https://open.spotify.com.
  • A player loads content and cookies from the provider. Tell readers about it where your consent rules require it; facade output loads the player only after a click.

Referrer-Policy

A page-wide Referrer-Policy: no-referrer stops YouTube players from working. This is why players are built with strict-origin-when-cross-origin.

For the other CSP settings, see Security and CSP.

Why does the video not load right away in the editor?

Inside the editor a card stands in for the player. Nothing loads from the provider until someone presses play, which protects the writer's privacy and keeps the page light.

Can I embed videos from other sites?

No. Only YouTube, Vimeo and Spotify are supported. Other links are not turned into players, and the panel lists the supported providers.

My page sends Referrer-Policy no-referrer. Why does YouTube not work?

YouTube rejects embedded players that arrive without a Referer. A page-wide no-referrer policy stops YouTube players from working.

Last updated: