Inserting Media and Embeds

Every media control lives on the editor toolbar, and everything you insert comes out of your asset library — so uploading a file and placing it on a page are the same flow approached from two directions. Pick from the library and the file is already there; drag a file onto the canvas and it is uploaded, added to the library, and placed where you let go, in one gesture.

Each embed then has a settings dialog. The dialogs are generous; the published site is not. This page tells you, embed by embed, which fields survive the trip from the editor to a reader’s browser.

Insert Asset

Insert Asset on the toolbar opens the asset picker over the canvas. It lists everything in your library with a search box (matching the asset’s name), a type filter reading All Types / Images / Videos / Audio / Documents, and label filtering. Choosing an asset closes the picker and inserts it at the cursor.

The asset picker modal open over the page editor, with a search term typed and a grid of image and video thumbnails

What gets inserted depends on the asset’s type — you do not choose a block, the type chooses it for you:

Asset typeWhat lands in the pageThen
ImageAn image blockAlt text is pre-filled from the asset’s name; the width and height come from the original file
VideoA Cloudflare video player blockPlayback options are set in the video dialog
AudioAn audio player blockThe path and asset id are filled in for you
DocumentAn inline download link carrying the document’s nameNot a block — it lands in the sentence you are writing

Everything the picker offers is already in the library, so the material you are inserting is documented on Asset Library.

Drag and drop

Drag a file from your desktop onto the canvas and let go: Leed uploads it, adds it to your library, and inserts it at the position under your pointer. The drop indicator between two blocks shows exactly where it will land.

Dropping the same file anywhere else in the CMS uploads it to the library without inserting it. Both routes need the asset:create privilege; without it the CMS attaches no drag listeners at all. The full behavior — MIME-type routing, the crop step for images, the size limits — is on Uploading by Drag and Drop.

Images

Click an image in the canvas and its gear opens Image Settings, which has two fields: Alt Text and Title.

The Image Settings dialog with alt text pre-filled and a title typed in, over the image widget

Alt text is never optional. It is the sentence a screen reader speaks and the text a search engine reads, and Leed already wrote you a first draft — a vision model describes every image at upload time and you can regenerate it, as Uploading Images describes. Edit it to say what the image means in this context; do not clear it.

Title is different, and most of the time you should leave it blank. A title is not metadata — it turns the image into a <figure> with a visible <figcaption> on the published page, which the docs stylesheet then frames with a border. That is a design decision. Write one when the caption says something the surrounding prose does not: a state the reader must notice, a value that differs from the default, or the deliberate difference between two images shown side by side.

Dialog fieldMarkdownRequired?DefaultReaches the published site?Notes
Alt Textthe text inside ![…]Strongly recommendedThe asset’s nameYesWritten for you at upload by the vision model
Titlethe quoted string after the URLNoEmptyYes — renders a <figure> with a visible captionA visible design element, not free metadata
—=WIDTHxHEIGHT appended to the alt textNoThe original file’s pixel sizeYesTaken from the asset record; there is no dialog control
—data-assetid—The asset idYesTies the placement back to the library record
—data-responsiver="false"—Emitted for SVG onlyYesOpts an SVG out of the responsive rewrite

Sizing and the responsive rewrite are the site build’s job, not the editor’s: the build turns each image into a srcset of CDN variants, which Image Variants and Responsive Images explains.

Video

Video comes from your library, where Cloudflare Stream has already processed it — a freshly uploaded file is not insertable until that finishes, as Video, Audio and Document Assets sets out. Insert it from the asset picker, then open the widget’s gear for Cloudflare Video Settings: Autoplay, Show Controls, Loop, Muted, Poster Image URL and Start Time (seconds).

Ticking Autoplay ticks Muted for you, and the Muted label changes to read Muted (required for autoplay) — browsers refuse to autoplay a video with sound, so this is the dialog stopping you from shipping a player that silently never starts.

Dialog fieldMarkdown parameterRequired?DefaultReaches the published site?Notes
—videoIdYesFrom the assetYesThe Cloudflare Stream id; set when you pick the asset
—assetIdYesFrom the assetYesThe Leed asset id
AutoplayautoplayNoOffYes — becomes autoplay=true on the player URLForces Muted on
Show ControlscontrolsNoOnYes
LooploopNoOffYes
MutedmutedNoOffYes
—allowFullscreenNoOnYesNo dialog control; on unless the markdown says otherwise
—width / heightNoUnsetYesCarried from the asset when known
Poster Image URL—NoEmptyNoShows in the editor’s preview only
Start Time (seconds)—No0NoShows in the editor’s preview only
—thumbnailTimestampNoUnsetYesPicks the poster frame, in seconds. No dialog control — only reachable in hand-written or imported markdown

YouTube

YouTube Video on the toolbar opens Insert YouTube Video. It asks for a Video ID — the part after v= in a YouTube URL, not the whole URL — and then offers Title, Width, Height, Start Time (seconds), End Time (seconds) and seven checkboxes: Autoplay, Show Controls, Loop, Muted, Show Info, Show Related Videos and Modest Branding. The Insert button stays disabled until the Video ID is non-empty.

The Insert YouTube Video dialog scrolled to show every field including the checkbox group
Dialog fieldMarkdown parameterRequired?DefaultReaches the published site?Notes
Video IDvideoIdYes—YesBecomes src="https://www.youtube.com/embed/<id>"
TitletitleNoEmptyNoThe iframe’s title is hard-coded to YouTube video player
WidthwidthNo560YesWritten only when it differs from the default
HeightheightNo315YesWritten only when it differs from the default
AutoplayautoplayNoOffPartlyAdds autoplay to the iframe’s allow list; does not start playback
MutedmutedNoOffNoTicked for you when you tick Autoplay
LooploopNoOffNo
Show ControlscontrolsNoOnNo
Start Time (seconds)startTimeNo0No
End Time (seconds)endTimeNo0No
Show InfoshowInfoNoOnNo
Show Related VideosrelNoOnNo
Modest BrandingmodestbrandingNoOffNo
—allowFullscreenNoOnYesAlways emitted; there is no control for turning it off

Audio

An audio asset inserted from the picker arrives with its Audio Path and Asset ID already filled in — the two required fields of the Insert Audio dialog are not things you normally type. The dialog then offers Auto Play, Show Controls and Loop, and all three reach the reader.

Dialog fieldMarkdown parameterRequired?DefaultReaches the published site?Notes
Audio PathpathYesFilled by the picker, as /f/<assetId>/<filename>YesBecomes the <source src>
Asset IDassetIdYesFilled by the pickerYesEmitted as data-assetid
Auto PlayautoplayNoOffYes
Show ControlscontrolsNoOnYes
LooploopNoOffYes

The site renders a plain HTML <audio> element with the fallback line Your browser does not support the audio element. — no custom player, no skin. Like video, an audio asset’s transcript can be inserted into the page from this dialog on Starter and up.

Iframes

Iframe on the toolbar opens Insert Iframe: URL (required), Title, Sandbox, Loading (Lazy or Eager), Referrer Policy (an eight-option list defaulting to Strict Origin When Cross Origin) and Allow Full Screen.

Leed injects nothing here. An option you leave blank is simply absent from the emitted <iframe> — so a blank Sandbox means no sandbox at all, not a safe default.

Dialog fieldMarkdown parameterRequired?DefaultReaches the published site?Notes
URLsrcYes—YesThe only supported way to give the frame a URL
TitletitleNoEmptyYesScreen readers announce it — write one
SandboxsandboxNoEmptyYesA space-separated permission list, e.g. allow-scripts allow-same-origin
LoadingloadingNolazyYeslazy or eager
Referrer PolicyreferrerPolicyNostrict-origin-when-cross-originYesEmitted lower-cased as referrerpolicy
Allow Full ScreenallowFullScreenNoOffYesEmitted as the bare allowfullscreen attribute
—width / heightNoUnsetYesNo dialog control; settable in markdown

Forms

Form on the toolbar inserts a form block. The dialog is a single Form select listing every form in your workspace by name, with the hint Select the form to embed. The editor writes only the form’s id into the page.

At build time the block becomes the {{> leed/form }} Handlebars partial with that id, and the partial renders the real form — fields, validation and submission included. Placing a Form on a Page covers what the reader gets and where the submissions go.

Icons

Icon on the toolbar opens the icon chooser. Icons are inline, so they sit inside a sentence rather than owning a block: click to open settings.

An icon’s color is set from the icon’s own color picker, which stores a Tailwind text color and a dark-mode twin — those come out as ordinary text-… and dark:text-… classes and render correctly.

Dialog fieldMarkdownRequired?DefaultReaches the published site?Notes
Icon (chooser)fa-solid fa-gear — the style prefix and icon nameYes—YesAn icon with no style prefix is not written at all
Color (picker)text-<color>-<depth>NoUnsetYesAn ordinary Tailwind class
Dark color (picker)dark:text-<color>-<depth>NoUnsetYesStored alongside the light color
—any other class nameNo—YesAppended to the class list verbatim

The full shortcode syntax, including the style prefixes and where icon names come from, is on Embeds and Icons.

From the file to what a reader receives

Nothing you set in a dialog is applied in the browser directly. The editor stores your choices on the block, the page serializes to Leed Markdown when it publishes, and the site build turns each embed into HTML — which is where the fields in the tables above are either honored or quietly dropped.

flowchart TD
  DROP["Drag a file onto the canvas"] --> UP["Upload"]
  PICK["Insert Asset → asset picker"] --> LIB
  UP --> LIB["Asset library record<br/>AI alt text · transcript · Stream processing"]
  LIB --> NODE["A block in the editor<br/>image · video · audio · download link"]
  YT["YouTube · Iframe · Form · Icon<br/>toolbar dialogs"] --> NODE
  NODE --> DLG["Settings dialog<br/>options stored on the block"]
  DLG --> MD["Leed Markdown<br/>written when the page publishes"]
  MD --> BUILD["Site build renderer"]
  BUILD --> IMG["An img tag with a responsive srcset"]
  BUILD --> IFR["An iframe — YouTube keeps only<br/>src · width · height · allowfullscreen"]
  BUILD --> AUD["An audio tag with a text fallback"]
  BUILD --> FRM["The form partial"]
  DLG -. "poster · start time · most YouTube options" .-> LOST["Stored, never emitted"]

The delivery side is automatic and needs nothing from you: images are resized and served in modern formats from the CDN, and video is streamed adaptively. What you choose in the editor is composition; what the reader’s connection gets is the build’s problem.

Every button named here is cataloged with its shortcut on Editor Toolbar Reference, and the non-media dialogs behind a block’s gear — alerts, collapsibles, code blocks, tab groups — are on Block Types and Block Settings.

ESC