cta-end

npm: @playerstack/plugin-cta-end

When the video ends, render an overlay with optional title, custom HTML, anchor button (label + URL + UTM), and replay. Hook into the event bus for analytics.

Quickstart

<player-stack
  src="https://youtu.be/dQw4w9WgXcQ"
  data-config='{
    "ctaEnd": {
      "enabled": true,
      "title": "Want the deep dive?",
      "button": {
        "label": "Get the course",
        "url": "https://example.com/course",
        "utm": "utm_source=playerstack&utm_medium=video"
      },
      "showReplay": true
    }
  }'
>
</player-stack>

Config

config.ctaEnd:

FieldTypeDescription
enabledbooleanPlugin is a no-op unless true
titlestring?Heading text inside the overlay
htmlstring?Custom HTML rendered in the overlay (innerHTML — sanitize on your end)
button.labelstringVisible button text
button.urlstringDestination URL
button.utmstring?Query string appended to URL (e.g. utm_source=...)
showReplayboolean?Render a “Replay” button that re-plays the video and dismisses the overlay

Events

NamePayloadWhen
cta:click{ label: string, url: string }User clicks the CTA button
cta:replayundefinedUser clicks the replay button
ctx.events.on("cta:click", ({ label, url }) => {
  navigator.sendBeacon(
    "/api/track",
    JSON.stringify({
      event: "cta_click",
      label,
      url,
    }),
  );
});

Styling

The overlay uses CSS variables you can override:

Use controls-policy to set these per-player, or define them globally in your stylesheet.

HTML safety

config.ctaEnd.html is rendered via innerHTML. Sanitize user-provided content before passing it in. For server-rendered, trusted content, this is fine. For dynamic user input, use title + button instead.