npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fsegurai/marked-extended-embeds

v17.0.0

Published

Marked extension for embedding rich media from various platforms

Readme

An extension library for Marked.js to enhance Markdown rendering.

@fsegurai/marked-extended-embeds Marked extension for embedding rich media from various platforms

🎯 Overview

The marked-extended-embeds extension transforms your Markdown documents into rich, interactive experiences by seamlessly embedding content from 18+ platforms. With automatic platform detection, responsive layouts, privacy controls, and extensive customization options, it's perfect for documentation, blogs, portfolios, and educational content.

✨ Key Features

  • 🎬 18+ Supported Platforms: YouTube, Vimeo, CodePen, Spotify, Twitter, Figma, and more
  • 🔍 Auto-Detection: Automatically identifies platforms from URLs
  • 📐 Responsive Design: Automatic aspect ratio handling (16:9, 4:3, 1:1, 21:9, custom)
  • 🔒 Privacy First: Privacy-enhanced embeds with no-cookie domains
  • 🛡️ Security Built-in: Iframe sandboxing with configurable permissions
  • Performance Optimized: Lazy loading support for better page speed
  • 🎨 Fully Customizable: Custom templates, themes, and styling
  • Accessible: Semantic HTML with ARIA attributes
  • 📱 Mobile Friendly: Responsive layouts that work on all devices
  • 🎛️ Provider Configs: Per-provider customization options
  • 🪝 Callback Hooks: onLoad and onError event handlers
  • 🔧 Framework Agnostic: Works with React, Vue, Angular, Svelte, and vanilla JS

🎪 Live Demo

Experience all 18+ embed types in action: View Demo


Table of contents

Installation

To add @fsegurai/marked-extended-embeds along with Marked.js to your package.json use the following commands.

bun install @fsegurai/marked-extended-embeds marked@>=17.0.0 --save

Usage

Basic Usage

Import @fsegurai/marked-extended-embeds and apply it to your Marked instance as shown below.

Quick Start

Basic Syntax

The extension automatically detects the platform from URLs or you can explicitly specify the provider:

::::embed{title="My Video" aspectRatio="16:9"}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';

// or UMD script
// <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.js"></script>
// <script src="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds/lib/index.umd.js"></script>

marked.use(markedExtendedEmbeds());

### Installation

Install the package using your preferred package manager:

```bash
# Using Bun (recommended)
bun add @fsegurai/marked-extended-embeds

# Using npm
npm install @fsegurai/marked-extended-embeds

# Using yarn
yarn add @fsegurai/marked-extended-embeds

# Using pnpm
pnpm add @fsegurai/marked-extended-embeds

Basic Implementation

import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';

// Import styles (required for functionality)
import '@fsegurai/marked-extended-embeds/styles/embed.css';
// Optional: Import theme for styled appearance
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';

// Register the extension
marked.use(markedExtendedEmbeds());

// Your markdown content with embeds
const markdown = `
# My Document

## Video Tutorial

::::embed{title="Getting Started" aspectRatio="16:9"}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

## Code Example

::::embed{provider="codepen" theme="dark"}
https://codepen.io/username/pen/abc123
::::embedend

## Music Playlist

::::embed{title="Coding Playlist"}
https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M
::::embedend
`;

// Parse and render
const html = marked.parse(markdown);
console.log(html);

Syntax & Usage

Basic Embed Syntax

::::embed{properties}
URL or content
::::embedend

Auto-Detection Examples

The extension automatically detects the platform from the URL:

<!-- YouTube video -->
::::embed{title="Tutorial Video"}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

<!-- Vimeo video -->
::::embed{}
https://vimeo.com/123456789
::::embedend

<!-- CodePen pen -->
::::embed{}
https://codepen.io/username/pen/abc123
::::embedend

<!-- Twitter/X post -->
::::embed{}
https://twitter.com/username/status/1234567890
::::embedend

<!-- Spotify track -->
::::embed{}
https://open.spotify.com/track/abc123
::::embedend

<!-- GitHub Gist -->
::::embed{}
https://gist.github.com/username/abc123def456
::::embedend

Explicit Provider

You can explicitly specify the provider:

::::embed{provider="youtube" title="My Video"}
https://www.youtube.com/watch?v=abc123
::::embedend

::::embed{provider="mermaid" title="System Architecture"}
graph TD;
    A[Client] --> B[Server];
    B --> C[Database];
::::embedend

::::embed{provider="pdf" title="Documentation"}
https://example.com/document.pdf
::::embedend

Common Property Examples

<!-- With aspect ratio -->
::::embed{aspectRatio="16:9"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- With custom dimensions -->
::::embed{width="800px" height="600px"}
https://www.vimeo.com/123456789
::::embedend

<!-- With autoplay and muted -->
::::embed{autoplay="true" muted="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- With privacy mode -->
::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- With theme -->
::::embed{theme="dark"}
https://codepen.io/username/pen/abc123
::::embedend

<!-- With custom start time -->
::::embed{startTime="30"}
https://www.youtube.com/watch?v=abc123
::::embedend

The extension automatically detects providers, applies responsive layouts with proper aspect ratios, and includes
accessibility features. All embeds support lazy loading, sandbox security, and customizable styling.

### Importing Styles

This extension **does not automatically inject styles**. You must import the CSS or SCSS files manually to ensure proper styling.

#### Option 1: Using CSS (Recommended for most projects)

```javascript
// Import minimal structural styles (required for functionality)
import '@fsegurai/marked-extended-embeds/styles/embed.css';

// Optional: Import the basic theme
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';

Or using CDN (vanilla HTML/JavaScript projects):

<!-- Latest version -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds@latest/dist/styles/embed.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/marked-extended-embeds@latest/dist/styles/embed-theme.css">

<!-- Or lock to a specific version (recommended for production) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/[email protected]/dist/styles/embed.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fsegurai/[email protected]/dist/styles/embed-theme.css">

Option 2: Using SCSS (For customization)

// Import structural styles with customizable variables
@import '@fsegurai/marked-extended-embeds/styles/embed.scss';

// Optional: Import theme with customizable variables
@import '@fsegurai/marked-extended-embeds/styles/embed-theme.scss';

// Customize SCSS variables before importing (optional)
$embed-border-radius: 12px;
$embed-box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
@import '@fsegurai/marked-extended-embeds/styles/embed-theme.scss';

Angular Integration

For Angular projects using ngx-markdown:

// In your angular.json, add the CSS file to styles array:
{
  "styles": [
    "node_modules/@fsegurai/marked-extended-embeds/dist/styles/embed.css",
    "node_modules/@fsegurai/marked-extended-embeds/dist/styles/embed-theme.css",
    // ... other styles
  ]
}

// Or import in your global styles.scss:
@import '@fsegurai/marked-extended-embeds/styles/embed.scss';
@import '@fsegurai/marked-extended-embeds/styles/embed-theme.scss';

Styling Your Embeds

This extension follows a separation of concerns approach. The extension generates semantic HTML with CSS classes, while styles are imported separately, giving you full control over the visual appearance.

Generated HTML Structure


<div class="marked-extended-embed-container"
     data-provider="youtube"
     data-embed-id="dQw4w9WgXcQ">
    <!-- Loading state -->
    <div class="marked-extended-embed-loading">
        <div class="marked-extended-embed-spinner"></div>
        <p>Loading...</p>
    </div>
  
    <!-- Embedded content (iframe) -->
    <iframe
            src="https://www.youtube.com/embed/dQw4w9WgXcQ"
            loading="lazy"
            sandbox="allow-scripts allow-same-origin"
            allowfullscreen>
    </iframe>

    <!-- Optional caption -->
    <div class="marked-extended-embed-caption">
        Video description
    </div>
</div>

CSS Classes Reference

| Class | Purpose | Element | |-------|---------|---------| | .marked-extended-embed-container | Main wrapper | Container | | .marked-extended-embed-container[data-provider="youtube"] | Provider-specific styling | Container | | .marked-extended-embed-loading | Loading state overlay | Loading div | | .marked-extended-embed-spinner | Loading spinner animation | Spinner div | | .marked-extended-embed-loading-text | "Loading..." text | Text element | | .marked-extended-embed-caption | Caption/description | Caption div | | iframe | Embedded content | Iframe |

Complete Styling Example

/* Base Container */
.marked-extended-embed-container {
  position: relative;
  width: 100%;
  margin: 1.5rem 0;
  border-radius: 8px;
  overflow: hidden;
  background: #f5f5f5;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* Responsive aspect ratio wrapper */
.marked-extended-embed-container::before {
  content: '';
  display: block;
  padding-top: 56.25%; /* 16:9 aspect ratio */
}

.marked-extended-embed-container iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
}

/* Loading State */
.marked-extended-embed-loading {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.05);
  z-index: 1;
}

.marked-extended-embed-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #e0e0e0;
  border-top-color: #2196F3;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.marked-extended-embed-loading-text {
  margin-top: 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: #666;
}

/* Caption Styling */
.marked-extended-embed-caption {
  padding: 0.75rem 1rem;
  font-size: 0.875rem;
  color: #666;
  background: #fafafa;
  border-top: 1px solid #e0e0e0;
  text-align: center;
}

/* Provider-Specific Backgrounds */
.marked-extended-embed-container[data-provider="youtube"] {
  background: #000;
}

.marked-extended-embed-container[data-provider="vimeo"] {
  background: #000;
}

.marked-extended-embed-container[data-provider="codepen"] {
  background: #1e1e1e;
}

.marked-extended-embed-container[data-provider="codesandbox"] {
  background: #151515;
}

.marked-extended-embed-container[data-provider="spotify"] {
  background: #191414;
}

.marked-extended-embed-container[data-provider="soundcloud"] {
  background: #ff5500;
}

.marked-extended-embed-container[data-provider="twitter"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="github-gist"] {
  background: #f6f8fa;
}

.marked-extended-embed-container[data-provider="figma"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="mermaid"] {
  background: #fff;
  padding: 1rem;
}

.marked-extended-embed-container[data-provider="pdf"] {
  background: #525659;
}

.marked-extended-embed-container[data-provider="loom"] {
  background: #000;
}

.marked-extended-embed-container[data-provider="miro"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="excalidraw"] {
  background: #fff;
}

.marked-extended-embed-container[data-provider="drawio"],
.marked-extended-embed-container[data-provider="diagrams-net"] {
  background: #fff;
}

Dark Mode Support

/* Light theme */
body.light .marked-extended-embed-container {
  background: #f5f5f5;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

body.light .marked-extended-embed-caption {
  background: #fafafa;
  color: #666;
  border-top-color: #e0e0e0;
}

body.light .marked-extended-embed-loading {
  color: #666;
}

body.light .marked-extended-embed-spinner {
  border-color: #e0e0e0;
  border-top-color: #2196F3;
}

/* Dark theme */
body.dark .marked-extended-embed-container {
  background: #1e1e1e;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

body.dark .marked-extended-embed-caption {
  background: #2a2a2a;
  color: #e0e0e0;
  border-top-color: #3a3a3a;
}

body.dark .marked-extended-embed-loading {
  color: #ccc;
}

body.dark .marked-extended-embed-spinner {
  border-color: #333;
  border-top-color: #2196F3;
}

/* Provider-specific dark mode overrides */
body.dark .marked-extended-embed-container[data-provider="mermaid"],
body.dark .marked-extended-embed-container[data-provider="twitter"],
body.dark .marked-extended-embed-container[data-provider="figma"],
body.dark .marked-extended-embed-container[data-provider="miro"],
body.dark .marked-extended-embed-container[data-provider="excalidraw"],
body.dark .marked-extended-embed-container[data-provider="drawio"],
body.dark .marked-extended-embed-container[data-provider="diagrams-net"] {
  background: #1e1e1e;
}

body.dark .marked-extended-embed-container[data-provider="github-gist"] {
  background: #0d1117;
}

Custom Aspect Ratios

/* 16:9 (default - videos) */
.marked-extended-embed-container[data-provider="youtube"]::before,
.marked-extended-embed-container[data-provider="vimeo"]::before,
.marked-extended-embed-container[data-provider="loom"]::before {
  padding-top: 56.25%;
}

/* 1:1 (square - music players) */
.marked-extended-embed-container[data-provider="spotify"]::before,
.marked-extended-embed-container[data-provider="soundcloud"]::before {
  padding-top: 100%;
}

/* 4:3 (presentations) */
.marked-extended-embed-container[data-provider="google-slides"]::before {
  padding-top: 75%;
}

/* Full height (code editors) */
.marked-extended-embed-container[data-provider="codepen"]::before,
.marked-extended-embed-container[data-provider="codesandbox"]::before,
.marked-extended-embed-container[data-provider="replit"]::before {
  padding-top: 100%;
  min-height: 500px;
}

Responsive Adjustments

@media (max-width: 768px) {
  .marked-extended-embed-container {
    margin: 1rem 0;
    border-radius: 4px;
  }

  .marked-extended-embed-caption {
    padding: 0.5rem 0.75rem;
    font-size: 0.8125rem;
  }

  /* Reduce height for code editors on mobile */
  .marked-extended-embed-container[data-provider="codepen"]::before,
  .marked-extended-embed-container[data-provider="codesandbox"]::before {
    min-height: 400px;
  }
}

Print Styles

@media print {
  .marked-extended-embed-container {
    break-inside: avoid;
    box-shadow: none;
    border: 1px solid #ccc;
  }

  .marked-extended-embed-loading {
    display: none;
  }

  /* Show caption */
  .marked-extended-embed-caption {
    display: block !important;
  }

  /* Hide interactive embeds, show placeholder */
  .marked-extended-embed-container iframe {
    display: none;
  }

  .marked-extended-embed-container::after {
    content: "Embedded content: " attr(data-provider);
    display: block;
    padding: 2rem;
    text-align: center;
    color: #666;
    font-style: italic;
  }
}

Copy Demo Theme

For complete styling with all 18+ providers: embed-theme.css

Check the demo to see all embed types in action.

Supported Platforms

The extension supports 18 different platforms with automatic URL detection:

| Platform | Type | Example URL | |------------------|---------------|-------------------------------| | YouTube | Video | youtube.com/watch?v=... | | Vimeo | Video | vimeo.com/123456789 | | CodePen | Code | codepen.io/user/pen/abc | | CodeSandbox | Code | codesandbox.io/s/abc | | Twitter | Social | twitter.com/user/status/123 | | GitHub Gist | Code | gist.github.com/user/abc | | Spotify | Music | open.spotify.com/track/abc | | SoundCloud | Music | soundcloud.com/user/track | | SlideShare | Presentation | slideshare.net/user/slides | | Figma | Design | figma.com/file/abc | | Loom | Video | loom.com/share/abc | | Miro | Collaboration | miro.com/app/board/abc | | Mermaid | Diagram | mermaid://graph TD; A-->B; | | Excalidraw | Drawing | excalidraw.com/#room=abc | | Draw.io | Diagram | diagrams.net/... | | Diagrams.net | Diagram | diagrams.net/... | | PDF | Document | Any PDF URL | | iframe | Generic | Any URL |

Each platform is automatically detected from the URL pattern. You can also explicitly specify the provider using the provider property.

Configuration Options

The marked-extended-embeds extension accepts the following configuration options:

  • className: The base CSS class name for embeds. Defaults to 'marked-extended-embed.'
  • prefixId: The prefix ID for embed elements. Defaults to 'embed'.
  • defaultAspectRatio: Default aspect ratio ('16:9', '4:3', '1:1', '21:9'). Defaults to '16:9'.
  • allowFullscreen: Allow fullscreen mode for embeds. Defaults to true.
  • lazyLoad: Enable lazy loading for better performance. Defaults to true.
  • privacyMode: Use privacy-enhanced domains when available. Defaults to false.
  • template: Custom HTML template for rendering embeds. Defaults to built-in template.
  • customizeToken: Function to customize embed tokens. Defaults to null.
  • enableSandbox: Enable iframe sandbox for security. Defaults to true. to ['allow-scripts', 'allow-same-origin', 'allow-popups', 'allow-presentation'].
  • providers: Custom provider configurations. Defaults to {}.
  • onEmbedLoad: Callback when embed loads. Defaults to null.
  • onEmbedError: Callback when embed fails. Defaults to null.

Note: For styling, import CSS/SCSS files manually (see Importing Styles).

Embed Properties

Embed syntax supports the following properties:

Common Properties:

  • title: Title/caption for the embed
  • aspectRatio: Aspect ratio ('16:9', '4:3', '1:1', '21:9', 'custom')
  • width: Custom width (e.g., '800px')
  • height: Custom height (e.g., '600px')
  • provider: Explicit provider name
  • id: Custom identifier for the embed
  • className: Custom CSS class

Media Properties:

  • autoplay: Auto-play video/audio (boolean)
  • muted: Start muted (boolean)
  • loop: Loop playback (boolean)
  • startTime: Start time for videos (e.g., '30')
  • controls: Show media controls (boolean)

Display Properties:

  • lazyLoad: Enable lazy loading (boolean)
  • allowFullscreen: Allow fullscreen mode (boolean)
  • privacyMode: Use privacy-enhanced mode (boolean)
  • theme: Theme setting ('light', 'dark')

Custom Parameters:

  • param-*: Any property starting with 'param-' becomes a URL parameter

Advanced Examples

YouTube Video with Options

::::embed{
title="Tutorial Video"
aspectRatio="16:9"
autoplay="true"
muted="true"
startTime="30"
privacyMode="true"
}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

CodePen with Dark Theme

::::embed{title="Interactive Demo" theme="dark"}
https://codepen.io/username/pen/abc123
::::embedend

Responsive Design File

::::embed{
title="Mobile App Design"
aspectRatio="4:3"
width="100%"
}
https://www.figma.com/file/abc123/Mobile-App
::::embedend

Spotify Playlist

::::embed{title="Coding Playlist" aspectRatio="1:1"}
https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M
::::embedend

CodeSandbox Project

::::embed{
title="React Example"
theme="dark"
param-view="preview"
}
https://codesandbox.io/s/react-new
::::embedend

Mermaid Diagram

::::embed{provider="mermaid" title="System Architecture"}
mermaid://graph TD;
A[Client] -->|Request| B[API Gateway];
B --> C[Service 1];
B --> D[Service 2];
C --> E[Database];
D --> E;
::::embedend

PDF Document

::::embed{
provider="pdf"
title="Technical Specification"
aspectRatio="4:3"
}
https://example.com/document.pdf
::::embedend

Loom Video with Custom Size

::::embed{
title="Product Demo"
width="800px"
height="450px"
}
https://www.loom.com/share/abc123
::::embedend

Twitter Thread

::::embed{title="Latest Announcement"}
https://twitter.com/username/status/1234567890
::::embedend

GitHub Gist

::::embed{title="Code Snippet"}
https://gist.github.com/username/abc123
::::embedend

Excalidraw Sketch

::::embed{title="Whiteboard Sketch" aspectRatio="16:9"}
https://excalidraw.com/#room=abc123,def456
::::embedend

Draw.io Diagram

::::embed{provider="drawio" title="System Architecture" aspectRatio="4:3"}
https://viewer.diagrams.net/?highlight=0000ff&edit=_blank&title=diagram
::::embedend

Privacy Mode

Enable privacy-enhanced embedding for supported platforms:

::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

This uses youtube-nocookie.com for YouTube and similar privacy-focused domains when available.

Custom Provider Configuration

Configure provider-specific settings:

marked.use(markedExtendedEmbeds({
    providers: {
        youtube: {
            domain: 'www.youtube-nocookie.com',
            params: {
                rel: '0',
                modestbranding: '1',
            },
        },
        spotify: {
            params: {
                theme: '0',
            },
        },
    },
}));

Security Features

The extension includes several security features:

  • Sandbox by default: Iframes use sandbox attribute with restricted permissions
  • Configurable permissions: Customize allowed iframe capabilities
  • Privacy mode: Use no-cookie domains when available
  • XSS protection: All user input is escaped
  • HTTPS enforcement: Embed URLs use secure protocols

Accessibility

All embeds include proper accessibility features:

  • Semantic HTML with proper roles
  • Alt text and titles
  • Keyboard navigation support
  • Screen reader compatibility
  • Loading states with aria-labels

Platform-Specific Guides

Video Platforms

YouTube

<!-- Basic -->
::::embed{}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

<!-- With options -->
::::embed{
  autoplay="true"
  muted="true"
  startTime="30"
  privacyMode="true"
  param-rel="0"
  param-modestbranding="1"
}
https://www.youtube.com/watch?v=dQw4w9WgXcQ
::::embedend

<!-- YouTube Shorts -->
::::embed{}
https://www.youtube.com/shorts/abc123
::::embedend

Vimeo

<!-- Basic -->
::::embed{}
https://vimeo.com/123456789
::::embedend

<!-- With options -->
::::embed{
  autoplay="true"
  loop="true"
  param-color="ff0000"
  param-portrait="0"
}
https://vimeo.com/123456789
::::embedend

Loom

::::embed{title="Product Demo"}
https://www.loom.com/share/abc123def456
::::embedend

Code Platforms

CodePen

<!-- Basic -->
::::embed{}
https://codepen.io/username/pen/abc123
::::embedend

<!-- With theme -->
::::embed{
  theme="dark"
  param-default-tab="js,result"
  param-editable="true"
}
https://codepen.io/username/pen/abc123
::::embedend

CodeSandbox

::::embed{
  theme="dark"
  param-view="preview"
  param-hidenavigation="1"
}
https://codesandbox.io/s/react-new
::::embedend

GitHub Gist

::::embed{title="Code Snippet"}
https://gist.github.com/username/abc123def456
::::embedend

Music & Audio Platforms

Spotify

<!-- Track -->
::::embed{aspectRatio="1:1"}
https://open.spotify.com/track/abc123
::::embedend

<!-- Album -->
::::embed{}
https://open.spotify.com/album/abc123
::::embedend

<!-- Playlist -->
::::embed{}
https://open.spotify.com/playlist/abc123
::::embedend

<!-- Podcast -->
::::embed{}
https://open.spotify.com/episode/abc123
::::embedend

SoundCloud

::::embed{param-color="ff5500"}
https://soundcloud.com/artist/track-name
::::embedend

Social Media

Twitter/X

::::embed{title="Latest Announcement"}
https://twitter.com/username/status/1234567890
::::embedend

<!-- Also supports X.com URLs -->
::::embed{}
https://x.com/username/status/1234567890
::::embedend

Design & Collaboration Tools

Figma

::::embed{
  title="Mobile App Design"
  aspectRatio="4:3"
}
https://www.figma.com/file/abc123/Project-Name
::::embedend

<!-- Prototype view -->
::::embed{}
https://www.figma.com/proto/abc123
::::embedend

Miro

::::embed{title="Brainstorm Board"}
https://miro.com/app/board/abc123
::::embedend

Excalidraw

::::embed{title="Whiteboard Sketch"}
https://excalidraw.com/#room=abc123,def456
::::embedend

Diagram Tools

Mermaid

::::embed{provider="mermaid" title="System Flow"}
graph TD;
    A[Start] --> B{Decision};
    B -->|Yes| C[Process 1];
    B -->|No| D[Process 2];
    C --> E[End];
    D --> E;
::::embedend

<!-- Sequence diagram -->
::::embed{provider="mermaid"}
sequenceDiagram
    Alice->>John: Hello John!
    John-->>Alice: Hi Alice!
::::embedend

<!-- Gantt chart -->
::::embed{provider="mermaid"}
gantt
    title Project Timeline
    section Phase 1
    Task 1: 2026-01-01, 30d
    Task 2: 2026-02-01, 20d
::::embedend

Draw.io / Diagrams.net

::::embed{provider="drawio" aspectRatio="4:3"}
https://viewer.diagrams.net/?highlight=0000ff&title=architecture
::::embedend

Documents

PDF

::::embed{
  provider="pdf"
  title="Technical Specification"
  aspectRatio="4:3"
}
https://example.com/documents/spec.pdf
::::embedend

SlideShare

::::embed{title="Conference Presentation"}
https://www.slideshare.net/username/presentation-title
::::embedend

Generic Iframe

Custom Embed

::::embed{
  provider="iframe"
  title="Custom Content"
  aspectRatio="16:9"
}
https://example.com/custom-embed
::::embedend

Best Practices

1. Choose Appropriate Aspect Ratios

Match aspect ratio to content type:

<!-- Videos: 16:9 (default) -->
::::embed{aspectRatio="16:9"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Presentations: 4:3 -->
::::embed{aspectRatio="4:3"}
https://www.slideshare.net/user/presentation
::::embedend

<!-- Music players: 1:1 (square) -->
::::embed{aspectRatio="1:1"}
https://open.spotify.com/track/abc123
::::embedend

<!-- Ultra-wide content: 21:9 -->
::::embed{aspectRatio="21:9"}
https://example.com/ultrawide-content
::::embedend

2. Use Privacy Mode for User Privacy

Enable privacy mode for platforms that support it:

<!-- Good: Privacy-conscious -->
::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Uses youtube-nocookie.com instead of youtube.com -->

3. Implement Lazy Loading

Improve page performance with lazy loading:

marked.use(markedExtendedEmbeds({
  lazyLoad: true  // Default is true
}));
<!-- Lazy loading enabled by default -->
::::embed{}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Explicitly disable for above-the-fold content -->
::::embed{lazyLoad="false"}
https://www.youtube.com/watch?v=abc123
::::embedend

4. Add Meaningful Titles

Always provide descriptive titles for accessibility:

<!-- Good: Descriptive title -->
::::embed{title="React Hooks Tutorial - Complete Guide"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Avoid: Generic title -->
::::embed{title="Video"}
https://www.youtube.com/watch?v=abc123
::::embedend

5. Control Autoplay Wisely

Use autoplay sparingly and always with muted:

<!-- Good: Autoplay with muted -->
::::embed{autoplay="true" muted="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Avoid: Autoplay without muted (poor UX) -->
::::embed{autoplay="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

6. Optimize for Mobile

Test embeds on mobile devices:

/* Add mobile-specific styles */
@media (max-width: 768px) {
  .marked-extended-embed-container {
    margin: 1rem 0;
  }
  
  /* Reduce height for code editors on mobile */
  .marked-extended-embed-container[data-provider="codepen"],
  .marked-extended-embed-container[data-provider="codesandbox"] {
    min-height: 400px;
  }
}

7. Use Sandbox for Security

Keep sandbox enabled (default) for security:

// Good: Sandbox enabled (default)
marked.use(markedExtendedEmbeds({
  enableSandbox: true,
  sandboxPermissions: [
    'allow-scripts',
    'allow-same-origin',
    'allow-popups',
    'allow-presentation'
  ]
}));

// Only disable if absolutely necessary

Troubleshooting

Embeds Not Displaying

Problem: Embeds don't appear on the page.

Solutions:

  1. Verify syntax is correct:
<!-- Correct -->
::::embed{}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Wrong -->
:::embed{}  <!-- ❌ Missing colon -->
https://www.youtube.com/watch?v=abc123
:::embedend
  1. Check if provider is supported:
// List of supported providers
const supported = [
  'youtube', 'vimeo', 'twitter', 'codepen', 'codesandbox',
  'github-gist', 'spotify', 'soundcloud', 'slideshare',
  'figma', 'loom', 'miro', 'mermaid', 'excalidraw',
  'drawio', 'diagrams-net', 'pdf', 'iframe'
];
  1. Verify URL format:
<!-- YouTube - multiple formats supported -->
https://www.youtube.com/watch?v=abc123  ✅
https://youtu.be/abc123                  ✅
https://www.youtube.com/embed/abc123     ✅
https://www.youtube.com/shorts/abc123    ✅
  1. Import styles:
import '@fsegurai/marked-extended-embeds/styles/embed.css';

Styling Not Applied

Problem: Embeds appear unstyled or broken.

Solutions:

  1. Import CSS files:
import '@fsegurai/marked-extended-embeds/styles/embed.css';
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
  1. Check CSS file paths in build config

  2. Verify CSS is loaded (check DevTools Network tab)

  3. For CDN usage, check for 404 errors

Incorrect Aspect Ratio

Problem: Embed appears stretched or squashed.

Solution:

<!-- Specify correct aspect ratio -->
::::embed{aspectRatio="16:9"}  <!-- Videos -->
https://www.youtube.com/watch?v=abc123
::::embedend

::::embed{aspectRatio="1:1"}  <!-- Music players -->
https://open.spotify.com/track/abc123
::::embedend

::::embed{aspectRatio="4:3"}  <!-- Presentations -->
https://www.slideshare.net/user/slides
::::embedend

<!-- Or use custom dimensions -->
::::embed{width="800px" height="600px"}
https://example.com/content
::::embedend

Privacy Mode Not Working

Problem: Privacy mode doesn't activate.

Solutions:

  1. Check if provider supports privacy mode:
<!-- Supported: YouTube, Vimeo -->
::::embed{privacyMode="true"}
https://www.youtube.com/watch?v=abc123
::::embedend

<!-- Not all providers support privacy mode -->
  1. Configure globally:
marked.use(markedExtendedEmbeds({
  privacyMode: true  // Enable for all embeds
}));

Lazy Loading Not Working

Problem: All embeds load immediately.

Solutions:

  1. Ensure lazy loading is enabled:
marked.use(markedExtendedEmbeds({
  lazyLoad: true  // Default is true
}));
  1. Check browser support:
<!-- Fallback for older browsers -->
<script>
if ('loading' in HTMLIFrameElement.prototype) {
  // Lazy loading supported
} else {
  // Load polyfill or fallback
}
</script>

Autoplay Not Working

Problem: Videos don't autoplay.

Solutions:

  1. Always use muted with autoplay:
<!-- Modern browsers require muted for autoplay -->
::::embed{autoplay="true" muted="true"}
https://www.youtube.com/watch?v=abc123
::::embedend
  1. Check browser autoplay policies:
  • Chrome: Requires muted or user interaction
  • Safari: Requires muted
  • Firefox: Respects user preferences

Sandbox Restrictions

Problem: Embed functionality limited due to sandbox.

Solution:

// Customize sandbox permissions
marked.use(markedExtendedEmbeds({
  enableSandbox: true,
  sandboxPermissions: [
    'allow-scripts',
    'allow-same-origin',
    'allow-popups',
    'allow-presentation',
    'allow-forms',  // Add if forms needed
    'allow-modals'  // Add if modals needed
  ]
}));

Framework Integration

React Integration

// EmbedMarkdown.tsx
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';
import '@fsegurai/marked-extended-embeds/styles/embed.css';
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
import { useEffect, useState, useCallback } from 'react';

// Configure marked
marked.use(markedExtendedEmbeds({
  lazyLoad: true,
  privacyMode: true,
  onEmbedLoad: (id, provider) => {
    console.log(\`Embed loaded: \${provider} (\${id})\`);
  },
  onEmbedError: (id, provider, error) => {
    console.error(\`Embed error: \${provider} (\${id})\`, error);
  }
}));

interface Props {
  content: string;
  enablePrivacy?: boolean;
}

export function EmbedMarkdown({ content, enablePrivacy = true }: Props) {
  const [html, setHtml] = useState('');
  
  useEffect(() => {
    // Reconfigure based on props
    marked.use(markedExtendedEmbeds({
      privacyMode: enablePrivacy,
      lazyLoad: true
    }));
    
    const parsed = marked.parse(content);
    setHtml(parsed);
  }, [content, enablePrivacy]);
  
  return (
    <div 
      className="markdown-with-embeds"
      dangerouslySetInnerHTML={{ __html: html }} 
    />
  );
}

// Usage:
// <EmbedMarkdown content={markdown} enablePrivacy={true} />

Vue 3 Integration

<script setup lang="ts">
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';
import '@fsegurai/marked-extended-embeds/styles/embed.css';
import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
import { computed, watch } from 'vue';

marked.use(markedExtendedEmbeds({
  lazyLoad: true,
  privacyMode: true,
  onEmbedLoad: (id, provider) => {
    console.log(\`Loaded: \${provider}\`);
  }
}));

interface Props {
  content: string;
  privacyMode?: boolean;
  lazyLoad?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  privacyMode: true,
  lazyLoad: true
});

const html = computed(() => {
  marked.use(markedExtendedEmbeds({
    privacyMode: props.privacyMode,
    lazyLoad: props.lazyLoad
  }));
  return marked.parse(props.content);
});

watch([() => props.privacyMode, () => props.lazyLoad], () => {
  console.log('Config changed');
});
</script>

<template>
  <div class="markdown-content" v-html="html" />
</template>

Angular Integration

// embed-markdown.component.ts
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { marked } from 'marked';
import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';

marked.use(markedExtendedEmbeds({
  lazyLoad: true,
  privacyMode: true,
  onEmbedLoad: (id, provider) => {
    console.log(\`Embed loaded: \${provider}\`);
  }
}));

@Component({
  selector: 'app-embed-markdown',
  template: \`<div [innerHTML]="parsedContent"></div>\`,
  styleUrls: [
    '../node_modules/@fsegurai/marked-extended-embeds/styles/embed.css',
    '../node_modules/@fsegurai/marked-extended-embeds/styles/embed-theme.css'
  ]
})
export class EmbedMarkdownComponent implements OnChanges {
  @Input() content: string = '';
  @Input() privacyMode: boolean = true;
  @Input() lazyLoad: boolean = true;
  parsedContent: SafeHtml = '';

  constructor(private sanitizer: DomSanitizer) {}

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['content'] || changes['privacyMode'] || changes['lazyLoad']) {
      marked.use(markedExtendedEmbeds({
        privacyMode: this.privacyMode,
        lazyLoad: this.lazyLoad
      }));
      
      const html = marked.parse(this.content);
      this.parsedContent = this.sanitizer.bypassSecurityTrustHtml(html);
    }
  }
}

Svelte Integration

<script lang="ts">
  import { marked } from 'marked';
  import markedExtendedEmbeds from '@fsegurai/marked-extended-embeds';
  import '@fsegurai/marked-extended-embeds/styles/embed.css';
  import '@fsegurai/marked-extended-embeds/styles/embed-theme.css';
  
  marked.use(markedExtendedEmbeds({
    lazyLoad: true,
    privacyMode: true,
    onEmbedLoad: (id, provider) => {
      console.log(\`Loaded: \${provider}\`);
    }
  }));
  
  export let content: string;
  export let privacyMode: boolean = true;
  
  $: html = (() => {
    marked.use(markedExtendedEmbeds({
      privacyMode
    }));
    return marked.parse(content);
  })();
</script>

<div class="markdown-content">
  {@html html}
</div>

Performance Optimization

1. Enable Lazy Loading

marked.use(markedExtendedEmbeds({
  lazyLoad: true  // Defers loading until visible
}));

2. Use Intersection Observer

// Custom intersection observer for advanced control
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const iframe = entry.target.querySelector('iframe');
      if (iframe && iframe.dataset.src) {
        iframe.src = iframe.dataset.src;
        delete iframe.dataset.src;
      }
    }
  });
});

document.querySelectorAll('.marked-extended-embed-container').forEach(embed => {
  observer.observe(embed);
});

3. Optimize for Multiple Embeds

<!-- Load only above-the-fold embeds immediately -->
::::embed{lazyLoad="false"}
https://www.youtube.com/watch?v=first-video
::::embedend

<!-- Lazy load below-the-fold embeds -->
::::embed{lazyLoad="true"}
https://www.youtube.com/watch?v=second-video
::::embedend

::::embed{lazyLoad="true"}
https://www.youtube.com/watch?v=third-video
::::embedend

4. Use Facade Pattern

// Load lightweight preview, full embed on click
marked.use(markedExtendedEmbeds({
  customizeToken: (token) => {
    // Add click-to-load functionality
    token.meta.useFacade = true;
  }
}));

Contributing

Found a bug or have a feature request? Please open an issue on GitHub.

Related Resources

Available Extensions

| Extension | Package | Version | Description | |-------------|--------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|----------------------------------------------------------------------| | All - Bundle | @fsegurai/marked-extended-bundle | npm | Includes all extensions in a single package for easy integration | | Accordion | @fsegurai/marked-extended-accordion | npm | Add collapsible accordion sections to your markdown | | Alert | @fsegurai/marked-extended-alert | npm | Create styled alert boxes for important information | | Comments | @fsegurai/marked-extended-comments | npm | Add comment sections with author and timestamp metadata | | Embeds | @fsegurai/marked-extended-embeds | npm | Easily embed content from various platforms (YouTube, Twitter, etc.) | | Footnote | @fsegurai/marked-extended-footnote | npm | Add footnotes with automatic numbering | | Kanban | @fsegurai/marked-extended-kanban | npm | Create kanban boards with customizable columns and cards | | Lists | @fsegurai/marked-extended-lists | npm | Enhanced list formatting options | | Slide | @fsegurai/marked-extended-slide | npm | Create slide decks directly from markdown content | | Spoiler | @fsegurai/marked-extended-spoiler | npm | Hide content behind spoiler tags | | Tables | @fsegurai/marked-extended-tables | npm | Advanced table formatting with cell spanning | | Tabs | @fsegurai/marked-extended-tabs | npm | Create tabbed content sections | | Timeline | @fsegurai/marked-extended-timeline | npm | Display content in an interactive timeline format | | Typographic | @fsegurai/marked-extended-typographic | npm | Improve typography with smart quotes, dashes, and more |

Demo Application

To see all extensions in action, check out the [DEMO].

To set up the demo locally, follow the next steps:

git clone https://github.com/fsegurai/marked-extensions.git
bun install
bun start

This will serve the application locally at http://[::1]:8000.

License

Licensed under MIT.