# Using external services for media previews

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

You can use proxy services like [Iframely](https://iframely.com/) or [Embedly](https://embed.ly/) for two related purposes:

* To show previews of non-previewable providers (X, Instagram, Facebook, and others) **inside the editor**.
* To render the `<oembed>` tags produced by CKEditor 5 as rich previews **on your target website**.

Both use the same underlying services but are set up in different places.

<a id="in-the-editor">

## In the editor

To get around the limitations of showing media embed previews, you can use services like [Iframely](https://iframely.com/). This will allow a rich preview of the content inside CKEditor 5. By inserting an Iframely-hosted `<iframe>`, you can preview the content from hundreds of media providers.

Follow the [Iframely integration with CKEditor 5](https://iframely.com/docs/ckeditor) page for a detailed explanation. You can also check the final result in the demo below. If you are using ad-blocking software, it might also block the previews inside the editor.

<!-- AI-AGENT-NOTE: An interactive demo is embedded here but is not represented in this Markdown file. If you need to see it in action, open this page in a browser (e.g. via a browser-automation MCP like Chrome DevTools or Playwright), or let the user know a live demo is available on this page. -->

This demo presents a limited set of features. Visit the [feature-rich editor example](../../examples/builds-custom/full-featured-editor.md) to see more in action.

<a id="on-your-website">

## On your website

By default, the media embed feature produces output that does not contain previews of embedded media, called the [semantic output](media-embed-configuration.md#semantic-data-output-default). This means that you need to transform the output `<oembed>` elements into real media on your target website.

There are many ways to do that. The simplest plug-and-play solutions are described here. You can also implement this transformation as part of your backend service, or you can use different services than described in this section.

> **Note**
>
> While the easiest solution (described below) is to replace embedded media on the client side, it is not necessarily the most optimal way. A more powerful and flexible solution is to request these services on your backend. Refer to the documentation of the service of your choice for more information.

<a id="iframely">

### Iframely

[Iframely](https://iframely.com) offers the [embed.js](https://iframely.com/docs/embedjs) library, which converts [various media](https://iframely.com/docs/providers) URLs into rich previews. It works in the frontend and remains fully compatible with the output produced by CKEditor 5.

First, having [secured the API key](https://iframely.com/docs/allow-origins), load the `embed.js` library from the CDN into your website:

```html
<head>
	...
	<script charset="utf-8" src="//cdn.iframe.ly/embed.js?api_key={API KEY}"></script>
	...
</head>
```

<a id="semantic-data">

#### Semantic data

You can convert all `<oembed>` elements like the following X (Twitter) post produced by CKEditor 5:

```html
<figure class="media">
	<oembed url="https://x.com/ckeditor/status/1021777799844126720"></oembed>
</figure>
```

using this short code snippet:

```html
<script>
	document.querySelectorAll( 'oembed[url]' ).forEach( element => {
		iframely.load( element, element.attributes.url.value );
	} );
</script>
```

<a id="non-semantic-data">

#### Non-semantic data

When you configure the feature to [include media previews](media-embed-configuration.md#including-previews-in-data) in the output, you can still use Iframely for media embeds like the following one:

```html
<figure class="media">
	<div data-oembed-url="https://x.com/ckeditor/status/1021777799844126720">
		[Media preview]
	</div>
</figure>
```

You can still convert this data by Iframely with just a few extra lines of code. To achieve that, in addition to the code snippet from the previous section, use a slightly longer code snippet that discards the media preview saved in the database before using `iframely.load()`:

```html
<script>
	document.querySelectorAll( 'div[data-oembed-url]' ).forEach( element => {
		// Discard the static media preview from the database (empty the <div data-oembed-url="...">).
		while ( element.firstChild ) {
			element.removeChild( element.firstChild );
		}

		// Generate the media preview using Iframely.
		iframely.load( element, element.dataset.oembedUrl ) ;
	} );
</script>
```

<a id="embedly">

### Embedly

Just like Iframely, [Embedly](https://embed.ly) offers the client–side API, which converts media URLs into rich previews.

To start using it, load the library from the CDN into your website:

```html
<head>
	...
	<script async charset="utf-8" src="//cdn.embedly.com/widgets/platform.js"></script>
	...
</head>
```

<a id="semantic-data-2">

#### Semantic data

You can convert `<oembed>` elements like the following Twitter (X) post produced by CKEditor 5:

```html
<figure class="media">
	<oembed url="https://x.com/ckeditor/status/1021777799844126720"></oembed>
</figure>
```

using this code snippet:

```html
<script>
	document.querySelectorAll( 'oembed[url]' ).forEach( element => {
		// Create the <a href="..." class="embedly-card"></a> element that Embedly uses
		// to discover the media.
		const anchor = document.createElement( 'a' );

		anchor.setAttribute( 'href', element.getAttribute( 'url' ) );
		anchor.className = 'embedly-card';

		element.appendChild( anchor );
	} );
</script>
```

Embedly automatically discovers links like `<a href="..." class="embedly-card"></a>` and replaces them with rich media previews.

<a id="non-semantic-data-2">

#### Non-semantic data

In this case, the code is almost the same as with the semantic data, but you should discard the media preview saved in the database before using Embedly to avoid code duplication:

```html
<script>
	document.querySelectorAll( 'div[data-oembed-url]' ).forEach( element => {
		// Discard the static media preview from the database (empty the <div data-oembed-url="...">).
		while ( element.firstChild ) {
			element.removeChild( element.firstChild );
		}

		// Create the <a href="..." class="embedly-card"></a> element that Embedly uses
		// to discover the media.
		const anchor = document.createElement( 'a' );

		anchor.setAttribute( 'href', element.dataset.oembedUrl );
		anchor.className = 'embedly-card';

		element.appendChild( anchor );
	} );
</script>
```

<a id="known-issues">

## Known issues

The [media embed resize feature](media-embed-resize.md) (`MediaEmbedResize`) is not compatible with proxy-based preview providers like Iframely or Embedly. The proxy controls the iframe height at runtime, so a user-applied width on the `<figure>` no longer matches the proxy-chosen height. Remove `MediaEmbedResize` from `plugins` (or pass it via `removePlugins`) in setups that use these providers. If you still need to control the size of these embeds, register a width-applying custom style through [`config.mediaEmbed.styles`](media-embed-styles.md) that sets a `width` on the `<figure>`.

---

Full index of the CKEditor 5 documentation: [llms.txt](../../../llms.txt)
