# General HTML Support

With the General HTML Support (GHS) feature, developers can enable HTML features that are not supported by any other dedicated CKEditor 5 plugins. GHS lets you add elements, attributes, classes, and styles to the source. It also ensures this markup stays in the editor window and in the output.

<a id="demo">

## Demo

Use the [Enhanced source code editing feature](../source-editing/source-editing-enhanced.md) toolbar button to view and edit the HTML source of the document. You can find the configuration of this snippet below the demo.

You can configure the General HTML Support feature using the `config.htmlSupport` property. With this property, you need to list the HTML features that should be handled by GHS.

<!-- 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="additional-feature-information">

## Additional feature information

Here are some examples of HTML features that you can enable using General HTML Support:

* The `<section>`, `<article>`, and `<div>` elements.

* The `<audio>`, `<video>`, and `<iframe>` elements.

* The `<span>` and `<cite>` elements.

* Some attributes of existing dedicated CKEditor 5 features:

  * The `data-*` and `id` attributes on, for example, `<p>` and `<h1-h6>`.
  * The `style` and `classes` attributes on, for example, `<strong>` and `<a>`.

You can load (for example, via `editor.setData()`), paste, output (for example, via `editor.getData()`) the enabled HTML features. They are also visible in the editing area. To a limited extent, you can also edit such content in the editor. Read more about it in the [Level of support](#level-of-support) section.

<a id="level-of-support">

## Level of support

The difference between specific CKEditor 5 features such as [basic styles](../basic-styles.md) or [headings](../headings.md) and the HTML features enabled by GHS is that a plugin that supports a specific HTML feature provides a complete user experience for that feature. GHS ensures only that such content is accepted by the editor.

For instance, the dedicated [bold](../basic-styles.md#available-text-styles) feature offers a toolbar button used to make the selected text bold. Together with the [autoformatting feature](../autoformat.md), it also allows for applying bold style to content by typing a Markdown shortcode (`**foo**`) in the editor. The [headings](../headings.md) feature offers a dropdown from which the user can choose a heading level and ensures that pressing `Enter` at the end of a heading creates a new paragraph (and not another heading).

The General HTML Support does not offer any UI for the enabled features and takes only the basic semantics of a given feature into account. If you enable support for `<div>` elements via GHS, the user will not be able to create such elements from the editor UI. The GHS will know that a `<div>` is a container element, so it can wrap other blocks (like paragraphs) but cannot be used inline (next to, for example, a `<strong>` element). In this respect, it is similar to the Advanced Content Filtering (ACF) feature from CKEditor 4 as it lets you create a list of markup tags that the editor will not strip.

Therefore, the main use cases for GHS would be:

* Ensuring backward content compatibility with legacy systems.
* Introducing basic support for missing HTML features at a low cost.

> **Note**
>
> Considering the nature of GHS, you may consider installing the [Enhanced source code editing](../source-editing/source-editing-enhanced.md) feature alongside it.

<a id="installation">

## Installation

After [installing the editor](../../getting-started/installation/cloud/quick-start.md), add the feature to your plugin list and toolbar configuration:

**NPM**

```js
import { ClassicEditor, GeneralHtmlSupport } from 'ckeditor5';

ClassicEditor
	.create( {
		licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
		plugins: [ GeneralHtmlSupport, /* ... */ ],
		htmlSupport: {
			// Configuration.
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

**CDN**

```js
const { ClassicEditor, GeneralHtmlSupport } = CKEDITOR;

ClassicEditor
	.create( {
		licenseKey: '<YOUR_LICENSE_KEY>',
		plugins: [ GeneralHtmlSupport, /* ... */ ],
		htmlSupport: {
			// Configuration.
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

<a id="configuration">

## Configuration

By default, enabling the [`GeneralHtmlSupport`](../../api/module_html-support_generalhtmlsupport-GeneralHtmlSupport.md) plugin does not enable support for any given element. You need to configure the elements the user wants to use via the [`config.htmlSupport`](../../api/module_core_editor_editorconfig-EditorConfig.md#member-htmlSupport) option. List of predefined elements than can be enabled this way is [available further in this guide](#predefined-supported-elements). It is also possible to define and enable [custom elements](#enabling-custom-elements).

```js
ClassicEditor
	.create( {
		// ... Other configuration options ...
		htmlSupport: {
			allow: [ /* HTML features to allow. */ ],
			disallow: [ /* HTML features to disallow. */ ]
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

The notation of the `allow` and `disallow` rules looks as follows:

```js
[
	{
		// The element name to enable or extend with
		// the following styles, classes, and other attributes.
		name: string|regexp,

		// Styles to allow (by name, name and value, or just all).
		styles: object<string=>true|string|regexp>|array<string>|true,

		// Classes to allow (by name or just all).
		classes: array<string|regexp>|true,

		// Other attributes to allow (by name, name and value, or just all).
		attributes: object<string=>true|string|regexp>|array<string>|true,
	}
]
```

Several implementation examples:

```js
htmlSupport: {
	allow: [
		// Enables plain <div> elements.
		{
			name: 'div'
		},

		// Enables plain <div>, <section>, and <article> elements.
		{
			name: /^(div|section|article)$/
		},

		// Enables <div> elements with all inline styles (but no other attributes).
		{
			name: 'div',
			styles: true
		},

		// Enables <div> elements with "foo" and "bar" classes.
		{
			name: 'div',
			classes: [ 'foo', 'bar' ]
		},

		// Adds support for "foo" and "bar" classes to the already supported
		// <p> elements (those are enabled by the dedicated paragraph feature).
		{
			name: 'p',
			classes: [ 'foo', 'bar' ]
		},

		// Enables <div> elements with foo="true" attribute and "bar" attribute that
		// can accept any value (the Boolean "true" value works as an asterisk).
		{
			name: 'div',
			attributes: {
				foo: 'true',
				bar: true
			}
		},

		// Adds support for style="color: *" to the already supported
		// <p> and <h2-h4> elements.
		{
			name: /^(p|h[2-4])$/',
			styles: { 'color': true }
		},
}
```

The General HTML Support feature distinguishes several content types, each treated a bit differently:

* Container elements (like `<section>`, `<div>`).
* Inline elements (like `<span>`, `<a>`).
* Object elements (like `<iframe>`, `<video>`).

The enabled elements will not just be available “anywhere” in the content. They still need to adhere to certain rules derived from the HTML schema and common sense. Also, the behavior of specific types of elements in the editing area will be different. For instance, the object elements will only be selectable as a whole. The inline elements will work the same as other formatting features supported by CKEditor 5 (like bold and italic) do.

<a id="enabling-all-html-features">

### Enabling all HTML features

Sometimes you might want to enable all HTML features, so the editor will allow all elements and attributes. You can achieve this with a special configuration:

```js
htmlSupport: {
	allow: [
		{
			name: /.*/,
			attributes: true,
			classes: true,
			styles: true
		}
	]
}
```

> **Note**
>
> Enabling all HTML features creates a security risk. You should pass a list of disallowed elements and attributes to the configuration to make sure that any malicious code will not be saved and executed in the editor.

This configuration will work similarly to the [`allowedContent: true`](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html#cfg-allowedContent) option from CKEditor 4.

<a id="enabling-script-tags-for-legacy-use-cases">

### Enabling script tags for legacy use cases

> **Warning**
>
> Scripts are blocked by default for [security reasons](#security). For new implementations, we discourage embedding scripts in editor content.

Scripts are blocked by default because allowing `<script>` tags in editor content would let arbitrary JavaScript run whenever that content is rendered. It creates a serious cross-site scripting (XSS) risk—content from untrusted sources (e.g., copied from a malicious site or supplied by an attacker) could steal session data, modify the page, or perform actions on behalf of the user. If you attempt to place the `<script>` tag in the content, you may see an error like this:

```
view renderer filler not found
```

Blocking scripts by default keeps the editing surface and the published output safe unless you explicitly enable scripts for a controlled, legacy scenario. For example, if you have content from CKEditor 4 and script tags are necessary. For such a case you can explicitly allow `<script>` tags using the configuration:

```js
htmlSupport: {
	allow: [
		{
			name: 'script',
			attributes: true,
			classes: true,
			styles: true
		}
	]
}
```

This configuration allows `<script>` elements along with their attributes, classes, and styles, preventing the editor from crashing when such content is loaded.

<a id="security">

### Security

When you set up the GHS to allow elements like `<script>` or attributes like `onclick`, you expose the users of your application to a possibly malicious markup. This can be code mistakenly copied from a risky website or purposely provided by a bad actor. An example of that could be: `<div onclick="leakUserData()">`.

The content inside the editor (what you see in the editing area) is filtered by default from typical content that could break the editor. However, the editor does not feature a full XSS filter. We recommend configuring GHS to enable specific HTML markup instead of enabling all markup at once.

Moreover, as a general rule, not exclusive to GHS, there should always be a sanitization process present on the backend side of your application. Even the best filtering done on the browser side of your application can be mitigated, and every network call can be manipulated, thus bypassing the frontend filtering. This can quickly become a security risk.

In addition to the sanitization process and safe GHS configuration, it is highly recommended to set strict [Content Security Policy](../../getting-started/setup/csp.md) rules.

<a id="iframe-sandbox">

#### Iframe sandbox

`<iframe>` elements can introduce security risks if not properly restricted. To address this, browsers support the [`sandbox` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe#sandbox) on iframes, which restricts what the embedded content is allowed to do.

GHS automatically enforces a `sandbox` attribute on all `<iframe>` elements in the **editing view** via the built-in `IframeElementSupport` plugin. This behavior is controlled by the `htmlSupport.htmlIframeSandbox` configuration option.

> **Note**
>
> The `htmlIframeSandbox` option only affects what is rendered in the editing area. It does **not** modify the document data returned by `editor.getData()`. Your backend sanitization pipeline should independently enforce iframe sandboxing in the output HTML.

The `htmlIframeSandbox` option accepts `true` (the default), an array of allowed [sandbox flag values](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe#sandbox), or `false`. Setting it to `true` enforces the strictest mode — every `<iframe>` in the editing view gets an empty `sandbox=""` attribute, disabling scripts, forms, popups, and all other privileged features. Passing an array limits the sandbox to only the specified flags; any flag not on the list is stripped from existing iframes. Setting the option to `false` disables enforcement entirely and leaves the attribute untouched.

```js
ClassicEditor
	.create( {
		htmlSupport: {
			// Default. Enforces empty sandbox on all iframes.
			htmlIframeSandbox: true,

			// Only these flags are kept.
			// htmlIframeSandbox: [ 'allow-scripts', 'allow-same-origin' ],

			// No enforcement — attribute is left as-is.
			// htmlIframeSandbox: false
		}
	} );
```

<a id="enabling-custom-elements">

### Enabling custom elements

You can define custom HTML elements with attributes and classes.

To use a new element, you need to register it with [`DataSchema`](../../api/module_html-support_dataschema-DataSchema.md) as one of the types below:

* Inline element.
* Block element.

To enable such elements and add attributes or classes to them, you need to use the [`allowElement`](../../api/module_html-support_datafilter-DataFilter.md#function-allowElement) and [`allowAttributes`](../../api/module_html-support_datafilter-DataFilter.md#function-allowAttributes) methods from the [`DataFilter`](../../api/module_html-support_datafilter-DataFilter.md) API.

Base implementation example:

**NPM**

```js
import { ClassicEditor, Essentials, Paragraph, Plugin, SourceEditingEnhanced, GeneralHtmlSupport } from 'ckeditor5';

/**
* A plugin extending General HTML Support, for example, with custom HTML elements.
*/
class ExtendHTMLSupport extends Plugin {
	static get requires() {
		return [ GeneralHtmlSupport ];
	}

	init() {
		// Extend the schema with custom HTML elements.
		const dataFilter = this.editor.plugins.get( 'DataFilter' );
		const dataSchema = this.editor.plugins.get( 'DataSchema' );

		// Inline element.
		dataSchema.registerInlineElement( {
			view: 'element-inline',
			model: 'myElementInline'
		} );

		// Custom elements need to be registered using direct API instead of configuration.
		dataFilter.allowElement( 'element-inline' );
		dataFilter.allowAttributes( { name: 'element-inline', attributes: { 'data-foo': false }, classes: [ 'foo' ] } );

		// Block element.
		dataSchema.registerBlockElement( {
			view: 'element-block',
			model: 'myElementBlock',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		} );

		dataFilter.allowElement( 'element-block' );
	}
}

ClassicEditor
	.create( {
		attachTo: document.querySelector( '#editor' ),
		plugins: [
			Essentials,
			Paragraph,
			SourceEditingEnhanced,
			ExtendHTMLSupport
		],
		htmlSupport: {
			allow: [
				{
					name: /.*/,
					attributes: true,
					classes: true,
					styles: true
				}
			]
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

**CDN**

```js
const { ClassicEditor, Essentials, Paragraph, Plugin, SourceEditingEnhanced, GeneralHtmlSupport } = CKEDITOR;

/**
* A plugin extending General HTML Support, for example, with custom HTML elements.
*/
class ExtendHTMLSupport extends Plugin {
	static get requires() {
		return [ GeneralHtmlSupport ];
	}

	init() {
		// Extend the schema with custom HTML elements.
		const dataFilter = this.editor.plugins.get( 'DataFilter' );
		const dataSchema = this.editor.plugins.get( 'DataSchema' );

		// Inline element.
		dataSchema.registerInlineElement( {
			view: 'element-inline',
			model: 'myElementInline'
		} );

		// Custom elements need to be registered using direct API instead of configuration.
		dataFilter.allowElement( 'element-inline' );
		dataFilter.allowAttributes( { name: 'element-inline', attributes: { 'data-foo': false }, classes: [ 'foo' ] } );

		// Block element.
		dataSchema.registerBlockElement( {
			view: 'element-block',
			model: 'myElementBlock',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		} );

		dataFilter.allowElement( 'element-block' );
	}
}

ClassicEditor
	.create( {
		attachTo: document.querySelector( '#editor' ),
		plugins: [
			Essentials,
			Paragraph,
			SourceEditingEnhanced,
			ExtendHTMLSupport
		],
		htmlSupport: {
			allow: [
				{
					name: /.*/,
					attributes: true,
					classes: true,
					styles: true
				}
			]
		}
	} )
	.then( /* ... */ )
	.catch( /* ... */ );
```

You can treat both inline and block elements as object elements. To make it possible, it is necessary to set the [isObject](../../api/module_html-support_dataschema-HtmlSupportDataSchemaDefinition.md#member-isObject) property to `true`.

```js
// Inline object element.
dataSchema.registerInlineElement( {
	view: 'object-inline',
	model: 'myObjectInline',
	isObject: true,
	modelSchema: {
		inheritAllFrom: '$inlineObject'
	}
} );

dataFilter.allowElement( 'object-inline' );

// Block object element.
dataSchema.registerBlockElement( {
	view: 'object-block',
	model: 'myObjectBlock',
	isObject: true,
	modelSchema: {
		inheritAllFrom: '$blockObject'
	}
} );

dataFilter.allowElement( 'object-block' );
```

<a id="predefined-supported-elements">

### Predefined supported elements

The HTML elements listed below can be turned on directly via the `allow` setting of the `config.htmlSupport` option [mentioned above](#configuration).

<a id="block-elements">

#### Block elements

* address
* article
* aside
* blockquote
* button
* caption
* center
* col
* colgroup
* dd
* details
* dir
* div
* dl
* dt
* fieldset
* figcaption
* figure
* footer
* form
* header
* hgroup
* hr
* hx
* img
* input
* legend
* li
* main
* menu
* nav
* ol
* p
* pre
* section
* summary
* table
* tbody
* td
* tfoot
* th
* thead
* tr
* ul

<a id="inline-elements">

#### Inline elements

* a
* abbr
* acronym
* audio
* b
* bdi
* bdo
* big
* canvas
* cite
* code
* del
* dfn
* embed
* em
* figure
* font
* i
* iframe
* ins
* kbd
* li
* mark
* meter
* object
* oembed
* ol
* output
* progress
* q
* s
* samp
* script
* select
* small
* span
* strong
* style
* sub
* sup
* tbody
* thead
* time
* tt
* u
* ul
* var
* video

<a id="known-issues">

## Known issues

You can add support for arbitrary styles, classes, and other attributes to existing CKEditor 5 features (such as paragraphs, headings, list items, etc.). Most of the existing CKEditor 5 features can already be extended this way; however, some cannot yet.

> **Note**
>
> While the GHS feature is stable, some problems with complex documents may occur if you use it together with [real-time collaboration](../collaboration/real-time-collaboration/real-time-collaboration.md).

We are open to feedback, so if you find any issue, feel free to report it in the [main CKEditor 5 repository](https://github.com/ckeditor/ckeditor5/issues/).

<a id="related-features">

## Related features

CKEditor 5 has other features related to HTML editing that you may want to check:

* [Full page HTML](full-page-html.md) – Allows using CKEditor 5 to edit entire HTML pages, from `<html>` to `</html>`, including the page metadata.
* [Enhanced source code editing](../source-editing/source-editing-enhanced.md) – Allows for viewing and editing the source code of the document in a handy modal window (compatible with all editor types) with syntax highlighting, autocompletion and more.
* [HTML embed](html-embed.md) – Allows embedding an arbitrary HTML snippet in the editor.

---

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