How can I help you?
XHTML validation in React Rich Text Editor Component
4 Sep 202524 minutes to read
The Syncfusion React Rich Text Editor provides features to ensure content complies with XHTML standards and is secure against cross-site scripting (XSS) attacks. The enableXhtml property enforces continuous XHTML validation, while enableHtmlSanitizer and the beforeSanitizeHtml event protect against XSS vulnerabilities. These features are essential for maintaining standards-compliant and secure content, particularly when editorMode is set to HTML.
XHTML validation
The editor includes an enableXhtml property that allows for continuous validation of the Rich Text Editor’s source content against the XHTML standard. When content is entered or modified in the editor, this feature ensures ongoing compliance by automatically removing invalid elements and attributes.
Validating attributes
When enableXhtml is set to true, the editor enforces the following attribute rules:
-
Case Sensitivity: Attributes must be lowercase (e.g.,
class, notCLASS). - Quotation Marks: Attribute values must be enclosed in quotation marks..
- Validity: Only valid attributes for corresponding HTML elements are allowed.
-
Required Attributes: Required attributes for HTML elements must be included (e.g.,
altfor<img>).
Validating HTML elements
The editor also enforces these element rules:
-
Case Sensitivity: HTML tags must be lowercase (e.g.,
<p>, not<P>). - Proper Closing: All opening tags must have corresponding closing tags.
- Element Validity: Only valid HTML elements are permitted.
- Nesting: Elements must be properly nested to maintain structure.
- Root Element: The content must have a single root element.
-
Element Hierarchy: Inline elements cannot contain block elements (e.g.,
<span>cannot contain<div>).
The following example demonstrates enabling XHTML validation:
[Class-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
class App extends React.Component {
rteValue = "<p>The Syncfusion Rich Text Editor, a WYSIWYG (what you see is what you get) editor, is a user interface that allows you to create, edit, and format rich text content. You can try out a demo of this editor here.</p><p><b>Key features:</b></p><ul><li><p>Provides <IFRAME> and <DIV> modes.</p></li><li><p>Bulleted and numbered lists.</p></li><li><p>Handles images, hyperlinks, videos, hyperlinks, uploads, etc.</p></li><li><p>Contains undo/redo manager. </p></li></ul><div style='display: inline-block; width: 60%; vertical-align: top; cursor: auto;'><img alt='Sky with sun' src='https://cdn.syncfusion.com/ej2/richtexteditor-resources/RTE-Overview.png' width='309' style='min-width: 10px; min-height: 10px; width: 309px; height: 174px;' class='e-rte-image e-imginline e-rte-drag-image' height='174' /></div>";
render() {
return (<RichTextEditorComponent height={450} value={this.rteValue} enableXhtml={true}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
class App extends React.Component<{},{}> {
private rteValue: string = "<p>The Syncfusion Rich Text Editor, a WYSIWYG (what you see is what you get) editor, is a user interface that allows you to create, edit, and format rich text content. You can try out a demo of this editor here.</p><p><b>Key features:</b></p><ul><li><p>Provides <IFRAME> and <DIV> modes.</p></li><li><p>Bulleted and numbered lists.</p></li><li><p>Handles images, hyperlinks, videos, hyperlinks, uploads, etc.</p></li><li><p>Contains undo/redo manager. </p></li></ul><div style='display: inline-block; width: 60%; vertical-align: top; cursor: auto;'><img alt='Sky with sun' src='https://cdn.syncfusion.com/ej2/richtexteditor-resources/RTE-Overview.png' width='309' style='min-width: 10px; min-height: 10px; width: 309px; height: 174px;' class='e-rte-image e-imginline e-rte-drag-image' height='174' /></div>";
public render() {
return (
<RichTextEditorComponent height={450} enableXhtml={true} value={this.rteValue}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
}
export default App;[Functional-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
function App() {
let rteValue = "<p>The Syncfusion Rich Text Editor, a WYSIWYG (what you see is what you get) editor, is a user interface that allows you to create, edit, and format rich text content. You can try out a demo of this editor here.</p><p><b>Key features:</b></p><ul><li><p>Provides <IFRAME> and <DIV> modes.</p></li><li><p>Bulleted and numbered lists.</p></li><li><p>Handles images, hyperlinks, videos, hyperlinks, uploads, etc.</p></li><li><p>Contains undo/redo manager. </p></li></ul><div style='display: inline-block; width: 60%; vertical-align: top; cursor: auto;'><img alt='Sky with sun' src='https://cdn.syncfusion.com/ej2/richtexteditor-resources/RTE-Overview.png' width='309' style='min-width: 10px; min-height: 10px; width: 309px; height: 174px;' class='e-rte-image e-imginline e-rte-drag-image' height='174' /></div>";
return (<RichTextEditorComponent height={450} enableXhtml={true} value={rteValue}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
function App() {
let rteValue: string = "<p>The Syncfusion Rich Text Editor, a WYSIWYG (what you see is what you get) editor, is a user interface that allows you to create, edit, and format rich text content. You can try out a demo of this editor here.</p><p><b>Key features:</b></p><ul><li><p>Provides <IFRAME> and <DIV> modes.</p></li><li><p>Bulleted and numbered lists.</p></li><li><p>Handles images, hyperlinks, videos, hyperlinks, uploads, etc.</p></li><li><p>Contains undo/redo manager. </p></li></ul><div style='display: inline-block; width: 60%; vertical-align: top; cursor: auto;'><img alt='Sky with sun' src='https://cdn.syncfusion.com/ej2/richtexteditor-resources/RTE-Overview.png' width='309' style='min-width: 10px; min-height: 10px; width: 309px; height: 174px;' class='e-rte-image e-imginline e-rte-drag-image' height='174' /></div>";
return (
<RichTextEditorComponent height={450} enableXhtml={true} value={rteValue}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
export default App;Cross-Site scripting (XSS) prevention
The Rich Text Editor allows users to edit the content with security by preventing cross-site scripting (XSS). By default, it provides built-in support to remove elements from editor content that cause XSS attacks. The editor removes the elements based on the attributes if it is possible to execute a script.
Enabling XSS prevention
The enableHtmlSanitize, enabled by default, activates XSS prevention. When active, the editor automatically removes elements like <script> and attributes like onmouseover from the content.
The following example shows XSS prevention removing a <script> tag and onmouseover attribute:
[Class-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
class App extends React.Component {
rteValue="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>";
onBeforeSanitizeHtml(e) {
e.helper = (value) => {
e.cancel = true;
let temp = document.createElement('div');
temp.innerHTML = value;
let scriptTag = temp.querySelector('script');
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
render() {
return (<RichTextEditorComponent height={450} value={this.rteValue} beforeSanitizeHtml={this.onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar, BeforeSanitizeHtmlArgs, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
class App extends React.Component<{},{}> {
private rteValue:string ="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>";
public onBeforeSanitizeHtml(e: BeforeSanitizeHtmlArgs): void {
e.helper = (value: string) => {
e.cancel = true;
let temp: HTMLElement = document.createElement('div');
temp.innerHTML = value;
let scriptTag: HTMLElement = temp.querySelector('script') as HTMLElement;
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
public render() {
return (
<RichTextEditorComponent height={450} value={this.rteValue} beforeSanitizeHtml={this.onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
}
export default App;[Functional-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
function App() {
let rteValue="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>"
const onBeforeSanitizeHtml=(e) =>{
e.helper = (value) => {
e.cancel = true;
let temp = document.createElement('div');
temp.innerHTML = value;
let scriptTag = temp.querySelector('script');
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
return (<RichTextEditorComponent height={450} value={rteValue} beforeSanitizeHtml={onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar,BeforeSanitizeHtmlArgs, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
function App(){
let rteValue:string="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>";
const onBeforeSanitizeHtml=(e:BeforeSanitizeHtmlArgs) =>{
e.helper = (value: string) => {
e.cancel = true;
let temp: HTMLElement = document.createElement('div');
temp.innerHTML = value;
let scriptTag: HTMLElement = temp.querySelector('script') as HTMLElement;
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
return (
<RichTextEditorComponent height={450} value={rteValue} beforeSanitizeHtml={onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
export default App;The XSS prevention feature is only applicable when the editorMode is set to HTML.
Custom XSS prevention
For more precise control over XSS prevention, you can implement custom filtering logic using the beforeSanitizeHtml event.
Implementing custom cross-site scripting and fililtering in Rich Text Editor
- Use the beforeSanitizeHtml event to define custom filtering rules.
- Utilize the
helperfunction from the event argument to apply your custom filters. - Set the
cancelargument totrueif you want to override the built-in XSS prevention entirely.
The following sample demonstrates how to filter the script tag by value.
[Class-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
class App extends React.Component {
rteValue="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>";
onBeforeSanitizeHtml(e) {
e.helper = (value) => {
e.cancel = true;
let temp = document.createElement('div');
temp.innerHTML = value;
let scriptTag = temp.querySelector('script');
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
render() {
return (<RichTextEditorComponent height={450} value={this.rteValue} beforeSanitizeHtml={this.onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar, BeforeSanitizeHtmlArgs, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
class App extends React.Component<{},{}> {
private rteValue:string ="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>";
public onBeforeSanitizeHtml(e: BeforeSanitizeHtmlArgs): void {
e.helper = (value: string) => {
e.cancel = true;
let temp: HTMLElement = document.createElement('div');
temp.innerHTML = value;
let scriptTag: HTMLElement = temp.querySelector('script') as HTMLElement;
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
public render() {
return (
<RichTextEditorComponent height={450} value={this.rteValue} beforeSanitizeHtml={this.onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
}
export default App;[Functional-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
function App() {
let rteValue="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>"
const onBeforeSanitizeHtml=(e) =>{
e.helper = (value) => {
e.cancel = true;
let temp = document.createElement('div');
temp.innerHTML = value;
let scriptTag = temp.querySelector('script');
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
return (<RichTextEditorComponent height={450} value={rteValue} beforeSanitizeHtml={onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar,BeforeSanitizeHtmlArgs, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import { detach } from '@syncfusion/ej2-base';
function App(){
let rteValue:string="<div>Prevention of Cross Sit Scripting (XSS)</div><script>alert('hi')</script>";
const onBeforeSanitizeHtml=(e:BeforeSanitizeHtmlArgs) =>{
e.helper = (value: string) => {
e.cancel = true;
let temp: HTMLElement = document.createElement('div');
temp.innerHTML = value;
let scriptTag: HTMLElement = temp.querySelector('script') as HTMLElement;
if (scriptTag) {
detach(scriptTag);
}
return temp.innerHTML;
}
}
return (
<RichTextEditorComponent height={450} value={rteValue} beforeSanitizeHtml={onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
export default App;You can also filter out the e.selectors.tags and e.selector.attributes in the beforeSanitizeHtml event to control which HTML tags and attributes are allowed to appear.
For instance, if you want to display <iframe>, By manipulating the e.selectors.tags property in this event, you can selectively remove tags like <iframe>. This approach ensures that your application can safely display iframe while preventing potential security risks associated with XSS vulnerabilities.
The following sample demonstrates how to filter the iframe tag.
[Class-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
class App extends React.Component {
rteValue=`<div>Prevention of Cross-Site Scripting (XSS)</div><script>alert('hi')</script><iframe srcdoc="<p>The Rich Text Editor component is WYSIWYG ('what you see is what you get') editor that provides the best user experience to create and update the content. Users can format their content using standard toolbar commands.</p>"></iframe>`;
onBeforeSanitizeHtml(e) {
if (e.selectors && e.selectors.tags) {
e.selectors.tags = e.selectors.tags.filter((tag) => tag !== 'iframe:not(.e-rte-embed-url)');
e.selectors.tags = [('iframe[src^="https://"]')];
}
}
render() {
return (<RichTextEditorComponent height={450} value={this.rteValue} beforeSanitizeHtml={this.onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar, BeforeSanitizeHtmlArgs, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
class App extends React.Component<{},{}> {
public rteValue: string = `<div>Prevention of Cross-Site Scripting (XSS)</div><script>alert('hi')</script><iframe srcdoc="<p>The Rich Text Editor component is WYSIWYG ('what you see is what you get') editor that provides the best user experience to create and update the content. Users can format their content using standard toolbar commands.</p>"></iframe>`;
public onBeforeSanitizeHtml(e: BeforeSanitizeHtmlArgs): void {
if (e.selectors && e.selectors.tags) {
e.selectors.tags = e.selectors.tags.filter((tag: string) => tag !== 'iframe:not(.e-rte-embed-url)');
e.selectors.tags = [('iframe[src^="https://"]')];
}
}
public render() {
return (
<RichTextEditorComponent height={450} value={this.rteValue} beforeSanitizeHtml={this.onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
}
export default App;[Functional-component]
import { HtmlEditor, Image, Inject, Link, QuickToolbar, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
function App() {
let rteValue = `<div>Prevention of Cross-Site Scripting (XSS)</div><script>alert('hi')</script><iframe srcdoc="<p>The Rich Text Editor component is WYSIWYG ('what you see is what you get') editor that provides the best user experience to create and update the content. Users can format their content using standard toolbar commands.</p>"></iframe>`;
const onBeforeSanitizeHtml=(e) =>{
if (e.selectors && e.selectors.tags) {
e.selectors.tags = e.selectors.tags.filter((tag) => tag !== 'iframe:not(.e-rte-embed-url)');
e.selectors.tags = [('iframe[src^="https://"]')];
}
}
return (<RichTextEditorComponent height={450} value={rteValue} beforeSanitizeHtml={onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]}/>
</RichTextEditorComponent>);
}
export default App;import { HtmlEditor, Image, Inject, Link, QuickToolbar,BeforeSanitizeHtmlArgs, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
function App(){
let rteValue:string = `<div>Prevention of Cross-Site Scripting (XSS)</div><script>alert('hi')</script><iframe srcdoc="<p>The Rich Text Editor component is WYSIWYG ('what you see is what you get') editor that provides the best user experience to create and update the content. Users can format their content using standard toolbar commands.</p>"></iframe>`;
const onBeforeSanitizeHtml=(e: BeforeSanitizeHtmlArgs)=> {
if (e.selectors && e.selectors.tags) {
e.selectors.tags = e.selectors.tags.filter((tag: string) => tag !== 'iframe:not(.e-rte-embed-url)');
e.selectors.tags = [('iframe[src^="https://"]')];
}
}
return (
<RichTextEditorComponent height={450} value={rteValue} beforeSanitizeHtml={onBeforeSanitizeHtml}>
<Inject services={[Toolbar, Image, Link, HtmlEditor, QuickToolbar]} />
</RichTextEditorComponent>
);
}
export default App;