<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jollen Moyani</title>
    <description>The latest articles on DEV Community by Jollen Moyani (@jollenmoyani).</description>
    <link>https://dev.to/jollenmoyani</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F972927%2F876a12f1-3771-485a-87be-b132e7ebd4bf.png</url>
      <title>DEV Community: Jollen Moyani</title>
      <link>https://dev.to/jollenmoyani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jollenmoyani"/>
    <language>en</language>
    <item>
      <title>From HTML to Structured Content: Why We Built the Modern Rich Text Editor</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Thu, 22 Oct 2026 15:08:44 +0000</pubDate>
      <link>https://dev.to/syncfusion/from-html-to-structured-content-why-we-built-the-modern-rich-text-editor-lnn</link>
      <guid>https://dev.to/syncfusion/from-html-to-structured-content-why-we-built-the-modern-rich-text-editor-lnn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Traditional HTML editing can become challenging when applications need consistent, structured content. That’s why Syncfusion introduced its Modern Rich Text Editor in Essential Studio 2026 Volume 3 as a preview. Built on ProseMirror, it brings schema-driven editing and JSON-based document management. Explore why we built it, how HTML and JSON compare, and how to get started with React.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most rich text editors still treat HTML as the document itself. That approach works well for basic text formatting, but modern applications increasingly need more: structured content, interactive components, reliable change tracking, workflow automation, AI-powered content experiences, and future collaboration capabilities.&lt;/p&gt;

&lt;p&gt;As we evolved our editing platform, we repeatedly encountered limitations that stemmed from a single architectural decision: HTML was acting as both the presentation layer and the source of truth.&lt;/p&gt;

&lt;p&gt;To address these challenges, we built the Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/rich-text-editor-sdk/react-modern-rich-text-editor" rel="noopener noreferrer"&gt;Modern Rich Text Editor&lt;/a&gt;, introduced in the Rich Text Editor SDK Volume 3 2026 release. Instead of storing content as HTML, it uses a schema-driven document model powered by the Syncfusion Headless Editor and built on &lt;code&gt;ProseMirror&lt;/code&gt;. Content is stored as structured JSON by default, while HTML remains available whenever applications need it.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll explore the challenges of HTML-based editing, how structured editing addresses them, why we created a new editor instead of extending the existing Rich Text Editor, and how to get started with the Modern Rich Text Editor in React.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we built the Modern Rich Text Editor
&lt;/h2&gt;

&lt;p&gt;Our existing Rich Text Editor is built around HTML editing. The HTML inside the editor is the document, and every edit modifies that markup directly.&lt;/p&gt;

&lt;p&gt;That model works well for formatting content. However, as customers requested structured content experiences, custom interactive blocks, reliable change information, workflow integration, and pathways toward collaboration and AI-assisted authoring, we repeatedly encountered limitations that could not be solved without rethinking the underlying document model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The challenges of HTML-based editing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Equivalent Content Can Have Different Markup
&lt;/h3&gt;

&lt;p&gt;The following examples all render identically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;b&amp;gt;&lt;/span&gt;Release notes&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Release notes&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"font-weight:bold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Release notes&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although visually identical, they produce different HTML structures. Browser behavior, pasted content, and editing actions can generate multiple variants over time, making content harder to compare, search, transform, and sanitize consistently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interactive content can break during editing
&lt;/h3&gt;

&lt;p&gt;Components such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Callouts&lt;/li&gt;
&lt;li&gt;Checklists&lt;/li&gt;
&lt;li&gt;Collapsible sections&lt;/li&gt;
&lt;li&gt;Structured content blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;often depend on HTML structure and CSS classes.&lt;/p&gt;

&lt;p&gt;When users paste content, delete across boundaries, or perform unexpected editing actions, these structures can become invalid or partially broken, leaving content that no longer behaves correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML doesn’t describe user intent
&lt;/h3&gt;

&lt;p&gt;Features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Autosave&lt;/li&gt;
&lt;li&gt;Audit history&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Review workflows&lt;/li&gt;
&lt;li&gt;Track changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;need meaningful change information.&lt;/p&gt;

&lt;p&gt;Comparing HTML only indicates that markup changed. It doesn’t tell applications that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A heading was added&lt;/li&gt;
&lt;li&gt;A paragraph became a list&lt;/li&gt;
&lt;li&gt;A checklist item was completed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Extending the editor becomes increasingly complex
&lt;/h3&gt;

&lt;p&gt;As more features are added, each one must account for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser editing behavior&lt;/li&gt;
&lt;li&gt;Copy and paste&lt;/li&gt;
&lt;li&gt;Serialization&lt;/li&gt;
&lt;li&gt;Selection handling&lt;/li&gt;
&lt;li&gt;Edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, new functionality becomes harder to maintain and evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  How structured editing solves these problems
&lt;/h2&gt;

&lt;p&gt;The Modern Rich Text Editor takes a different approach.&lt;/p&gt;

&lt;p&gt;Instead of treating HTML as the document, it treats content as a structured, typed document model governed by a schema.&lt;/p&gt;

&lt;p&gt;Every edit is validated against rules that define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which content is allowed&lt;/li&gt;
&lt;li&gt;Where content can exist&lt;/li&gt;
&lt;li&gt;How content behaves&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;HTML becomes an output format rather than the source of truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistent content representation
&lt;/h3&gt;

&lt;p&gt;Whether users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Bold&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use keyboard shortcuts&lt;/li&gt;
&lt;li&gt;Paste HTML from another source&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the content is normalized into a single representation within the document model.&lt;/p&gt;

&lt;p&gt;This prevents content drift and creates a canonical format for storage and processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema-protected content blocks
&lt;/h3&gt;

&lt;p&gt;Interactive content such as callouts, checklists, and collapsible sections are defined as document nodes with explicit rules.&lt;/p&gt;

&lt;p&gt;As users edit content, the schema helps maintain valid document structure instead of allowing partially broken markup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reliable information about changes
&lt;/h3&gt;

&lt;p&gt;Every modification is represented as a structured operation on the document model.&lt;/p&gt;

&lt;p&gt;Applications can understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What changed&lt;/li&gt;
&lt;li&gt;Where it changed&lt;/li&gt;
&lt;li&gt;Which nodes were affected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without comparing large HTML strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extensibility built into the architecture
&lt;/h3&gt;

&lt;p&gt;New functionality integrates with the document model rather than relying on browser editing behavior.&lt;/p&gt;

&lt;p&gt;Developers can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute commands through a typed API&lt;/li&gt;
&lt;li&gt;Customize keyboard shortcuts&lt;/li&gt;
&lt;li&gt;Intercept actions&lt;/li&gt;
&lt;li&gt;Extend behavior through the Headless Editor architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why we built a new editor instead of modifying the existing one
&lt;/h2&gt;

&lt;p&gt;The limitations discussed above trace back to a fundamental design decision: &lt;strong&gt;&lt;code&gt;HTML is the document&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Changing that assumption would affect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Value handling&lt;/li&gt;
&lt;li&gt;Event contracts&lt;/li&gt;
&lt;li&gt;Editing behavior&lt;/li&gt;
&lt;li&gt;Storage strategies&lt;/li&gt;
&lt;li&gt;Existing integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structured editing isn’t simply a feature.&lt;/p&gt;

&lt;p&gt;It’s a different definition of what the document is.&lt;/p&gt;

&lt;p&gt;Creating a new editor allowed us to deliver a modern, structured foundation without introducing breaking changes for existing Rich Text Editor customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture behind the Modern Rich Text Editor
&lt;/h2&gt;

&lt;p&gt;The editor was designed around five core principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One canonical document model&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Content exists in a structured document model.&lt;/li&gt;
&lt;li&gt;JSON is the default format because it preserves structure directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A UI-independent engine&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The document model, schema, commands, and editing behavior live inside the Syncfusion Headless Editor.&lt;/li&gt;
&lt;li&gt;The user interface sits on top of that foundation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A proven editing framework&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Rather than building a new editing engine from scratch, we chose &lt;code&gt;ProseMirror&lt;/code&gt; as the underlying document and editing framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit change information&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Changes are reported as structured operations instead of requiring HTML diffing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Programmatic editing&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Formatting and content manipulation are performed through typed commands rather than DOM manipulation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why ProseMirror
&lt;/h2&gt;

&lt;p&gt;We chose &lt;code&gt;ProseMirror&lt;/code&gt; because it offers four key capabilities that support our goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema validation:&lt;/strong&gt; Content must conform to defined document structures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions:&lt;/strong&gt; Every edit becomes a discrete and describable operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extension architecture:&lt;/strong&gt; New capabilities can be added without rewriting editing behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future collaboration readiness:&lt;/strong&gt; Its document model was designed to support collaborative editing scenarios and advanced content workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We built the Syncfusion schema, commands, extensions, and UI experiences on top of that foundation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Document Model
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;valueFormat="json"&lt;/code&gt;, content is represented as an &lt;code&gt;EditorDocument&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"document"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"schemaVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heading"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"attrs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Release checklist"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When HTML is loaded or pasted into the editor, it is parsed into this document model and normalized according to the schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing between JSON and HTML storage
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Choose JSON when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to query or analyze content.&lt;/li&gt;
&lt;li&gt;AI applications need access to structured content.&lt;/li&gt;
&lt;li&gt;You need to version documents.&lt;/li&gt;
&lt;li&gt;You plan to add review or collaboration features in the future.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose HTML when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existing systems consume HTML directly.&lt;/li&gt;
&lt;li&gt;Your primary requirement is to render content immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Recommended approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For many applications, storing content as JSON and generating HTML when needed offers a practical balance. JSON preserves the document structure, while HTML can be used for rendering and indexing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured editing: A quick example
&lt;/h2&gt;

&lt;p&gt;Theory is useful, but the best way to understand a structured document model is to see it in action.&lt;/p&gt;

&lt;p&gt;In this example, we’ll build a React application that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loads content as a JSON document&lt;/li&gt;
&lt;li&gt;Persists content to storage&lt;/li&gt;
&lt;li&gt;Tracks editing activity&lt;/li&gt;
&lt;li&gt;Adds custom keyboard shortcuts&lt;/li&gt;
&lt;li&gt;Inserts structured content programmatically&lt;/li&gt;
&lt;li&gt;Generates HTML whenever it’s needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This walkthrough uses &lt;a href="https://help.syncfusion.com/rich-text-editor-sdk/react/richtexteditor-ui/getting-started" rel="noopener noreferrer"&gt;React&lt;/a&gt;, Vite, and TypeScript. Equivalent packages are also available for &lt;a href="https://help.syncfusion.com/rich-text-editor-sdk/javascript/richtexteditor-ui/getting-started" rel="noopener noreferrer"&gt;JavaScript&lt;/a&gt;, &lt;a href="https://help.syncfusion.com/rich-text-editor-sdk/angular/richtexteditor-ui/getting-started" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;, and &lt;a href="https://help.syncfusion.com/rich-text-editor-sdk/vue/richtexteditor-ui/getting-started" rel="noopener noreferrer"&gt;Vue&lt;/a&gt; applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Prerequisites&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before you begin, make sure that &lt;code&gt;Node.js&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt; are installed on your machine. You’ll also need a Syncfusion license key or evaluation trial for development beyond the evaluation period.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the project and install the packages
&lt;/h3&gt;

&lt;p&gt;Create a new React application with Vite and install the Modern Rich Text Editor packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest my-app &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; react-ts
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @syncfusion/ej2-react-richtexteditor-ui@35.1.37 &lt;span class="nt"&gt;--save&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @syncfusion/ej2-tailwind3-theme@35.1.37 &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the theme
&lt;/h3&gt;

&lt;p&gt;Import the Tailwind 3 theme into your application stylesheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'../node_modules/@syncfusion/ej2-tailwind3-theme/styles/richtexteditor-ui/index.css'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create an editor that loads, saves, and audits content
&lt;/h3&gt;

&lt;p&gt;The following example demonstrates several capabilities of the Modern Rich Text Editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading an &lt;code&gt;EditorDocument&lt;/code&gt; from storage&lt;/li&gt;
&lt;li&gt;Storing content as structured JSON&lt;/li&gt;
&lt;li&gt;Auditing user interactions through events&lt;/li&gt;
&lt;li&gt;Creating custom keyboard shortcuts&lt;/li&gt;
&lt;li&gt;Inserting a callout programmatically using the commands API&lt;/li&gt;
&lt;li&gt;Generating HTML on demand from the document model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Replace &lt;code&gt;src/App.tsx&lt;/code&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;RichTextEditorUIComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SlashCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ChangeEventArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ActionCompleteEventArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EditorDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EditorKeyBindingMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@syncfusion/ej2-react-richtexteditor-ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STORAGE_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;release-notes-doc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultDocument&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EditorDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;marks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;schemaVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Release checklist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paragraph&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ship the &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;marks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; on Friday.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Restore saved content, falling back to the default if it's missing or malformed.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadDocument&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;EditorDocument&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STORAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;defaultDocument&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaultDocument&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;defaultDocument&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Custom shortcuts. Ctrl maps to Cmd on macOS automatically.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keyBindings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EditorKeyBindingMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code-block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl+alt+c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;checklist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl+alt+l&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;editorRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RichTextEditorUIComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;initialDocument&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadDocument&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No changes yet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;persist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EditorDocument&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STORAGE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your API call.&lt;/span&gt;
      &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Saved at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleTimeString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Save failed. Your changes are still in the editor.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Fires when content is committed: on blur, after the autosave interval, or after save().&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChangeEventArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Log user-initiated edits that changed the document, for example for an audit trail.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onActionComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActionCompleteEventArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isInteracted&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentChanged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[audit] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveNow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertCallout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;editorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;callout&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;callout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getHtml&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RichTextEditorUIComponent&lt;/span&gt;
        &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;editorRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"400px"&lt;/span&gt;
        &lt;span class="na"&gt;valueFormat&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"json"&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialDocument&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Type / for commands"&lt;/span&gt;
        &lt;span class="na"&gt;slashCommandSettings&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;keyBindings&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;keyBindings&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;actionComplete&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onActionComplete&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Inject&lt;/span&gt; &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SlashCommand&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RichTextEditorUIComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;saveNow&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Save now&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;insertCallout&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Insert info callout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logHtml&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Log HTML&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run the application
&lt;/h3&gt;

&lt;p&gt;Start the development server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the editor loads, try the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type &lt;code&gt;/&lt;/code&gt; and insert a heading, checklist, or another supported block. Click outside the editor to commit the change and trigger persistence.&lt;/li&gt;
&lt;li&gt;Refresh the page. The editor restores the previously saved JSON document from local storage.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Ctrl + Alt + L&lt;/code&gt; to create a checklist using the custom keyboard shortcut.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Log HTML&lt;/code&gt; and inspect the browser console to see the HTML generated from the structured document model.&lt;/li&gt;
&lt;li&gt;Open the console and perform formatting actions. User-initiated edits that modify the document appear as audit entries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This example highlights one of the key differences between the Modern Rich Text Editor and traditional HTML-based editors: content is stored as a structured document and HTML is generated only when you need it. That gives applications a consistent content model for storage, processing, and future workflow integration while preserving HTML compatibility for rendering and publishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending the editor
&lt;/h2&gt;

&lt;p&gt;The Modern Rich Text Editor supports multiple customization layers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extension Point&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;strong&gt;Use Case&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;strong&gt;keyBindings&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Customize keyboard shortcuts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;strong&gt;commands()&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execute actions from custom UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;&lt;strong&gt;actionBegin&lt;/strong&gt;&lt;/code&gt; / &lt;code&gt;&lt;strong&gt;actionComplete&lt;/strong&gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Intercept, cancel, or audit actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toolbar and Slash Commands&lt;/td&gt;
&lt;td&gt;Customize authoring experiences&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For deeper customization, developers can leverage the Headless Editor’s schema system, extension architecture, migration support, and custom &lt;code&gt;NodeViews&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating existing HTML content
&lt;/h2&gt;

&lt;p&gt;Migration doesn’t have to be all-or-nothing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Continue loading existing HTML.&lt;/li&gt;
&lt;li&gt;Evaluate real-world content inside the editor.&lt;/li&gt;
&lt;li&gt;Convert content into structured JSON.&lt;/li&gt;
&lt;li&gt;Store both representations.&lt;/li&gt;
&lt;li&gt;Transition applications to JSON when ready.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach minimizes migration risk while allowing teams to adopt structured editing incrementally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stored JSON compatibility:&lt;/strong&gt; Keep &lt;code&gt;schemaVersion&lt;/code&gt; with every stored document so you can detect and migrate older content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanitization:&lt;/strong&gt; Treat HTML produced from user content as untrusted wherever you render it outside the editor. Sanitize it on output, and validate link URLs and image sources in your back end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence:&lt;/strong&gt; The change event fires on commit, not on every keystroke. Pair it with an explicit save action for forms, and save before navigation in single-page apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side rendering:&lt;/strong&gt; The editor needs a browser DOM. In SSR frameworks such as Next.js, render it only on the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility:&lt;/strong&gt; The product page lists &lt;a href="https://www.syncfusion.com/rich-text-editor-sdk/react-modern-rich-text-editor" rel="noopener noreferrer"&gt;WAI-ARIA and keyboard support&lt;/a&gt;. Still, test the blocks your users actually create (tables, callouts, collapsible sections) with your screen readers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle size:&lt;/strong&gt; Features such as slash commands are separate modules that you inject only when needed. Measure your build to confirm the effect in your app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Areas we’re exploring
&lt;/h2&gt;

&lt;p&gt;The structured document model creates a foundation for future capabilities such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-assisted authoring&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Review workflows&lt;/li&gt;
&lt;li&gt;Track changes&lt;/li&gt;
&lt;li&gt;Real-time collaboration&lt;/li&gt;
&lt;li&gt;Advanced paste cleanup&lt;/li&gt;
&lt;li&gt;Additional import/export formats&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;These capabilities are not included in version Essential Studio 35.1.37 and should not be interpreted as release commitments.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Evaluation checklist
&lt;/h2&gt;

&lt;p&gt;Before adopting the Modern Rich Text Editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate real content against the editor schema&lt;/li&gt;
&lt;li&gt;Decide on JSON, HTML, or hybrid storage&lt;/li&gt;
&lt;li&gt;Design save behavior around commit timing&lt;/li&gt;
&lt;li&gt;Sanitize generated HTML where required&lt;/li&gt;
&lt;li&gt;Test keyboard and accessibility workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: What is the Modern Rich Text Editor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s a &lt;strong&gt;WYSIWYG&lt;/strong&gt; editor in the Syncfusion Rich Text Editor SDK that stores content as a structured document instead of as HTML. You format text with the toolbar or slash commands as usual, but behind the scenes every paragraph, heading, list, and piece of formatting is saved as a typed node in a JSON document. It’s built on the Syncfusion Headless Editor, which uses &lt;strong&gt;&lt;code&gt;ProseMirror&lt;/code&gt;&lt;/strong&gt; as its editing engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: What does structured editing mean in simple terms?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In HTML editing, the editor saves whatever markup is on screen, so the same bold word can be stored as &lt;strong&gt;&lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt;&lt;/strong&gt; , or a styled &lt;strong&gt;&lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;&lt;/strong&gt;. In structured editing, the editor keeps a clean data model of the content, such as “a level 2 heading” or “the word beta with a bold mark,” and checks every edit against a set of rules called a schema. The screen is only a view of that model, so content is stored the same way every time, no matter how it was typed or pasted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: Can I still get HTML from the editor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Call &lt;code&gt;getHtml()&lt;/code&gt; at any time to get the current content as an HTML string, for example, to display it on a page, send it in an email, or index it for search. To make the value property itself an HTML string, set &lt;code&gt;valueFormat&lt;/code&gt; to &lt;code&gt;html&lt;/code&gt;. For plain text, call &lt;code&gt;getText()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: Can I load the HTML content I already have?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Set &lt;code&gt;valueFormat&lt;/code&gt; to &lt;code&gt;html&lt;/code&gt; and pass your existing HTML as value. The editor converts it into its document model, so different markup for the same formatting is cleaned up into one form. Markup the editor doesn’t support may be simplified or removed, so test with a sample of your real content first. The “Migrating existing HTML content” section walks through a step-by-step approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5: Can I customize or extend the editor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, at two levels. In the Modern Rich Text Editor, you can change keyboard shortcuts (&lt;code&gt;keyBindings&lt;/code&gt;), apply formatting from your own buttons (&lt;code&gt;commands()&lt;/code&gt;), intercept, cancel, or log user actions (&lt;code&gt;actionBegin&lt;/code&gt; and &lt;code&gt;actionComplete&lt;/code&gt;), and choose which toolbar and slash command items appear. For bigger changes, such as defining your own content types with custom schemas and &lt;code&gt;NodeViews&lt;/code&gt;, use the extension architecture of the Headless Editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/rich-text-editor-sdk/react-modern-rich-text-editor" rel="noopener noreferrer"&gt;Modern Rich Text Editor&lt;/a&gt; represents a fundamental shift from HTML-centric editing to structured document editing.&lt;/p&gt;

&lt;p&gt;By treating content as a schema-driven document model rather than raw HTML, it provides consistent content representation, richer change information, stronger extensibility, and a foundation for modern content workflows.&lt;/p&gt;

&lt;p&gt;If you’re evaluating editor architectures for your next application, the Modern Rich Text Editor provides a path to structured content while preserving compatibility with existing HTML workflows.&lt;/p&gt;

&lt;p&gt;Start with the &lt;a href="https://help.syncfusion.com/rich-text-editor-sdk/react/richtexteditor-ui/getting-started" rel="noopener noreferrer"&gt;React Getting started guide&lt;/a&gt;, explore the &lt;a href="https://ej2.syncfusion.com/react/demos/#/tailwind3/rich-text-editor-ui/basic-editing" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;, and tell us what you’d like to build next. You can explore the complete &lt;a href="https://www.syncfusion.com/products/whatsnew" rel="noopener noreferrer"&gt;What’s New&lt;/a&gt; and &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; pages to discover the features most relevant to your project.&lt;/p&gt;

&lt;p&gt;For existing Syncfusion customers, the latest version of Essential Studio is available from the &lt;a href="https://www.syncfusion.com/sales/pricing" rel="noopener noreferrer"&gt;license and downloads&lt;/a&gt; page. New users can explore the capabilities through the &lt;a href="https://www.syncfusion.com/downloads" rel="noopener noreferrer"&gt;30-day free trial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have questions or feedback, reach out through our &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support forums&lt;/a&gt;, &lt;a href="https://support.syncfusion.com/" rel="noopener noreferrer"&gt;support portal&lt;/a&gt;, or &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback portal&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/react-rich-text-editor-mentions" rel="noopener noreferrer"&gt;How to Add Mentions and Slash Commands in a React Rich Text Editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/react-rich-text-editor-xss-prevention" rel="noopener noreferrer"&gt;How to Prevent XSS Attacks in React Rich Text Editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/merge-fields-in-react-rich-text-editor" rel="noopener noreferrer"&gt;How to Use Merge Fields in React Rich Text Editor for Dynamic Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/emojis-in-react-rich-text-editor" rel="noopener noreferrer"&gt;Introducing Emoji Icons Support in the React Rich Text Editor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>reactdevelopment</category>
      <category>richtexteditor</category>
      <category>contentmanagement</category>
    </item>
    <item>
      <title>C# 15 Union Types for API Results: Examples, Trade-Offs, and Migration</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Wed, 21 Oct 2026 14:17:46 +0000</pubDate>
      <link>https://dev.to/syncfusion/c-15-union-types-for-api-results-examples-trade-offs-and-migration-42h2</link>
      <guid>https://dev.to/syncfusion/c-15-union-types-for-api-results-examples-trade-offs-and-migration-42h2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;C# 15 union types let you represent a fixed set of possible outcomes, making API results clearer and helping the compiler identify unhandled cases. Learn how to use them in ASP.NET Core, compare them with Result, interfaces, and exceptions, and understand the trade-offs, serialization limits, and migration considerations before adopting them.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine building an e-commerce API that creates orders. An order might be created successfully, rejected because the quantity is invalid, or failed because the requested product cannot be found.&lt;/p&gt;

&lt;p&gt;Your code needs to handle each outcome correctly. A return type that does not clearly represent these possibilities can leave callers checking flags, interpreting ambiguous values, or using exceptions for expected business outcomes.&lt;/p&gt;

&lt;p&gt;C# 15 introduces union types, which let you define a fixed set of possible types for a value. The compiler can then help identify cases that a switch expression does not handle.&lt;/p&gt;

&lt;p&gt;Union types are a preview feature in the .NET 11 development cycle. Their syntax and supporting behavior may change before the final release, so verify the latest &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-15" rel="noopener noreferrer"&gt;C# 15 documentation&lt;/a&gt; before adopting them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is C# 15 union types?
&lt;/h2&gt;

&lt;p&gt;A union type represents a value that can be one of several predefined types. Unlike an interface or base class, its case types do not need to share an inheritance hierarchy.&lt;/p&gt;

&lt;p&gt;For example, an order-processing operation can produce three outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The order is created successfully.&lt;/li&gt;
&lt;li&gt;The request is invalid.&lt;/li&gt;
&lt;li&gt;The requested product cannot be found.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can represent these outcomes using records and a union:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;OrderCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;InvalidOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;ProductNotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ProductCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="nf"&gt;CreateOrderResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;OrderCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;InvalidOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ProductNotFound&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CreateOrderResult&lt;/code&gt; union can contain an &lt;code&gt;OrderCreated&lt;/code&gt;, &lt;code&gt;InvalidOrder&lt;/code&gt;, or &lt;code&gt;ProductNotFound&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;You can assign a case directly to the union because the compiler provides implicit conversions from its declared case types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;CreateOrderResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If more than one case type is equally applicable to a source value, the conversion is ambiguous and the compiler reports an error. Use distinct case types or explicitly create the intended case to avoid ambiguity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle every case with pattern matching
&lt;/h2&gt;

&lt;p&gt;One of the most useful features of union types is exhaustive pattern matching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;DescribeResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateOrderResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;OrderCreated&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;$"Order &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; was created."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;InvalidOrder&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;$"Invalid order: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="n"&gt;ProductNotFound&lt;/span&gt; &lt;span class="n"&gt;notFound&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;$"Product &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; was not found."&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The switch expression handles every declared case. If you add another case to &lt;code&gt;CreateOrderResult&lt;/code&gt;, the compiler can report an incomplete switch expression with warning &lt;code&gt;CS8509&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can make this warning fail the build by adding the following configuration to your project file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;WarningsAsErrors&amp;gt;&lt;/span&gt;$(WarningsAsErrors);CS8509&lt;span class="nt"&gt;&amp;lt;/WarningsAsErrors&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when an operation has a known set of outcomes and every caller must consider each possibility. For details, see Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union" rel="noopener noreferrer"&gt;C# union types reference&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using C# 15 union types in ASP.NET Core APIs
&lt;/h2&gt;

&lt;p&gt;Union types can separate business outcomes from HTTP response handling. The application service determines what happened, while the controller maps that outcome to an appropriate HTTP response.&lt;/p&gt;

&lt;p&gt;Let’s implement a simplified order-creation API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Define the request and service
&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;CreateOrderResult&lt;/code&gt; union from the previous section.&lt;/p&gt;

&lt;p&gt;First, define the request and service interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ProductCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Quantity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateOrderResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;CreateOrderRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service returns one of the declared result types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateOrderResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;CreateOrderRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Quantity&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateOrderResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"Quantity must be greater than zero."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// A real application would query its product catalog here.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="nf"&gt;ProductExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductCode&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateOrderResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProductNotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductCode&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CreateOrderResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;ProductExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;productCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;productCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"P100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simplified service validates the quantity, checks product availability, and returns the corresponding result. In a production application, replace the example product check and generated order ID with actual catalog and order-creation logic.&lt;/p&gt;

&lt;p&gt;Register the service with dependency injection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Map the results to HTTP responses
&lt;/h3&gt;

&lt;p&gt;The controller handles each result and returns the appropriate HTTP status code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/orders"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IOrderService&lt;/span&gt; &lt;span class="n"&gt;_orderService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrdersController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_orderService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OrderCreated&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status201Created&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProblemDetails&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status400BadRequest&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProblemDetails&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status404NotFound&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;CreateOrderRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CreateOrderResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_orderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;OrderCreated&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nf"&gt;CreatedAtAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetById&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

            &lt;span class="n"&gt;InvalidOrder&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nf"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProblemDetails&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Invalid order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status400BadRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Extensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"invalid_order"&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}),&lt;/span&gt;

            &lt;span class="n"&gt;ProductNotFound&lt;/span&gt; &lt;span class="n"&gt;notFound&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ProblemDetails&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                        &lt;span class="s"&gt;$"Product &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; was not found."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status404NotFound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Extensions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"product_not_found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"productCode"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProductCode&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id:guid}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ProducesResponseType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status200OK&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GetById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;OrderId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example maps each business outcome to an HTTP response:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Business outcome&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;HTTP response&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Order created&lt;/td&gt;
&lt;td&gt;201 Created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invalid order&lt;/td&gt;
&lt;td&gt;400 Bad Request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product not found&lt;/td&gt;
&lt;td&gt;404 Not Found&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The service does not need to know about HTTP status codes. The controller handles that responsibility and returns &lt;code&gt;ProblemDetails&lt;/code&gt; for the error responses.&lt;/p&gt;

&lt;p&gt;The example’s status codes and response bodies are illustrative. An actual API should follow its documented contract and established error-handling conventions.&lt;/p&gt;

&lt;p&gt;The key benefit is that the controller handles each declared result explicitly. If you add a case such as &lt;code&gt;InsufficientInventory&lt;/code&gt;, the compiler can help identify switch expressions that need updating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Union types vs. existing C# approaches
&lt;/h2&gt;

&lt;p&gt;Union types are not a replacement for every result-handling pattern. The right choice depends on the problem your application needs to solve.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Best suited for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Main consideration&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nullable return value&lt;/td&gt;
&lt;td&gt;A value may be absent, and that is the only alternative&lt;/td&gt;
&lt;td&gt;Does not describe several distinct outcomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exceptions&lt;/td&gt;
&lt;td&gt;Unexpected failures that cannot be handled locally&lt;/td&gt;
&lt;td&gt;Can obscure expected business outcomes if overused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;strong&gt;Result&amp;lt;T&amp;gt;&lt;/strong&gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A consistent success-or-failure pattern across operations&lt;/td&gt;
&lt;td&gt;May need additional modeling for multiple distinct outcomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;&lt;strong&gt;OneOf&lt;/strong&gt;&lt;/code&gt; or a similar library&lt;/td&gt;
&lt;td&gt;Union-like behavior before native unions are available&lt;/td&gt;
&lt;td&gt;Adds a dependency and library-specific conventions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interfaces or abstract records&lt;/td&gt;
&lt;td&gt;Types that share behavior or an extensible contract&lt;/td&gt;
&lt;td&gt;Do not inherently provide the same closed-case checking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C# 15 union types&lt;/td&gt;
&lt;td&gt;A value must be one of a fixed set of alternatives&lt;/td&gt;
&lt;td&gt;Requires preview-feature adoption and compatibility evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Union types vs. Result
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; abstraction can be a good choice when an application needs consistent error handling, validation details, or helper methods for chaining operations.&lt;/p&gt;

&lt;p&gt;Union types are especially useful when an operation has several distinct outcomes that callers should handle separately.&lt;/p&gt;

&lt;p&gt;For example, an inventory operation might return &lt;code&gt;InventoryReserved&lt;/code&gt;, &lt;code&gt;ProductNotFound&lt;/code&gt;, or &lt;code&gt;InsufficientInventory&lt;/code&gt;. Each case communicates a specific outcome.&lt;/p&gt;

&lt;p&gt;If your existing &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; implementation already handles the problem well, switching to union types simply because they are newer may not justify the migration cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Union types vs. interfaces
&lt;/h2&gt;

&lt;p&gt;Interfaces are appropriate when different implementations must share a contract and new implementations may be introduced by other parts of the application.&lt;/p&gt;

&lt;p&gt;Union types are better suited to a deliberately closed set of alternatives. The compiler knows which cases belong to the union and can help verify that all cases are handled.&lt;/p&gt;

&lt;p&gt;A closed class hierarchy may also be suitable when the cases are related classes that naturally share a base type. Consider a union when the alternatives are unrelated types or should not be forced into a common inheritance hierarchy.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you use union types?
&lt;/h2&gt;

&lt;p&gt;Union types are worth evaluating when an application has a known set of outcomes, and every caller should consider them deliberately.&lt;/p&gt;

&lt;p&gt;Consider them when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An API operation returns several expected business outcomes.&lt;/li&gt;
&lt;li&gt;A domain model has a fixed set of result types.&lt;/li&gt;
&lt;li&gt;Existing result objects permit confusing or contradictory combinations.&lt;/li&gt;
&lt;li&gt;You want compiler feedback when new cases are added.&lt;/li&gt;
&lt;li&gt;Your team controls the relevant consumers and can manage compatibility changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An existing approach may be more suitable when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your production environment cannot use preview features.&lt;/li&gt;
&lt;li&gt;External consumers need to introduce their own implementations.&lt;/li&gt;
&lt;li&gt;An established result abstraction already meets your needs.&lt;/li&gt;
&lt;li&gt;A public library cannot easily manage source-compatibility changes.&lt;/li&gt;
&lt;li&gt;A nullable value or simpler result type is sufficient.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Consider performance for value types
&lt;/h2&gt;

&lt;p&gt;Compiler-generated union declarations use an &lt;code&gt;object?&lt;/code&gt; reference to hold the active value. As a result, value-type cases are boxed in the standard generated representation.&lt;/p&gt;

&lt;p&gt;This does not automatically make union types too slow. However, if your union contains value types and is used in a performance-sensitive area, measure the actual workload. The C# union model also supports custom implementations that can avoid boxing by providing an appropriate access pattern.&lt;/p&gt;

&lt;p&gt;See Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/union" rel="noopener noreferrer"&gt;union types reference&lt;/a&gt; for implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should you consider before migrating?
&lt;/h2&gt;

&lt;p&gt;Changing a method’s return type is only one part of adopting union types. Callers, public contracts, and serialized data may also be affected.&lt;/p&gt;

&lt;p&gt;Follow these steps to plan the migration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Review the existing result model.&lt;/strong&gt; Identify methods that return custom result classes, callers that inspect success flags, and code that depends on existing error properties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define meaningful cases.&lt;/strong&gt; Create a separate type for each outcome that carries distinct information. Avoid cases that represent the same business meaning under different names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrate internal consumers first.&lt;/strong&gt; Introduce the union in a controlled area and update the relevant switch expressions. Keep existing HTTP response formats unchanged if clients are not intended to see a contract change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test compatibility before removing the old model.&lt;/strong&gt; Check response status codes, response bodies, OpenAPI descriptions, generated clients, and any persisted or transmitted data that depends on the old result type.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Adding a union case can require existing consumers to change their code. For public APIs and libraries, review this impact before releasing the new case.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on JSON serialization
&lt;/h2&gt;

&lt;p&gt;Starting with .NET 11, &lt;code&gt;System.Text.Json&lt;/code&gt; supports serialization and deserialization of C# union types. The serializer writes the active case and must identify the correct case when reading JSON.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonUnion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;TypeClassifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonUnionTypeStructuralClassifier&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="nf"&gt;CreateOrderResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;OrderCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;InvalidOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ProductNotFound&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structural classifier can distinguish object cases using their root-level properties. For this to work, the case shapes must be sufficiently distinct.&lt;/p&gt;

&lt;p&gt;For example, two cases with overlapping property structures may be ambiguous. Likewise, &lt;code&gt;Guid&lt;/code&gt; and &lt;code&gt;string&lt;/code&gt; both use JSON strings, so the default classifier cannot distinguish them reliably. A custom classifier may be needed when the default rules cannot select one case.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;JsonSerializerOptions.Web&lt;/code&gt;, an int may also be read from a JSON string. This can make &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;string&lt;/code&gt; union cases ambiguous during deserialization, requiring a custom classifier.&lt;/p&gt;

&lt;p&gt;Keep these points in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON serialization does not automatically guarantee that every union can be deserialized unambiguously.&lt;/li&gt;
&lt;li&gt;Changes to property names or required properties may affect how a structural classifier identifies a case.&lt;/li&gt;
&lt;li&gt;If you control the JSON contract and need a stable discriminator, a closed hierarchy with explicit polymorphic serialization may be more appropriate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the ASP.NET Core example above, the controller maps the result into an HTTP response rather than returning the union itself. Therefore, the union is an application-layer result and does not automatically become the API’s JSON contract.&lt;/p&gt;

&lt;p&gt;For the supported classifiers and limitations, see Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/union-types" rel="noopener noreferrer"&gt;System.Text.Json union serialization documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: Can a C# 15 union contain null?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. A union can include a nullable case type, such as &lt;code&gt;string?&lt;/code&gt;. The default value of a generated union can also have no active case, so some switch expressions may need to account for &lt;code&gt;null&lt;/code&gt;. If &lt;code&gt;null&lt;/code&gt; represents a meaningful business outcome, a named case type usually communicates the intent more clearly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: Can a union contain value types such as int or Guid?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Value types can be union cases. The standard compiler-generated representation stores the value through an &lt;code&gt;object?&lt;/code&gt; reference, so value-type cases are boxed. Consider performance testing if the union is used in an allocation-sensitive part of the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: Do C# 15 union types require a NuGet package?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Native union declarations do not require a third-party union package. They do require a compatible C# compiler and .NET SDK. Check the current preview requirements before creating a project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: Can union types replace enums?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not in every situation. Enums work well for simple named states. Union types are more useful when each alternative has a different type, carries different data, or requires different handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5: Are C# 15 union types ready for production?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Union types remain a preview feature in the C# 15 and .NET 11 development cycle. Their syntax and supporting behavior may change before the final release.&lt;/p&gt;

&lt;p&gt;Try them in a controlled project first. Verify the SDK and compiler versions, and test serialization, API contracts, tooling, and performance before considering production adoption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;C# 15 union types provide a way to represent a fixed set of possible outcomes and help developers identify unhandled cases during compilation.&lt;/p&gt;

&lt;p&gt;For ASP.NET Core APIs, they can make business results explicit while allowing controllers to map those results to HTTP responses. They can also represent closed sets of domain states without forcing unrelated types into a shared inheritance hierarchy.&lt;/p&gt;

&lt;p&gt;Union types are not automatically better than &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt;, interfaces, exceptions, or existing libraries. Their value depends on whether the application benefits from a closed set of alternatives and compiler-checked handling.&lt;/p&gt;

&lt;p&gt;Before adopting them, evaluate the feature in a controlled project, verify the current preview behavior, and test compatibility with your API contracts. Choose union types when they solve a real modeling problem, not simply because they are a new C# feature.&lt;/p&gt;

</description>
      <category>aspnetcore</category>
      <category>dotnetdevelopment</category>
      <category>csharp</category>
      <category>architecture</category>
    </item>
    <item>
      <title>What’s New in Syncfusion Document SDKs 2026 Volume 3: AI Chunking, PDF Security, and Data Extraction</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Fri, 16 Oct 2026 14:52:27 +0000</pubDate>
      <link>https://dev.to/syncfusion/whats-new-in-syncfusion-document-sdks-2026-volume-3-ai-chunking-pdf-security-and-data-extraction-3c8b</link>
      <guid>https://dev.to/syncfusion/whats-new-in-syncfusion-document-sdks-2026-volume-3-ai-chunking-pdf-security-and-data-extraction-3c8b</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Syncfusion Document SDKs 2026 Volume 3 brings updates across its document-processing libraries, including a .NET Document Chunking Library (Preview) for AI-powered retrieval, a production-ready JavaScript PDF Library, PDF security and digital signature validation, SVG-to-PDF conversion, and expanded data extraction with barcode and QR code recognition.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Document processing involves more than creating and converting files. You may need to prepare content for AI retrieval, verify whether a signed contract has been modified, or extract data from existing documents.&lt;/p&gt;

&lt;p&gt;The Syncfusion&lt;sup&gt;®&lt;/sup&gt; Document SDKs &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;2026 Volume 3&lt;/a&gt; release introduces updates across several libraries. To make these updates easier to explore, we’re covering them in two blogs. In this blog, we’ll focus on the PDF, Document Chunking, and Data Extraction libraries, exploring their new capabilities and how they can help developers solve real-world challenges.&lt;/p&gt;

&lt;p&gt;In our upcoming blog, we’ll cover the updates to the Word, Excel, and PowerPoint libraries.&lt;/p&gt;

&lt;p&gt;Let’s look at what’s new in each library.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introducing .NET Document Chunking&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The new &lt;a href="https://www.syncfusion.com/document-sdk/net-document-chunking-library" rel="noopener noreferrer"&gt;.NET Document Chunking Library&lt;/a&gt; (Preview) helps developers prepare documents for AI-powered retrieval by dividing content into meaningful chunks while preserving structure, hierarchy, and metadata.&lt;/p&gt;

&lt;p&gt;This is useful when building an AI assistant that retrieves information from company policies, technical manuals, and product documentation. Unlike fixed-length splitting, which can separate related information, document chunking helps retain meaningful sections for retrieval.&lt;/p&gt;

&lt;p&gt;The library supports PDF, Word, Excel, PowerPoint, and Markdown formats. Developers can use it to prepare content for retrieval-augmented generation (RAG) and semantic-search applications without implementing separate chunking logic for each format.&lt;/p&gt;

&lt;p&gt;For instance, an assistant answering a question about company policy can retrieve relevant sections from the prepared document chunks.&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript PDF Library is now production-ready
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/document-sdk/javascript-pdf-library" rel="noopener noreferrer"&gt;JavaScript PDF Library&lt;/a&gt; moves from preview to production-ready status in this release.&lt;/p&gt;

&lt;p&gt;Along with existing PDF creation, editing, page manipulation, form, and annotation capabilities, Volume 3 introduces updates to PDF security, digital signature validation, and document manipulation.&lt;/p&gt;

&lt;p&gt;These capabilities are useful for applications that handle contracts, financial records, approval documents, and other sensitive information.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Strengthen PDF Security&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Applications that generate confidential financial statements or customer agreements may need to control who can open a document, modify its contents, or perform specific actions.&lt;/p&gt;

&lt;p&gt;The JavaScript PDF Library supports security capabilities such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RC4 and AES &lt;a href="https://www.syncfusion.com/document-sdk/javascript-pdf-library/encryption" rel="noopener noreferrer"&gt;encryption&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;User and owner passwords&lt;/li&gt;
&lt;li&gt;Document permission restrictions&lt;/li&gt;
&lt;li&gt;Digital signatures&lt;/li&gt;
&lt;li&gt;Signature validation&lt;/li&gt;
&lt;li&gt;Long-Term Validation (LTV) signatures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fce7r6wm18me5xq69ljs1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fce7r6wm18me5xq69ljs1.png" alt="Secure PDF documents with encryption, passwords, permissions, and digital signatures." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
Secure PDF documents



&lt;p&gt;Encryption, passwords, and permission settings help control document access and operations. Digital signatures help verify document integrity and signing information, while LTV support embeds relevant validation information to support future signature verification.&lt;/p&gt;

&lt;p&gt;These capabilities are useful when applications need to protect sensitive PDFs or manage signed documents for long-term storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Validate Digital Signatures&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Consider an application that receives digitally signed contracts from customers or business partners. Before sending these documents into an approval workflow, the application may need to check whether the documents have changed after signing and whether the signatures meet its validation requirements.&lt;/p&gt;

&lt;p&gt;The JavaScript PDF Library provides capabilities to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check document integrity.&lt;/li&gt;
&lt;li&gt;Inspect signing certificate chains.&lt;/li&gt;
&lt;li&gt;Evaluate timestamp information.&lt;/li&gt;
&lt;li&gt;Check certificate revocation status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm75o0vgcl2xjmz9hywq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm75o0vgcl2xjmz9hywq8.png" alt="Validate PDF signatures" width="799" height="452"&gt;&lt;/a&gt;&lt;/p&gt;
Validate PDF signatures



&lt;p&gt;These checks can help applications identify potential signature-validation issues before documents move into downstream business processes.&lt;/p&gt;

&lt;p&gt;For example, a contract-management system could validate incoming signed PDFs and flag documents that require additional review.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Generate&lt;/strong&gt;  &lt;strong&gt;Multi-Page PDF Reports with Tables&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Beyond security, the JavaScript PDF Library includes enhancements for generating structured PDF reports.&lt;/p&gt;

&lt;p&gt;Applications that generate invoices, statements, and reports often need to display large tables across multiple pages. The PDF grid supports structured tables with customizable layouts, formatting, and automatic pagination.&lt;/p&gt;

&lt;p&gt;Developers can use these capabilities to generate multi-page business documents without manually managing every page break.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy76pq2yerexccr5x9lhd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy76pq2yerexccr5x9lhd.png" alt="Generate multi-page PDF reports" width="799" height="530"&gt;&lt;/a&gt;&lt;/p&gt;
Generate multi-page PDF reports



&lt;h3&gt;
  
  
  Search text and customize PDF content
&lt;/h3&gt;

&lt;p&gt;Volume 3 also introduces improvements to PDF document manipulation, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text search within PDFs&lt;/li&gt;
&lt;li&gt;Image-based list markers&lt;/li&gt;
&lt;li&gt;Enhanced annotation border effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Text search helps applications locate specific information within PDF documents, while image-based list markers and annotation border effects provide additional formatting options.&lt;/p&gt;

&lt;p&gt;These enhancements support applications that need to search, customize, and manage PDF content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert SVG graphics and HTML content into PDFs
&lt;/h2&gt;

&lt;p&gt;PDF generation becomes more challenging when documents contain graphics or content originally designed for the web. A report might include vector logos, diagrams, and charts, while an invoice might be generated from an HTML template.&lt;/p&gt;

&lt;p&gt;Volume 3 includes updates for both scenarios.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.syncfusion.com/document-sdk/net-pdf-library" rel="noopener noreferrer"&gt;.NET PDF Library&lt;/a&gt; now supports &lt;strong&gt;SVG-to-PDF conversion&lt;/strong&gt; , allowing developers to preserve the quality and scalability of vector graphics such as logos, diagrams, illustrations, and charts when generating PDF documents.&lt;/li&gt;
&lt;li&gt;The HTML-to-PDF engine has also been updated with newer Blink binaries, providing improved security and continued compatibility with modern web standards for accurate PDF generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzdmmdaf6q4xc6tm2xb1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzdmmdaf6q4xc6tm2xb1w.png" alt="Convert SVG graphics to PDFs" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;
Convert SVG graphics to PDFs



&lt;p&gt;For organizations generating reports, dashboards, proposals, and customer-facing documents, these enhancements help ensure visual consistency across digital and printed outputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting more value from business documents
&lt;/h2&gt;

&lt;p&gt;Beyond document creation and conversion, organizations increasingly need to extract information automatically.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/document-sdk/net-smart-data-extraction-library" rel="noopener noreferrer"&gt;.NET Data Extraction Library&lt;/a&gt; expands those capabilities with new conversion and recognition features designed for content reuse and automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  PDF-to-Word and PDF-to-HTML conversion
&lt;/h3&gt;

&lt;p&gt;Developers can transform PDF documents into editable formats while preserving layouts, images, tables, and formatting. This simplifies document modernization, content migration, and workflow automation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftwmjt8dqpc9zzqxty765.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftwmjt8dqpc9zzqxty765.png" alt="Convert PDFs to editable Word documents" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;
Convert PDFs to editable Word documents



&lt;p&gt;For example, a legal team can convert archived PDF contracts into editable Word documents instead of recreating the content manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Barcode and QR code recognition
&lt;/h3&gt;

&lt;p&gt;The .NET Data Extraction Library supports barcode and QR code recognition in supported PDF and image documents. The extracted data can be returned as JSON for use in other applications.&lt;/p&gt;

&lt;p&gt;This capability can help automate identifier capture in workflows such as logistics, inventory management, manufacturing, and document intake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: What is the .NET Document Chunking Library used for?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The .NET Document Chunking Library helps developers prepare documents for AI-powered applications such as retrieval-augmented generation (RAG), semantic search, knowledge retrieval, and intelligent chat experiences. It automatically segments large documents into context-aware chunks while preserving document structure and metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: What PDF security features are available in the JavaScript PDF Library?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The JavaScript PDF Library supports RC4 and AES encryption, user and owner passwords, permission controls, digital signatures, and Long-Term Validation (LTV) signatures for building secure PDF workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: Does the JavaScript PDF Library support digital signature validation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. It supports checking document integrity, certificate chains, timestamp information, and certificate revocation status through OCSP and CRL verification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: Does the .NET Data Extraction Library support barcode and QR code extraction?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The library can recognize barcodes and QR codes in supported PDF and image documents and return the extracted data as JSON for downstream processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore Syncfusion Document SDKs 2026 Volume 3
&lt;/h2&gt;

&lt;p&gt;Whether you’re preparing documents for AI retrieval, validating signed PDFs, generating reports, or extracting business data, Syncfusion Document SDKs &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;2026 Volume 3&lt;/a&gt; introduces capabilities worth exploring.&lt;/p&gt;

&lt;p&gt;To learn more, visit the &lt;a href="https://www.syncfusion.com/products/whatsnew" rel="noopener noreferrer"&gt;What’s New&lt;/a&gt; page and &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; for the complete list of updates. You can also explore the relevant documentation and examples to see how these features fit into your applications.&lt;/p&gt;

&lt;p&gt;Existing Syncfusion customers can download the latest version of Essential Studio from the &lt;a href="https://www.syncfusion.com/sales/pricing" rel="noopener noreferrer"&gt;License and Downloads&lt;/a&gt; page. New users can explore the features with a free 30-day &lt;a href="https://www.syncfusion.com/downloads" rel="noopener noreferrer"&gt;trial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have questions or feedback? Connect with us through our &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support forums&lt;/a&gt;, &lt;a href="https://support.syncfusion.com/" rel="noopener noreferrer"&gt;support portal&lt;/a&gt;, or &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback portal&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/sneak-peek-2026-vol-3-document-sdk" rel="noopener noreferrer"&gt;Sneak Peek: 3 New Libraries Coming to Syncfusion Document SDK 2026 Volume 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-essential-studio-2026-vol-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion Essential Studio 2026 Volume 3: A2UI, New JavaScript Document Libraries, and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-dotnet-maui-2026-volume-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion .NET MAUI 2026 Volume 3: New Controls and Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-composer-ai-interface" rel="noopener noreferrer"&gt;Build Agent-Driven UI Designs Faster Using Syncfusion A2UI Composer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>syncfusion</category>
      <category>documentsdks</category>
      <category>releasehighlights</category>
      <category>digitalsignature</category>
    </item>
    <item>
      <title>How to Import Excel and CSV Data into a .NET MAUI DataGrid</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Thu, 15 Oct 2026 12:44:18 +0000</pubDate>
      <link>https://dev.to/syncfusion/how-to-import-excel-and-csv-data-into-a-net-maui-datagrid-4onh</link>
      <guid>https://dev.to/syncfusion/how-to-import-excel-and-csv-data-into-a-net-maui-datagrid-4onh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Learn how to import Excel and CSV files into a .NET MAUI DataGrid using FilePicker, Syncfusion XlsIO, and a lightweight CSV parser. The imported data is converted into a DataTable and displayed in an interactive DataGrid with sorting, filtering, grouping, and editing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Need to display data from Excel and CSV files directly in your .NET MAUI application? Instead of requiring users to open these files in separate applications, you can import them and display the data in a DataGrid, allowing users to work with it directly within your app.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll build an import process using the Syncfusion &lt;a href="https://www.syncfusion.com/maui-controls/maui-datagrid" rel="noopener noreferrer"&gt;.NET MAUI DataGrid&lt;/a&gt;. We’ll use the .NET MAUI FilePicker to select files, Syncfusion &lt;a href="https://www.syncfusion.com/document-sdk/net-excel-library" rel="noopener noreferrer"&gt;XlsIO&lt;/a&gt; to read Excel data, and a CSV parser to process CSV files. Both formats will be converted into a DataTable and displayed in the DataGrid.&lt;/p&gt;

&lt;p&gt;The import pipeline is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick an Excel or CSV file → Process the data → Store it in a DataTable → Show it in the DataGrid&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What we’ll build
&lt;/h2&gt;

&lt;p&gt;The application will allow users to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select an Excel or CSV file from their device.&lt;/li&gt;
&lt;li&gt;Read Excel data using Syncfusion XlsIO.&lt;/li&gt;
&lt;li&gt;Parse CSV data.&lt;/li&gt;
&lt;li&gt;Convert the imported data into a DataTable.&lt;/li&gt;
&lt;li&gt;Display the data in the Syncfusion DataGrid.&lt;/li&gt;
&lt;li&gt;View the number of imported rows and columns.&lt;/li&gt;
&lt;li&gt;Receive feedback when an import fails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the data is displayed, you can configure the DataGrid to support features such as sorting, filtering, grouping, and editing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why import Excel and CSV data into a .NET MAUI DataGrid?
&lt;/h2&gt;

&lt;p&gt;Excel and CSV files are convenient for storing and sharing tabular data. However, users may need more than a way to open these files. They may want to search, filter, sort, or edit the data directly within an application.&lt;/p&gt;

&lt;p&gt;Importing the data into a DataGrid provides a way to work with it inside the app.&lt;/p&gt;

&lt;p&gt;The key is to separate file processing from data display. First, read the file and prepare the data. Then, bind the resulting data to the DataGrid.&lt;/p&gt;

&lt;p&gt;Let’s implement this process step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implement Excel and CSV import in a .NET MAUI DataGrid
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before getting started, make sure your .NET MAUI project has the required workload installed.&lt;/p&gt;

&lt;p&gt;Add the following NuGet packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Syncfusion.Maui.DataGrid" rel="noopener noreferrer"&gt;Syncfusion.Maui.DataGrid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Syncfusion.XlsIO.NET.Core" rel="noopener noreferrer"&gt;Syncfusion.XlsIO.NETCore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Syncfusion’s .NET MAUI examples use XlsIO to read Excel files and export worksheet data to a DataTable, which can then be assigned to the DataGrid’s ItemsSource.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create the data model
&lt;/h3&gt;

&lt;p&gt;The imported data needs to be available to the UI so that the application can track changes to the data property.&lt;/p&gt;

&lt;p&gt;Create a model that implements &lt;strong&gt;INotifyPropertyChanged&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;INotifyPropertyChanged&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Raised when a property value changes to notify bound UI elements.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;PropertyChangedEventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;PropertyChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the DataTable provides a common structure for both Excel and CSV data.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create the ViewModel for file import
&lt;/h3&gt;

&lt;p&gt;Next, we’ll handle file selection and data import.&lt;/p&gt;

&lt;p&gt;The ViewModel will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the file picker.&lt;/li&gt;
&lt;li&gt;Determine whether the selected file is an Excel or CSV file.&lt;/li&gt;
&lt;li&gt;Read the file.&lt;/li&gt;
&lt;li&gt;Convert its contents into a DataTable.&lt;/li&gt;
&lt;li&gt;Return the imported data to the UI.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This keeps the file-import logic separate from the DataGrid display.&lt;/p&gt;

&lt;h4&gt;
  
  
  Import data from Excel
&lt;/h4&gt;

&lt;p&gt;For Excel files, Syncfusion XlsIO reads the workbook and exports the worksheet data to a DataTable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;DataTable&lt;/span&gt; &lt;span class="nf"&gt;ImportFromExcel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt; &lt;span class="n"&gt;excelStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;excelStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;excelEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Syncfusion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;XlsIO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExcelEngine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;xlsApp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excelEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Excel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;xlsApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Syncfusion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;XlsIO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExcelVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Xlsx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xlsApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workbooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dataTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExportDataTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UsedRange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Syncfusion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;XlsIO&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExcelExportDataTableOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ColumnNames&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove completely empty rows.&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;--)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dataTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;ItemArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;DBNull&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                &lt;span class="p"&gt;!(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dataTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dataTable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is &lt;strong&gt;ExportDataTable.&lt;/strong&gt; It converts the worksheet’s used range into a tabular structure that can be used as the DataGrid’s data source. This is also the approach shown in Syncfusion’s documented Excel-to-MAUI-DataGrid example.&lt;/p&gt;

&lt;p&gt;The code selects the first worksheet and treats its first exported row as the column names. It also removes completely empty rows before displaying the data.&lt;/p&gt;

&lt;h4&gt;
  
  
  Import data from CSV
&lt;/h4&gt;

&lt;p&gt;CSV files do not require an Excel workbook. We can read them directly from the selected stream and convert their contents into a DataTable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;DataTable&lt;/span&gt; &lt;span class="nf"&gt;ImportFromCsv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt; &lt;span class="n"&gt;csvStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// CSV import implementation:&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Read the CSV file from the provided stream.&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Extract column headers from the first row.&lt;/span&gt;
    &lt;span class="c1"&gt;// 3. Create DataTable columns based on the headers.&lt;/span&gt;
    &lt;span class="c1"&gt;// 4. Read each subsequent row and populate the DataTable.&lt;/span&gt;
    &lt;span class="c1"&gt;// 5. Return the populated DataTable.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the first row is treated as the column header. The code also handles duplicate column names by generating unique names and fills missing cells with empty strings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;The **ParseCsvLine&lt;/em&gt;* helper used in this implementation must be included in the complete sample repository.*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Load the selected file
&lt;/h3&gt;

&lt;p&gt;Now, we’ll connect the .NET MAUI FilePicker to the Excel and CSV import methods.&lt;/p&gt;

&lt;p&gt;The .NET MAUI FilePicker allows users to select a file from their device. The following method configures the supported file types, opens the selected file as a stream, and calls the appropriate import method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataTable&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ImportFromFilePickerAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Import data from the selected file and return it as a DataTable.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The import logic now has a clear responsibility:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;FilePicker selects the file.&lt;/li&gt;
&lt;li&gt;The file extension determines which import method to call.&lt;/li&gt;
&lt;li&gt;The selected method reads the file and returns a DataTable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The DataGrid can use the same data structure regardless of whether the user selects an Excel or CSV file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;This example supports *&lt;/em&gt;.xlsx** and &lt;strong&gt;.csv&lt;/strong&gt; files. The Excel import reads the first worksheet, and the CSV import treats the first row as the column header.*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Build the .NET MAUI DataGrid UI
&lt;/h3&gt;

&lt;p&gt;With the import logic ready, let’s create a simple UI that lets users select a file and view the imported data.&lt;/p&gt;

&lt;p&gt;The UI needs two primary elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;Import from File&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;A Syncfusion DataGrid to display the imported records.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt; &lt;span class="na"&gt;RowSpacing=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Grid.RowDefinitions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;RowDefinition&lt;/span&gt; &lt;span class="na"&gt;Height=&lt;/span&gt;&lt;span class="s"&gt;"Auto"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;RowDefinition&lt;/span&gt; &lt;span class="na"&gt;Height=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Grid.RowDefinitions&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
            &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt;
            &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt;
            &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Import from File"&lt;/span&gt;
            &lt;span class="na"&gt;Clicked=&lt;/span&gt;&lt;span class="s"&gt;"OnImportClicked"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;syncfusion:SfDataGrid&lt;/span&gt; &lt;span class="na"&gt;Grid.Row=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
                           &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"DataGrid"&lt;/span&gt;
                           &lt;span class="na"&gt;ColumnWidthMode=&lt;/span&gt;&lt;span class="s"&gt;"Fill"&lt;/span&gt;
                           &lt;span class="na"&gt;HeaderRowHeight=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt;
                           &lt;span class="na"&gt;RowHeight=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The button starts the file-selection process, while the DataGrid displays the imported data.&lt;/p&gt;

&lt;h4&gt;
  
  
  Connect the import button to the DataGrid
&lt;/h4&gt;

&lt;p&gt;Add the following event handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnImportClicked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ImportFromFilePickerAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;DataGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;DataGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ItemsSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultView&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;DisplayAlertAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;$"Imported &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rows and &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; columns."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;DisplayAlertAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Import Failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DataGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ItemsSource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultView&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assigns the imported data to the DataGrid through the DataTable’s default view.&lt;/p&gt;

&lt;p&gt;After a successful import, the application displays the number of imported rows and columns. If the user cancels file selection, the method returns without displaying a success message.&lt;/p&gt;

&lt;p&gt;If an exception occurs during selection or processing, the application displays an error alert.&lt;/p&gt;

&lt;h4&gt;
  
  
  See the import flow in action
&lt;/h4&gt;

&lt;p&gt;When you run the sample, the application displays an &lt;strong&gt;Import from File&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;Select an Excel or CSV file. The application reads the file, converts its contents into a DataTable, and assigns the result to the DataGrid. A success message displays the number of imported rows and columns.&lt;/p&gt;

&lt;p&gt;The result is a DataGrid containing the imported data, ready for further interaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8hcudzklhgafoyp9izn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff8hcudzklhgafoyp9izn.gif" alt="Import Excel and CSV data into a .NET MAUI DataGrid" width="600" height="414"&gt;&lt;/a&gt;&lt;/p&gt;
Import Excel and CSV data into a .NET MAUI DataGrid



&lt;h2&gt;
  
  
  Handling the imported data
&lt;/h2&gt;

&lt;p&gt;Once the data is displayed, you can extend the DataGrid configuration to support features based on your application’s requirements, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sorting.&lt;/li&gt;
&lt;li&gt;Filtering.&lt;/li&gt;
&lt;li&gt;Grouping.&lt;/li&gt;
&lt;li&gt;Editing.&lt;/li&gt;
&lt;li&gt;Custom column behavior.&lt;/li&gt;
&lt;li&gt;Data validation and application-specific processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because both file formats are converted into a DataTable, the same data-binding approach can be used regardless of the original file format.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub reference
&lt;/h2&gt;

&lt;p&gt;You can explore the complete sample, including the file-import logic and DataGrid implementation, in the &lt;a href="https://github.com/SyncfusionExamples/Seamlessly-Import-Excel-and-CSV-data-in-.NET-MAUI-DataGrid" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: What file formats are supported?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The sample supports Excel(.xlsx) and CSV(.csv) files. The file picker is configured to display these file types on the supported platforms.&lt;/p&gt;

&lt;p&gt;To support additional formats, update the file-picker configuration and add the corresponding processing logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: Which NuGet packages are required?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The sample uses: &lt;strong&gt;Syncfusion.Maui.DataGrid&lt;/strong&gt; and &lt;strong&gt;Syncfusion.XlsIO.NETCore.&lt;/strong&gt; The .NET MAUI workload is also required.&lt;/p&gt;

&lt;p&gt;For Excel import, XlsIO provides the APIs used to open the workbook and export worksheet data to a DataTable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: How is the imported data bound to the DataGrid?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both Excel and CSV data are converted into a DataTable. The resulting DataView is assigned to the DataGrid’s ItemsSource:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DataGrid.ItemsSource = table.DefaultView;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This provides a common data source for both file formats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: Does the import handle empty rows and duplicate headers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For Excel files, the sample removes completely empty rows after exporting the worksheet data.&lt;/p&gt;

&lt;p&gt;For CSV files, the import logic generates unique column names when duplicate headers are detected and fills missing cells with empty strings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5: Can I use a strongly typed collection instead of a DataTable?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. A DataTable is convenient when the column structure is determined by the imported file.&lt;/p&gt;

&lt;p&gt;If your application has a known schema, you can map the imported rows to a model and bind an &lt;strong&gt;ObservableCollection&lt;/strong&gt; to the DataGrid instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6: How can I handle large files?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The DataGrid supports virtualization for larger datasets. However, the import process itself also affects performance.&lt;/p&gt;

&lt;p&gt;For very large files, avoid unnecessary data copies and consider moving longer-running processing away from the UI thread so the application remains responsive.&lt;/p&gt;

&lt;p&gt;The appropriate approach depends on the file size, data structure, and processing requirements of your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Importing Excel and CSV files into the Syncfusion .NET MAUI DataGrid provides a way to work with file-based data directly inside an application.&lt;/p&gt;

&lt;p&gt;In this example, FilePicker handles file selection, Syncfusion &lt;a href="https://www.syncfusion.com/document-sdk/net-excel-library" rel="noopener noreferrer"&gt;XlsIO&lt;/a&gt; reads Excel data, a CSV parser processes CSV files, and both formats are converted into a DataTable before being displayed in the Syncfusion &lt;a href="https://www.syncfusion.com/maui-controls/maui-datagrid" rel="noopener noreferrer"&gt;.NET MAUI DataGrid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can extend this approach with application-specific validation, data transformation, strongly typed models, and additional DataGrid features.&lt;/p&gt;

&lt;p&gt;Explore the complete &lt;a href="https://github.com/SyncfusionExamples/Seamlessly-Import-Excel-and-CSV-data-in-.NET-MAUI-DataGrid" rel="noopener noreferrer"&gt;GitHub example&lt;/a&gt; to try importing your own Excel and CSV data into a .NET MAUI application.&lt;/p&gt;

&lt;p&gt;For more information, refer to the Syncfusion &lt;a href="https://help.syncfusion.com/maui/datagrid/overview" rel="noopener noreferrer"&gt;.NET MAUI DataGrid documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re a Syncfusion customer, you can access the latest Essential Studio release from the &lt;a href="https://www.syncfusion.com/account/downloads" rel="noopener noreferrer"&gt;license and downloads&lt;/a&gt; page. New users can try Syncfusion with a &lt;a href="https://www.syncfusion.com/downloads/" rel="noopener noreferrer"&gt;30-day free trial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For questions, contact us through our &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support forums&lt;/a&gt;, &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback portal&lt;/a&gt;, or &lt;a href="https://support.syncfusion.com/" rel="noopener noreferrer"&gt;support portal&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-dotnet-maui-2026-volume-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion .NET MAUI 2026 Volume 3: New Controls and Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/maui-interactive-viewer-control" rel="noopener noreferrer"&gt;Introducing the .NET MAUI Interactive Viewer Control for Interactive Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/build-maui-apps-dot-net-11-webinar" rel="noopener noreferrer"&gt;Build Modern .NET MAUI Apps with .NET 11 [Webinar Show Notes]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/fifth-set-open-source-maui-controls" rel="noopener noreferrer"&gt;Introducing the Fifth Set of Open-Source Syncfusion .NET MAUI Controls&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnetmaui</category>
      <category>crossplatform</category>
      <category>datagrid</category>
      <category>desktop</category>
    </item>
    <item>
      <title>Create Reusable, Schema-Driven Forms with Syncfusion Form Builder and Form Renderer</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Wed, 14 Oct 2026 13:46:10 +0000</pubDate>
      <link>https://dev.to/syncfusion/create-reusable-schema-driven-forms-with-syncfusion-form-builder-and-form-renderer-3m3i</link>
      <guid>https://dev.to/syncfusion/create-reusable-schema-driven-forms-with-syncfusion-form-builder-and-form-renderer-3m3i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Build schema-driven forms in Angular with Syncfusion Form Builder and Form Renderer. Design forms visually, export their configuration as a JSON schema, and render interactive forms with configured fields and validation rules. This approach separates the form definition from rendering logic and simplifies form maintenance.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Have you ever needed to update the same form across multiple applications whenever a field, label, or validation rule changes? Maintaining separate form definitions can lead to duplicated code and inconsistent behavior.&lt;/p&gt;

&lt;p&gt;What if you could define a form once and reuse its schema wherever needed? Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/angular-components/angular-form-builder" rel="noopener noreferrer"&gt;Form Builder&lt;/a&gt; and &lt;a href="https://www.syncfusion.com/angular-components/angular-form-renderer" rel="noopener noreferrer"&gt;Form Renderer&lt;/a&gt; provide a schema-first approach to form development. Form Builder lets you design forms visually and export a JSON-based schema, while Form Renderer uses that schema to generate an interactive form with the defined fields, layout, and validation rules.&lt;/p&gt;

&lt;p&gt;In this blog, you’ll explore how these components work together, understand the form schema, and learn how to render a form and handle its submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Syncfusion Form Builder?
&lt;/h2&gt;

&lt;p&gt;Syncfusion &lt;a href="https://www.syncfusion.com/angular-components/angular-form-builder" rel="noopener noreferrer"&gt;Form Builder&lt;/a&gt; is a visual form designer that lets you create forms by dragging and dropping fields onto a design canvas. Instead of manually defining every field in code, you can configure the form through the designer and inspect the generated JSON schema.&lt;/p&gt;

&lt;p&gt;The Form Builder interface has five main areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Toolbox:&lt;/strong&gt; Displays the available form fields you can add to the form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design canvas:&lt;/strong&gt; Provides the workspace for adding and arranging fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties pane:&lt;/strong&gt; Lets you configure the form and selected fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code view:&lt;/strong&gt; Displays the generated form schema as you make changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form preview:&lt;/strong&gt; Shows how the form will appear and behave.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can drag a field from the toolbox onto the design canvas or use the canvas’s add button to insert one through the context menu. After adding a field, configure its properties, such as its label and validation rules, through the properties pane.&lt;/p&gt;

&lt;p&gt;As you make changes, Form Builder updates the schema to reflect the form configuration. You can inspect the schema in the code view, preview the form, and export the definition for use with Form Renderer.&lt;/p&gt;

&lt;p&gt;For setup instructions, refer to the Angular Form Builder getting-started &lt;a href="https://ej2.syncfusion.com/angular/documentation/form-builder/getting-started" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the schema-first approach work?
&lt;/h2&gt;

&lt;p&gt;A form schema describes the structure and configuration of a form, including its fields, layout, validation rules, and settings. Form Builder creates this definition, and Form Renderer uses it to generate the form at runtime.&lt;/p&gt;

&lt;p&gt;The process consists of four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Design:&lt;/strong&gt; Add fields and configure their properties in Form Builder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export:&lt;/strong&gt; Generate the JSON schema containing the form definition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render:&lt;/strong&gt; Pass the schema to Form Renderer through its schema property.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle submission:&lt;/strong&gt; Use the renderer’s submit event to access the submitted data and validation status.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This separation keeps the form definition independent of the application code that displays and processes it. When a form needs to change, you can update the schema and ensure that the relevant applications load the updated version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.syncfusion.com/blogs/wp-content/uploads/2026/10/Form-Builder-interface.gif" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9xc0ck9gsp24wya7lhjb.gif" alt="Form Builder interface" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;
Form Builder interface



&lt;h2&gt;
  
  
  What is Syncfusion Form Renderer?
&lt;/h2&gt;

&lt;p&gt;Syncfusion &lt;a href="https://www.syncfusion.com/angular-components/angular-form-renderer" rel="noopener noreferrer"&gt;Form Renderer&lt;/a&gt; is a schema-driven component that converts a structured form definition into an interactive form at runtime. It uses the supplied schema to determine which fields to display, how to arrange them, and which validation rules to apply.&lt;/p&gt;

&lt;p&gt;Let’s look at an Angular example that defines a login form with an email field, a password field, a Remember Me checkbox, and a submit button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define the Form Schema in Angular
&lt;/h3&gt;

&lt;p&gt;The following example defines the form schema and passes it to the Form Renderer component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ViewEncapsulation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FormRendererModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@syncfusion/ej2-angular-form-renderer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div&amp;gt;
                  &amp;lt;div
                    id="form-renderer-control"
                    ejs-form-renderer
                    [schema]="formSchema"
                    (submit)="onSubmit($event)"&amp;gt;
                  &amp;lt;/div&amp;gt;
               &amp;lt;/div&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;encapsulation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ViewEncapsulation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;FormRendererModule&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;formSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.1.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;properties&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emailAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textbox_1785491685456_167&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emailAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email Address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textboxType&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widget&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textbox_1785491685456_537&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textboxType&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;minLength&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widget&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rememberMe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkbox_1785491685456_262&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rememberMe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Remember Me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widget&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit_button_initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;defaultFormsubmit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buttonType&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;widget&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;style&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;layout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;propertyId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emailAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;propertyId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;propertyId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rememberMe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;field&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;propertyId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Untitled Form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Is Valid:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submitted Data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Processing form submission...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rendered result appears as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.syncfusion.com/blogs/wp-content/uploads/2026/10/Form-Renderer-output.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa36gpuj5fmzjdw3qssug.png" alt="Form Renderer output" width="702" height="373"&gt;&lt;/a&gt;&lt;/p&gt;
Form Renderer output



&lt;p&gt;Form Renderer uses the schema property to build the form and the submit event to invoke the &lt;code&gt;onSubmit()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The schema includes three input fields and a submit button. It marks the email and password fields as required and applies a minimum length of six characters to the password. This rule illustrates schema-based validation and should not be treated as a production password policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand the Form Schema
&lt;/h2&gt;

&lt;p&gt;The schema organizes the form configuration into three main sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;properties:&lt;/strong&gt; Defines the form fields, including their names, labels, data types, widgets, and validation rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;layout:&lt;/strong&gt; Specifies the order in which fields appear. Each entry references a field through its propertyId.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;settings:&lt;/strong&gt; Stores form-level configuration, such as the form name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The widget property determines the type of control used to render a field. This example uses textbox, checkbox, and button widgets.&lt;/p&gt;

&lt;p&gt;Validation rules are defined alongside their corresponding fields. The email and password fields use required, while the password field also uses &lt;strong&gt;&lt;code&gt;minLength&lt;/code&gt;&lt;/strong&gt;. Other supported schema rules include maxLength and pattern, depending on the field configuration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;Client-side validation helps identify input errors, but it does not replace server-side validation or authorization. Always validate submitted data on the server before processing it or allowing access to protected resources.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For setup details and additional schema examples, refer to the Angular Form Renderer getting-started &lt;a href="https://ej2.syncfusion.com/angular/documentation/form-renderer/getting-started" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How form submission works
&lt;/h2&gt;

&lt;p&gt;Once the form is rendered, users can enter their details and select the Submit button. Because the button uses &lt;code&gt;"buttonType": "submit"&lt;/code&gt;, Form Renderer validates the form and raises its submit event.&lt;/p&gt;

&lt;p&gt;The event provides the submitted form data through &lt;code&gt;args.data&lt;/code&gt; and the validation status through &lt;strong&gt;&lt;code&gt;args.isValid&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the Angular example, the &lt;code&gt;onSubmit()&lt;/code&gt; method logs both values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Is Valid:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submitted Data:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Processing form submission...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The submitted data is keyed by the field names defined in the schema. For this example, it can contain values associated with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;emailAddress&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;password&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rememberMe&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The submit button is not included as an input value in the submitted data object.&lt;/p&gt;

&lt;p&gt;Checking &lt;code&gt;args.isValid&lt;/code&gt; lets the application determine whether the form passed validation before proceeding. In a real application, that step might involve sending the validated data to a server for further validation and processing.&lt;/p&gt;

&lt;p&gt;You can also explore the Form Renderer StackBlitz &lt;a href="https://stackblitz.com/edit/angular-twyalqd1" rel="noopener noreferrer"&gt;sample&lt;/a&gt; to see the control in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Form Builder vs. Form Renderer
&lt;/h2&gt;

&lt;p&gt;Form Builder and Form Renderer work together, but each has a distinct responsibility.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Form Builder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Form Renderer&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provides a visual form-design interface&lt;/td&gt;
&lt;td&gt;Generates an interactive form from a schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lets you add, arrange, and configure fields&lt;/td&gt;
&lt;td&gt;Maps field definitions to supported widgets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lets you configure field properties and validation rules&lt;/td&gt;
&lt;td&gt;Applies the configured layout and validation rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provides a preview and schema export&lt;/td&gt;
&lt;td&gt;Handles form interactions and raises form-action events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Produces the form schema&lt;/td&gt;
&lt;td&gt;Provides submitted form data to the application&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This separation means that an application does not need to recreate the form structure manually. It can use a supported schema and pass it to Form Renderer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why shared schemas simplify form development
&lt;/h2&gt;

&lt;p&gt;A schema-first approach is useful when form definitions need to be reused or updated regularly. Here are three practical benefits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avoid maintaining separate form definitions
&lt;/h3&gt;

&lt;p&gt;When multiple applications use the same form, maintaining separate markup and validation logic makes updates repetitive. A shared schema brings field definitions, layout, and validation rules together, reducing duplicated form configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep validation rules close to field definitions
&lt;/h3&gt;

&lt;p&gt;Rules such as required and &lt;code&gt;minLength&lt;/code&gt; can be defined alongside their corresponding fields. When validation requirements change, you can update the schema instead of modifying separate validation logic. Applications must load the updated schema for the changes to take effect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplify form updates across applications
&lt;/h3&gt;

&lt;p&gt;Consider a customer onboarding form used in a self-service portal, an administrator dashboard, and an internal support application. Instead of updating three separate form definitions, you can maintain one shared schema for applications that support it. Each application must load the appropriate schema version to reflect the changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where can these components be useful?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.syncfusion.com/angular-components/angular-form-builder" rel="noopener noreferrer"&gt;Form Builder&lt;/a&gt; and &lt;a href="https://www.syncfusion.com/angular-components/angular-form-renderer" rel="noopener noreferrer"&gt;Form Renderer&lt;/a&gt; are useful for applications that need configurable or frequently updated forms, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer onboarding forms&lt;/li&gt;
&lt;li&gt;Surveys and feedback forms&lt;/li&gt;
&lt;li&gt;Support and service-request forms&lt;/li&gt;
&lt;li&gt;Internal approval forms&lt;/li&gt;
&lt;li&gt;Configurable intake forms&lt;/li&gt;
&lt;li&gt;SaaS applications that allow users to design forms&lt;/li&gt;
&lt;li&gt;Low-code and no-code development platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a service team may need to add a field to a support-request form when its process changes. A shared schema can reduce the work involved in maintaining that form across multiple applications, provided each application loads the updated definition.&lt;/p&gt;

&lt;p&gt;The approach is particularly useful when form definitions change more frequently than the application code responsible for displaying them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with Form Builder and Form Renderer
&lt;/h2&gt;

&lt;p&gt;Ready to try schema-driven forms in your app? Use the following resources to explore the components and their configuration options.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Link&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Angular Form Builder&lt;/td&gt;
&lt;td&gt;&lt;a title="Getting started with Angular Form Builder component" href="https://ej2.syncfusion.com/angular/documentation/form-builder/getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Angular Form Renderer&lt;/td&gt;
&lt;td&gt;&lt;a title="Getting started with Angular Form Renderer component" href="https://ej2.syncfusion.com/angular/documentation/form-renderer/getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form Renderer schema reference&lt;/td&gt;
&lt;td&gt;&lt;a title="Form Schema in Angular Form Renderer component" href="https://ej2.syncfusion.com/angular/documentation/form-renderer/form-schema" rel="noopener noreferrer"&gt;Form Schema&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form Renderer examples&lt;/td&gt;
&lt;td&gt;&lt;a title="Form Renderer live web demo" href="https://ej2.syncfusion.com/angular/demos/#/bootstrap5/form-renderer/default" rel="noopener noreferrer"&gt;Angular demos&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Working with another framework? Check the corresponding Syncfusion documentation to ensure both packages fit your application setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start building schema-driven Forms
&lt;/h2&gt;

&lt;p&gt;Maintaining forms across applications does not have to mean maintaining separate definitions for every instance. Syncfusion &lt;a href="https://www.syncfusion.com/angular-components/angular-form-builder" rel="noopener noreferrer"&gt;Form Builder&lt;/a&gt; and &lt;a href="https://www.syncfusion.com/angular-components/angular-form-renderer" rel="noopener noreferrer"&gt;Form Renderer&lt;/a&gt; separate form design from rendering, allowing you to create a form visually, export its JSON schema, and generate interactive forms with preconfigured fields and validation rules.&lt;/p&gt;

&lt;p&gt;By keeping the form definition in one reusable schema and ensuring that applications load the appropriate version, you can reduce duplicated definitions and simplify future updates.&lt;/p&gt;

&lt;p&gt;Design the form, reuse its schema, and make form maintenance easier.&lt;/p&gt;

&lt;p&gt;Explore the complete Essential Studio Volume 3 &lt;a href="https://www.syncfusion.com/products/whatsnew" rel="noopener noreferrer"&gt;What’s New&lt;/a&gt; and &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;Release Notes&lt;/a&gt; documentation to find the features relevant to your project.&lt;/p&gt;

&lt;p&gt;Existing Syncfusion customers can download the latest version from the &lt;a href="https://www.syncfusion.com/sales/pricing" rel="noopener noreferrer"&gt;license and downloads&lt;/a&gt; page, while new users can get started with a free &lt;a href="https://www.syncfusion.com/downloads" rel="noopener noreferrer"&gt;30-day trial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have questions or feedback? Connect with us through the Syncfusion &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support&lt;/a&gt; and &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback&lt;/a&gt; channels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-essential-studio-2026-vol-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion Essential Studio 2026 Volume 3: A2UI, New JavaScript Document Libraries, and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-composer-ai-interface" rel="noopener noreferrer"&gt;Build Agent-Driven UI Designs Faster Using Syncfusion A2UI Composer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-ai-powered-application" rel="noopener noreferrer"&gt;Introducing Syncfusion A2UI: Build Agent-Driven Interfaces with Reusable UI Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/sneak-peek-2026-vol-3-document-sdk" rel="noopener noreferrer"&gt;Sneak Peek: 3 New Libraries Coming to Syncfusion Document SDK 2026 Volume 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>formbuilder</category>
      <category>formrenderer</category>
      <category>whatsnew</category>
    </item>
    <item>
      <title>React Gantt Chart Performance: Which Library Handles 100,000 Tasks Best?</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Tue, 13 Oct 2026 15:36:11 +0000</pubDate>
      <link>https://dev.to/syncfusion/react-gantt-chart-performance-which-library-handles-100000-tasks-best-2hf9</link>
      <guid>https://dev.to/syncfusion/react-gantt-chart-performance-which-library-handles-100000-tasks-best-2hf9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Which React Gantt chart handles large schedules better? We benchmarked Syncfusion React Gantt, Bryntum Gantt, and DHTMLX Gantt across rendering, task operations, hierarchy changes, and dependency recalculation. Syncfusion performed best in most tested scenarios, completing the 100,000-task stress test in 44.4 seconds, compared with 122.2 seconds for DHTMLX. DHTMLX was faster at deletes, while Bryntum had no reported result for the 100K test. Results may vary with your data and environment.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Choosing a React Gantt chart isn’t just about features; performance matters, too. When task counts reach tens of thousands, does the chart still load quickly, respond to updates, and recalculate dependencies efficiently?&lt;/p&gt;

&lt;p&gt;To find out how they compare, we benchmarked Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/gantt-sdk/react-gantt-chart" rel="noopener noreferrer"&gt;React Gantt Chart&lt;/a&gt;, Bryntum Gantt, and DHTMLX Gantt with workloads of up to 100,000 tasks. All three libraries were tested using the same datasets, interactions, and environment, and the results and test resources were shared for independent verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the benchmark measure?
&lt;/h2&gt;

&lt;p&gt;The benchmark focuses on client-side React performance across five areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial rendering&lt;/li&gt;
&lt;li&gt;CRUD operations&lt;/li&gt;
&lt;li&gt;Hierarchical expand/collapse actions&lt;/li&gt;
&lt;li&gt;Dependency recalculation&lt;/li&gt;
&lt;li&gt;Large-scale stress workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benchmark does not evaluate feature depth, licensing, accessibility, exporting, collaboration features, or memory consumption.&lt;/p&gt;

&lt;p&gt;All tests used production builds, the same machine and browser, and the same logical datasets, with only the data-format adaptations required by each library’s API.&lt;/p&gt;

&lt;p&gt;Because Syncfusion conducted this benchmark, the results should be considered a reproducible data point rather than a definitive industry ranking. Independent validation is encouraged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test environment
&lt;/h2&gt;

&lt;p&gt;To compare the three libraries under consistent conditions, we used the following hardware and software configuration.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Item&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operating System&lt;/td&gt;
&lt;td&gt;Windows 11 Enterprise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Processor&lt;/td&gt;
&lt;td&gt;Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;16 GB RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser&lt;/td&gt;
&lt;td&gt;Microsoft Edge Version 154.0.4258.62 (Official build) (64-bit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;v24.19.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;19.2.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Tool&lt;/td&gt;
&lt;td&gt;Create React App (CRA)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  React Gantt library versions
&lt;/h3&gt;

&lt;p&gt;The benchmark used the following versions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="234"&gt;&lt;strong&gt;Library&lt;/strong&gt;&lt;/td&gt;
&lt;td width="84"&gt;&lt;strong&gt;Version&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="234"&gt;Syncfusion React Gantt Chart&lt;/td&gt;
&lt;td width="84"&gt;35.1.37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="234"&gt;Bryntum Gantt&lt;/td&gt;
&lt;td width="84"&gt;7.3.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="234"&gt;DHTMLX Gantt&lt;/td&gt;
&lt;td width="84"&gt;10.0.2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Performance can vary between releases, so these versions define the scope of this comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build and runtime conditions
&lt;/h2&gt;

&lt;p&gt;Benchmark results can be influenced by factors beyond the Gantt component itself. To reduce avoidable variance and provide a fair comparison, all runs were executed under the same baseline conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production builds only, without development-mode measurements.&lt;/li&gt;
&lt;li&gt;Local execution on the same machine.&lt;/li&gt;
&lt;li&gt;Browser extensions disabled.&lt;/li&gt;
&lt;li&gt;No intentionally parallel workloads during benchmark capture.&lt;/li&gt;
&lt;li&gt;Identical logical datasets, with only the minimum data-format adaptations required by each library.&lt;/li&gt;
&lt;li&gt;Consistent benchmark workflows, measurement logic, and runtime configuration across test scenarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benchmark repository is available on GitHub: &lt;a href="https://github.com/SyncfusionExamples/gantt-chart-performance-benchmark" rel="noopener noreferrer"&gt;Gantt Chart Performance Benchmark Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository is intended to provide the dataset generators, configuration files, benchmark scripts, and raw measurements in CSV and JSON formats. These resources allow readers to inspect the results, verify calculations, and reproduce the tests in their own environments.&lt;/p&gt;

&lt;p&gt;Readers are encouraged to run the workloads using their own hardware, browser versions, and project configurations to assess how the results relate to their applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dataset design
&lt;/h2&gt;

&lt;p&gt;The benchmark uses synthetic datasets rather than real project files. This approach allows the same workload characteristics to be applied consistently across the three libraries, although the datasets cannot represent every project structure or scheduling pattern.&lt;/p&gt;

&lt;p&gt;The scenarios cover 5,000 to 100,000 tasks and include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repeated parent-child structures for rendering tests.&lt;/li&gt;
&lt;li&gt;Deep hierarchies with 10 levels of nesting for navigation tests.&lt;/li&gt;
&lt;li&gt;Dependency-heavy schedules for recalculation tests.&lt;/li&gt;
&lt;li&gt;Combined hierarchy and scale workloads for stress testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dataset generators are included in the benchmark repository so that readers can recreate the same scenarios and validate the results in different environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we observed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initial rendering
&lt;/h3&gt;

&lt;p&gt;The largest gap appeared during initial load.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Syncfusion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bryntum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DHTMLX&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5K&lt;/td&gt;
&lt;td&gt;1.5 s&lt;/td&gt;
&lt;td&gt;8.3 s&lt;/td&gt;
&lt;td&gt;1.5 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10K&lt;/td&gt;
&lt;td&gt;2.1 s&lt;/td&gt;
&lt;td&gt;18.4 s&lt;/td&gt;
&lt;td&gt;3.3 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25K&lt;/td&gt;
&lt;td&gt;5.1 s&lt;/td&gt;
&lt;td&gt;52.5 s&lt;/td&gt;
&lt;td&gt;11.1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50K&lt;/td&gt;
&lt;td&gt;10.2 s&lt;/td&gt;
&lt;td&gt;91.2 s&lt;/td&gt;
&lt;td&gt;54.2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At smaller sizes, Syncfusion and DHTMLX produced similar results, while Bryntum required noticeably more time to complete rendering. As dataset size increased, the gap widened significantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjv1be42r33xfngf4qlav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjv1be42r33xfngf4qlav.png" alt="Initial render time (seconds) across dataset sizes" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;
Initial render time (seconds) across dataset sizes



&lt;p&gt;The most interesting observation is not the absolute numbers but the scaling behavior. Between 5K and 50K tasks, Syncfusion’s growth curve remained comparatively gradual, while DHTMLX and Bryntum increased more sharply.&lt;/p&gt;

&lt;p&gt;For teams routinely opening 25K-50K task portfolios, this difference could affect perceived application responsiveness.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;The page load timer starts when the dataset is assigned to the Gantt Chart. The timer stops in the dataBound event after the Gantt Chart has completed rendering, and the elapsed time is displayed as the initial rendering time.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Editing performance
&lt;/h3&gt;

&lt;p&gt;For interactive planning applications, editing performance can be just as important as initial rendering. &lt;/p&gt;

&lt;p&gt;Record updates were generally the fastest operation across all libraries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Dataset&lt;/th&gt;
&lt;th&gt;Syncfusion&lt;/th&gt;
&lt;th&gt;Bryntum&lt;/th&gt;
&lt;th&gt;DHTMLX&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5K&lt;/td&gt;
&lt;td&gt;9.8 ms&lt;/td&gt;
&lt;td&gt;30.20 ms&lt;/td&gt;
&lt;td&gt;16.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10K&lt;/td&gt;
&lt;td&gt;11.7 ms&lt;/td&gt;
&lt;td&gt;40.8 ms&lt;/td&gt;
&lt;td&gt;22.1 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25K&lt;/td&gt;
&lt;td&gt;17.2 ms&lt;/td&gt;
&lt;td&gt;44.8 ms&lt;/td&gt;
&lt;td&gt;76.0 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50K&lt;/td&gt;
&lt;td&gt;21.2 ms&lt;/td&gt;
&lt;td&gt;64.5 ms&lt;/td&gt;
&lt;td&gt;147.8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Syncfusion and DHTMLX consistently completed updates in milliseconds, while Bryntum showed higher variability across the reported scenarios. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4hyo0g8cgk0nfb0tia7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4hyo0g8cgk0nfb0tia7r.png" alt="Update operation latency (milliseconds)" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;
Update operation latency (milliseconds)



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;A record update represents the benchmark’s defined update operation applied to a single task. See the benchmark repository for the exact implementation and measurement logic.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The practical distinction here is substantial. A sub-25 ms update feels instantaneous. Multi-second delays are immediately visible to users and can interrupt planning workflows.&lt;/p&gt;

&lt;p&gt;Delete operations proved significantly more expensive for every library, especially at larger scales. Even the fastest measurements exceeded one second at 50K records, highlighting that large hierarchical removals remain a costly operation regardless of implementation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="101"&gt;&lt;strong&gt;Dataset&lt;/strong&gt;&lt;/td&gt;
&lt;td width="107"&gt;&lt;strong&gt;Syncfusion&lt;/strong&gt;&lt;/td&gt;
&lt;td width="115"&gt;&lt;strong&gt;Bryntum&lt;/strong&gt;&lt;/td&gt;
&lt;td width="102"&gt;&lt;strong&gt;DHTMLX&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="101"&gt;&lt;strong&gt;5K Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td width="107"&gt;133.3 ms&lt;/td&gt;
&lt;td width="115"&gt;311.9 ms&lt;/td&gt;
&lt;td width="102"&gt;96.8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="101"&gt;&lt;strong&gt;10K Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td width="107"&gt;972.1 ms&lt;/td&gt;
&lt;td width="115"&gt;660.3 ms&lt;/td&gt;
&lt;td width="102"&gt;219.6 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="101"&gt;&lt;strong&gt;25K Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td width="107"&gt;1,188.5 ms&lt;/td&gt;
&lt;td width="115"&gt;3,216.1 ms&lt;/td&gt;
&lt;td width="102"&gt;494.2 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="101"&gt;&lt;strong&gt;50K Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td width="107"&gt;4,550.6 ms&lt;/td&gt;
&lt;td width="115"&gt;6,108.7 ms&lt;/td&gt;
&lt;td width="102"&gt;1,245.7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpla308gpgz14cr44ufjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpla308gpgz14cr44ufjd.png" alt="Delete operation latency (milliseconds)" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;
Delete operation latency (milliseconds)



&lt;h3&gt;
  
  
  Deep hierarchies
&lt;/h3&gt;

&lt;p&gt;Large project schedules can contain nested work breakdown structures rather than flat task lists. To measure hierarchy operations, the benchmark tested expanding and collapsing all tasks across 10 levels of nesting.&lt;/p&gt;

&lt;p&gt;At 50K tasks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Operation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Syncfusion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bryntum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DHTMLX&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expand all&lt;/td&gt;
&lt;td&gt;69.6 ms&lt;/td&gt;
&lt;td&gt;6485.7 ms&lt;/td&gt;
&lt;td&gt;500.7 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collapse all&lt;/td&gt;
&lt;td&gt;43.9 ms&lt;/td&gt;
&lt;td&gt;3592.6 ms&lt;/td&gt;
&lt;td&gt;196.1 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The benchmark showed a substantial difference between the three implementations. Syncfusion and DHTMLX remained relatively responsive, while Bryntum required multiple seconds to complete bulk hierarchy actions in this benchmark configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency recalculation
&lt;/h3&gt;

&lt;p&gt;Dependency processing was the most computationally expensive workload.&lt;/p&gt;

&lt;p&gt;Even the best-performing measurements became substantial at larger sizes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Syncfusion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bryntum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DHTMLX&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25K&lt;/td&gt;
&lt;td&gt;15.9 s&lt;/td&gt;
&lt;td&gt;62.2 s&lt;/td&gt;
&lt;td&gt;20.6 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50K&lt;/td&gt;
&lt;td&gt;47.5 s&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;td&gt;84.7 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;NA (Not Available) indicates that the scenario could not be completed successfully due to excessive execution time or application instability.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl6uq4uqrh0i9kzghcbci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl6uq4uqrh0i9kzghcbci.png" alt="Dependency &amp;amp; critical path recalculation (seconds)" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;
Dependency &amp;amp; critical path recalculation (seconds)



&lt;p&gt;These results reinforce an important lesson: large dependency networks are expensive regardless of vendor.&lt;/p&gt;

&lt;p&gt;Teams building dependency-heavy planning systems should benchmark their own scheduling topology before committing to a component. The synthetic chains used in this benchmark help provide consistency, but they cannot represent every real-world project structure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;These tests used the dependency structure generated by the benchmark dataset. Because dependency topology can vary substantially between projects, these results should be treated as workload-specific rather than universal performance limits.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How the Gantt libraries handle 100,000 tasks
&lt;/h2&gt;

&lt;p&gt;The final scenario combined volume and hierarchy depth.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workload&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Syncfusion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bryntum&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DHTMLX&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25K / 5 levels&lt;/td&gt;
&lt;td&gt;6.7 s&lt;/td&gt;
&lt;td&gt;40.6 s&lt;/td&gt;
&lt;td&gt;9.1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50K / 10 levels&lt;/td&gt;
&lt;td&gt;14.4 s&lt;/td&gt;
&lt;td&gt;97.6 s&lt;/td&gt;
&lt;td&gt;32.3 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;75K / 10 levels&lt;/td&gt;
&lt;td&gt;26.8 s&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;td&gt;74.1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100K / 10 levels&lt;/td&gt;
&lt;td&gt;44.4 s&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;td&gt;122.2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this benchmark environment, Syncfusion completed the 100,000-task workload in 44.4 seconds, while DHTMLX required 122.2 seconds. Bryntum did not complete the largest reported scenarios&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftp7lsgzgak0urd08wpko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftp7lsgzgak0urd08wpko.png" alt="Combined scale + hierarchy stress test (seconds)" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;
Combined scale + hierarchy stress test (seconds)



&lt;h2&gt;
  
  
  What developers should take away?
&lt;/h2&gt;

&lt;p&gt;The key takeaway is that Gantt performance depends on the workload:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large schedules: Focus on initial rendering time.&lt;/li&gt;
&lt;li&gt;Frequent task edits: Compare update and delete latency.&lt;/li&gt;
&lt;li&gt;Dependency-heavy projects: Pay closer attention to recalculation performance than rendering alone.&lt;/li&gt;
&lt;li&gt;Projects with more than 50,000 tasks: Test with your own data rather than relying on benchmark results alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benchmark summary (all times in seconds)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Syncfusion&lt;/th&gt;
&lt;th&gt;Bryntum&lt;/th&gt;
&lt;th&gt;DHTMLX&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initial rendering (50K tasks)&lt;/td&gt;
&lt;td&gt;10.2&lt;/td&gt;
&lt;td&gt;91.2&lt;/td&gt;
&lt;td&gt;54.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Record update (50K tasks)&lt;/td&gt;
&lt;td&gt;0.021&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;td&gt;0.148&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete (50K tasks)&lt;/td&gt;
&lt;td&gt;4.551&lt;/td&gt;
&lt;td&gt;6.109&lt;/td&gt;
&lt;td&gt;1.246&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expand all (50K tasks)&lt;/td&gt;
&lt;td&gt;0.070&lt;/td&gt;
&lt;td&gt;6.486&lt;/td&gt;
&lt;td&gt;0.501&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collapse all (50K tasks)&lt;/td&gt;
&lt;td&gt;0.044&lt;/td&gt;
&lt;td&gt;3.593&lt;/td&gt;
&lt;td&gt;0.196&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependency recalculation (50K tasks)&lt;/td&gt;
&lt;td&gt;47.5&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;td&gt;84.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stress test (100K tasks, 10 levels)&lt;/td&gt;
&lt;td&gt;44.4&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;td&gt;122.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scrolling (FPS)&lt;/td&gt;
&lt;td&gt;Not compared&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; &lt;em&gt;Syncfusion recorded the lowest times in five of the six scenarios compared, with DHTMLX fastest for deletes. Bryntum did not render the 50K dependency recalculation or the 75K and 100K stress workloads. Results reflect synthetic data in one test environment, so validate them against your own workload.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A Gantt chart that performs well with a small dataset can behave very differently when it manages thousands of tasks and dependencies. Testing against your own workload matters as much as reading benchmark results.&lt;/p&gt;

&lt;p&gt;Syncfusion ran this benchmark to show how Syncfusion &lt;a href="https://www.syncfusion.com/gantt-sdk/react-gantt-chart" rel="noopener noreferrer"&gt;React Gantt Chart&lt;/a&gt;, Bryntum Gantt, and DHTMLX Gantt handle demanding scheduling operations. The results reflect one test environment and dataset structure, so use them as a starting point and rerun the tests on your own hardware and data.&lt;/p&gt;

&lt;p&gt;Ready to evaluate Syncfusion React Gantt Chart? Explore the live &lt;a href="https://www.syncfusion.com/gantt-sdk/react-gantt-chart" rel="noopener noreferrer"&gt;demos&lt;/a&gt;, read the &lt;a href="https://help.syncfusion.com/gantt-sdk/react/gantt-chart/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, or start a free trial to test it with your own schedules.&lt;/p&gt;

&lt;p&gt;For existing Syncfusion customers, the latest version of Essential Studio is available from the &lt;a href="https://www.syncfusion.com/sales/pricing?category=ui-components" rel="noopener noreferrer"&gt;License and Downloads&lt;/a&gt; page. New users can explore its capabilities through the 30-day free &lt;a href="https://www.syncfusion.com/downloads/spreadsheet-editor-sdk" rel="noopener noreferrer"&gt;trial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have questions or feedback, reach out through our &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support forum&lt;/a&gt;, &lt;a href="https://support.syncfusion.com/" rel="noopener noreferrer"&gt;support portal&lt;/a&gt;, or &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback portal&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/overview-syncfusion-react-gantt-chart" rel="noopener noreferrer"&gt;Overview of Syncfusion React Gantt Chart Component&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/react-gantt-datamanager-adaptor" rel="noopener noreferrer"&gt;How to Choose the Right DataManager Adaptor for React Gantt Chart Backend Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/integrate-react-gantt-chart-in-framer" rel="noopener noreferrer"&gt;How to Integrate React Gantt Chart in Framer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/flight-tracker-application-with-react-gantt-chart" rel="noopener noreferrer"&gt;How to Develop a Flight Tracker Application with React Gantt Chart&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ganttchart</category>
      <category>react</category>
      <category>reactganttchart</category>
      <category>bryntumgantt</category>
    </item>
    <item>
      <title>Why Modern AI Apps Feel Faster: Streaming UX Patterns That Actually Work</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Mon, 12 Oct 2026 12:31:18 +0000</pubDate>
      <link>https://dev.to/syncfusion/why-modern-ai-apps-feel-faster-streaming-ux-patterns-that-actually-work-4dd3</link>
      <guid>https://dev.to/syncfusion/why-modern-ai-apps-feel-faster-streaming-ux-patterns-that-actually-work-4dd3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Learn how to design responsive AI streaming interfaces by measuring TTFT and time to useful content, choosing the right streaming transport, handling partial responses, building safe Generative UI experiences, and monitoring performance in production.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An AI application can generate a response quickly and still feel slow. Users may wait without feedback, watch the layout shift as text arrives, or struggle to recover when a connection interrupts an otherwise useful answer.&lt;/p&gt;

&lt;p&gt;Unlike a conventional request that returns a completed result, an AI interaction may deliver partial text, retrieve information, call tools, and continue generating. The frontend must manage these stages without losing the user’s context.&lt;/p&gt;

&lt;p&gt;Streaming helps by making output available before the entire response is complete. However, a responsive experience depends on more than displaying text as it arrives.&lt;/p&gt;

&lt;p&gt;This guide explores how to measure streaming latency, choose a suitable transport, handle incomplete responses, build safe Generative UI experiences, and troubleshoot performance issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The code examples are illustrative rather than complete production implementations. In a real application, route requests through a backend you control. Keep provider credentials, authorization, and privileged tool execution on the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why streaming matters in AI applications
&lt;/h2&gt;

&lt;p&gt;A conventional application often follows a predictable sequence:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Request → Processing → Result&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;An AI interaction can take a less predictable path:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Request → Partial response → Tool call → More response → Completion&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It may also stop unexpectedly after producing useful output.&lt;/p&gt;

&lt;p&gt;The frontend therefore needs to communicate more than the final result. It should acknowledge the request, show meaningful progress, preserve useful output, and let users stop or retry when needed.&lt;/p&gt;

&lt;p&gt;For example, an assistant generating a long explanation can display the first useful paragraph while the remaining content is still being generated. Users can begin reading instead of waiting for the entire response.&lt;/p&gt;

&lt;p&gt;That improvement matters most when early output is meaningful, and the application can safely handle incomplete responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure the latency users actually experience
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Time to First Token (TTFT)&lt;/code&gt; measures the delay before the first response token becomes available at a defined measurement point. For an AI application, that point might be the provider, backend, or browser. The measurement boundary matters when comparing results.&lt;/p&gt;

&lt;p&gt;TTFT is not the same as the time when users first see content or receive information they can act on. These are separate milestones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time to first token:&lt;/strong&gt; When the first token becomes available at the chosen measurement point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to first visible content:&lt;/strong&gt; When response content is actually rendered in the interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to useful content:&lt;/strong&gt; When the response first provides information users can act on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to completion:&lt;/strong&gt; When the response finishes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fast first token does not guarantee that the answer will feel responsive. The response might contain a long preamble, or the frontend might delay rendering the content.&lt;/p&gt;

&lt;p&gt;Several factors can increase the delay before useful content appears:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model prefill and prompt size&lt;/li&gt;
&lt;li&gt;Provider-side queueing&lt;/li&gt;
&lt;li&gt;Network latency&lt;/li&gt;
&lt;li&gt;Backend orchestration&lt;/li&gt;
&lt;li&gt;Retrieval and tool initialization&lt;/li&gt;
&lt;li&gt;Buffering between the server and browser&lt;/li&gt;
&lt;li&gt;Frontend decoding, parsing, and rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Track TTFT alongside time to useful content and total completion time. For production monitoring, percentile measurements such as &lt;code&gt;P50&lt;/code&gt;, &lt;code&gt;P95&lt;/code&gt;, and &lt;code&gt;P99&lt;/code&gt; can reveal slow requests that averages hide.&lt;/p&gt;

&lt;p&gt;Define the measurement boundaries consistently. Receiving a token at the browser is not the same as rendering it on screen, so instrument those events separately when possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actual latency versus perceived latency
&lt;/h2&gt;

&lt;p&gt;Actual latency describes how long an operation takes. Perceived latency reflects how responsive the application feels while the operation is in progress.&lt;/p&gt;

&lt;p&gt;The interface can improve perceived responsiveness by acknowledging requests quickly, showing meaningful progress, and displaying useful output as it becomes available.&lt;/p&gt;

&lt;p&gt;If TTFT is low but the interface still feels slow, investigate rendering work, layout instability, buffering, and progress feedback before attributing the problem to the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should you use streaming?
&lt;/h2&gt;

&lt;p&gt;Streaming is not necessary for every AI feature. A regular request-response pattern may be simpler for short, predictable outputs such as classification labels, autocomplete suggestions, or brief validation messages.&lt;/p&gt;

&lt;p&gt;Streaming becomes more useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Responses are long enough that users benefit from reading early.&lt;/li&gt;
&lt;li&gt;Retrieval or tool execution introduces noticeable delays.&lt;/li&gt;
&lt;li&gt;The response evolves through multiple stages.&lt;/li&gt;
&lt;li&gt;Partial output remains useful if the connection fails.&lt;/li&gt;
&lt;li&gt;The interface needs to display structured events during generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The decision should depend on what users gain from receiving the result progressively. Streaming also introduces extra work around cancellation, state management, error handling, and rendering, so use it where those trade-offs make sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the right streaming transport
&lt;/h2&gt;

&lt;p&gt;Once streaming is useful for the interaction, choose a transport that matches the communication pattern.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Best suited for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Main trade-off&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Server-Sent Events (SSE)&lt;/code&gt; consumed through &lt;code&gt;EventSource&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;One-way server-to-browser events over HTTP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;EventSource&lt;/code&gt; offers limited control over request methods, headers, and payloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fetch with &lt;code&gt;ReadableStream&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;POST requests, custom headers, and application-specific streaming formats&lt;/td&gt;
&lt;td&gt;The application must handle stream reading, decoding, and parsing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WebSockets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Continuous, bidirectional communication&lt;/td&gt;
&lt;td&gt;Requires additional connection and lifecycle management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;SSE&lt;/code&gt;&lt;/strong&gt; and &lt;code&gt;EventSource&lt;/code&gt; are related, not separate transport choices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SSE&lt;/code&gt;&lt;/strong&gt; is the event-stream format and communication mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;EventSource&lt;/code&gt;&lt;/strong&gt; is the browser API commonly used to consume it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server-sent events with EventSource
&lt;/h3&gt;

&lt;p&gt;Server-Sent Events provide a way for a server to send a sequence of events to a browser over HTTP. They can be a good fit for text generation and other interactions where the server primarily sends updates to the client.&lt;/p&gt;

&lt;p&gt;The browser’s &lt;code&gt;EventSource&lt;/code&gt; API handles SSE event parsing and provides reconnection behavior. However, it does not offer the same request customization as fetch, which can be a limitation when an endpoint requires a POST request, custom headers, or a request body.&lt;/p&gt;

&lt;p&gt;Automatic reconnection also does not guarantee that an interrupted AI generation will resume from the exact point where it stopped. Resuming requires suitable server-side support, such as an event identifier or application-level continuation mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetch with ReadableStream
&lt;/h3&gt;

&lt;p&gt;Using fetch with a &lt;code&gt;ReadableStream&lt;/code&gt; gives the application more control over the request and response. This can be useful when the endpoint expects a POST request, authentication headers, a JSON body, or a custom event format.&lt;/p&gt;

&lt;p&gt;The trade-off is that the client must correctly decode and parse the response. It also needs to handle cancellation, connection failures, and incomplete messages.&lt;/p&gt;

&lt;p&gt;A network read does not necessarily correspond to one complete application event. A read may contain part of an event, one event, or several events. The parser must buffer data until complete messages can be identified.&lt;/p&gt;

&lt;p&gt;Use a well-tested streaming parser when possible rather than spreading protocol-specific parsing logic throughout the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  WebSockets
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;WebSockets&lt;/code&gt; support communication in both directions over a persistent connection. They can be appropriate for voice interactions, collaborative sessions, and applications that continuously exchange events.&lt;/p&gt;

&lt;p&gt;For many text-generation experiences, HTTP streaming is sufficient. WebSockets become more useful when the interaction itself requires ongoing communication in both directions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle the frontend problems that make streaming feel unreliable
&lt;/h2&gt;

&lt;p&gt;Receiving data progressively does not guarantee a good user experience. The frontend must keep the interface readable, stable, and recoverable while the response changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Show progress before the first response arrives
&lt;/h3&gt;

&lt;p&gt;Even a streaming request can have a noticeable delay before its first useful output. If the screen remains blank, users may assume that the request was lost.&lt;/p&gt;

&lt;p&gt;Show a clear processing state as soon as the request is accepted. Depending on the application, this might be a simple status message or a more specific indicator such as &lt;code&gt;Searching&lt;/code&gt; or &lt;code&gt;Retrieving information&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Only show intermediate states that reflect meaningful application events. Avoid suggesting that an operation is happening if the application cannot verify that it is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep the layout stable as the response grows
&lt;/h3&gt;

&lt;p&gt;A long response can move buttons, shift surrounding content, and pull the viewport away from the user’s current reading position.&lt;/p&gt;

&lt;p&gt;Reserve space where practical and avoid unnecessary layout changes. Auto-scroll only when the user is already near the bottom of the response. If they scroll upward to read earlier content, preserve their position and offer a &lt;strong&gt;Jump to latest&lt;/strong&gt; action.&lt;/p&gt;

&lt;p&gt;Batching UI updates can also reduce rendering work. The right update frequency depends on the framework, response size, and rendering cost. Updating once per animation frame is one option, not a universal requirement.&lt;/p&gt;

&lt;p&gt;The aim is to keep new content readable without making the rest of the page feel unstable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Render incomplete Markdown carefully
&lt;/h3&gt;

&lt;p&gt;Streaming Markdown presents a separate challenge because the content may not yet form a complete document.&lt;/p&gt;

&lt;p&gt;An incoming response might contain an unfinished code block, an incomplete link, unmatched emphasis markers, or a partially generated list. Reprocessing the entire document after every token can cause flickering and unnecessary rendering work.&lt;/p&gt;

&lt;p&gt;A streaming-aware renderer can buffer incoming text, update on a controlled schedule, and preserve completed blocks where possible. Incomplete content should be treated as provisional until enough input is available to render it reliably.&lt;/p&gt;

&lt;p&gt;Treat model-generated &lt;code&gt;HTML&lt;/code&gt; as untrusted input. Render plain text safely by default, and use appropriate sanitization if the application supports rich &lt;strong&gt;&lt;code&gt;HTML&lt;/code&gt;&lt;/strong&gt;. Never insert raw model output directly into &lt;code&gt;innerHTML&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let users stop or retry a response
&lt;/h3&gt;

&lt;p&gt;Long-running interactions should not leave users stuck waiting for an answer they no longer need.&lt;/p&gt;

&lt;p&gt;Provide controls appropriate to the application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stop&lt;/strong&gt; to cancel the current request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry&lt;/strong&gt; to attempt the request again after a failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regenerate&lt;/strong&gt; when the user wants another response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A browser request can be aborted with &lt;code&gt;AbortController&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;JavaScript&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responsePromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Explain AI streaming UX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// When the user selects Stop:&lt;/span&gt;
&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This illustrates how to attach a cancellation signal to a request; it does not implement a complete streaming lifecycle. Production code must consume the response body, handle abort and network errors, update the interface, and release any resources as appropriate.&lt;/p&gt;

&lt;p&gt;Aborting the browser request does not, by itself, guarantee that backend processing or upstream model generation has stopped. If the application needs to stop that work, the backend must support cancellation and propagate it to the relevant operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preserve partial responses after failure
&lt;/h3&gt;

&lt;p&gt;Suppose an assistant has already generated several useful paragraphs when the connection fails. Removing those paragraphs can make the interruption feel like data loss.&lt;/p&gt;

&lt;p&gt;Keep the visible output and communicate its status clearly. Distinguish between a completed response, a canceled response, and an interrupted response so users know what happened.&lt;/p&gt;

&lt;p&gt;Retry behavior should also be designed deliberately. A retry may restart the entire generation rather than continue from the interruption. Resuming requires support in the backend and the relevant protocol or application logic.&lt;/p&gt;

&lt;p&gt;Make it clear whether the next attempt will restart or resume when that difference affects the user’s work. Avoid presenting a restarted answer as a continuation of the previous response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check for buffering between the server and browser
&lt;/h3&gt;

&lt;p&gt;The backend may produce output progressively while an intermediary buffers it before sending it to the browser. In that situation, users receive several tokens at once instead of seeing them arrive progressively.&lt;/p&gt;

&lt;p&gt;If the browser’s received-data timing does not match the server’s output timing, inspect proxy, CDN, compression, and response-buffering configuration. Confirm that the deployment path supports the intended streaming behavior rather than assuming that a streaming endpoint guarantees progressive delivery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build safe generative UI experiences
&lt;/h3&gt;

&lt;p&gt;AI interfaces do not have to display only text. With Generative UI, a model can return structured information that the application maps to approved interface components.&lt;/p&gt;

&lt;p&gt;For example, rather than returning a weather description as plain text, a system might provide structured data that the frontend renders as a weather card. The same approach can support charts, forms, status panels, data views, and approval cards.&lt;/p&gt;

&lt;p&gt;The important boundary is that the model proposes content or a UI state; the application decides what it will render and which actions are permitted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate structured output before rendering
&lt;/h3&gt;

&lt;p&gt;Define the components your application supports and the data each component expects. An illustrative registry might look like this:&lt;/p&gt;

&lt;p&gt;JavaScript&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;displayWeather&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;weatherSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;showChart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chartSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;showApproval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;approvalSchema&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema definitions are application-specific placeholders, not built-in APIs.&lt;/p&gt;

&lt;p&gt;When a structured response arrives:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the requested component or action.&lt;/li&gt;
&lt;li&gt;Check that it exists in the approved registry.&lt;/li&gt;
&lt;li&gt;Parse the payload.&lt;/li&gt;
&lt;li&gt;Validate the payload against the expected schema.&lt;/li&gt;
&lt;li&gt;Render the corresponding component only after validation succeeds.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reject unknown component types and invalid payloads. Do not let the model introduce arbitrary UI components simply by naming them in its response.&lt;/p&gt;

&lt;p&gt;For streamed structured data, also account for incomplete payloads. Buffer data until the relevant message or structured unit is complete, or use a protocol designed to represent incremental updates. Do not treat a partial JSON object as a validated component configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep privileged actions on the server
&lt;/h3&gt;

&lt;p&gt;A structured response might propose an action such as sending an email or modifying a record. The browser should not execute that action merely because the model requested it.&lt;/p&gt;

&lt;p&gt;Instead, display the proposed action and ask for user confirmation when the action’s impact or risk warrants it. Send the request to the backend, where the server validates the arguments, checks authorization, and applies the appropriate safeguards before execution.&lt;/p&gt;

&lt;p&gt;For operations that affect external systems or modify important data, consider idempotency and audit logging as well. These controls help prevent unintended side effects and make the operation traceable.&lt;/p&gt;

&lt;p&gt;User confirmation is an additional safeguard, not a replacement for server-side authorization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make streaming interfaces accessible and trustworthy
&lt;/h2&gt;

&lt;p&gt;A responsive interface should communicate what is happening without overwhelming users or making unsupported claims.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provide evidence for factual responses
&lt;/h3&gt;

&lt;p&gt;When the application uses retrieval or external sources, place citations close to the claims they support. Users should be able to determine which source supports a statement without having to match every claim against an unrelated list at the end.&lt;/p&gt;

&lt;p&gt;If the system proposes an action rather than simply presenting information, make the proposed change and its consequences clear before the user confirms it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Announce updates meaningfully
&lt;/h3&gt;

&lt;p&gt;Screen readers can struggle with streamed responses if every token is added to a live region. Announcing each small change can interrupt reading and make the experience difficult to follow.&lt;/p&gt;

&lt;p&gt;Separate the visual rendering loop from the screen-reader announcement strategy.&lt;/p&gt;

&lt;p&gt;Consider these practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create live regions before streamed content arrives.&lt;/li&gt;
&lt;li&gt;Use polite announcements for ordinary progress and response updates.&lt;/li&gt;
&lt;li&gt;Reserve assertive announcements for urgent errors where appropriate.&lt;/li&gt;
&lt;li&gt;Announce meaningful chunks rather than every token.&lt;/li&gt;
&lt;li&gt;Keep completion, cancellation, and error messages in a separate status region.&lt;/li&gt;
&lt;li&gt;Test the behavior with the browsers and assistive technologies your application supports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;HTML&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"live"&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"polite"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These elements illustrate a possible structure, not a complete accessibility implementation. The application must still control what is announced and when. A code snippet alone does not establish WCAG conformance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Show observable application states
&lt;/h3&gt;

&lt;p&gt;Status messages such as &lt;code&gt;Searching&lt;/code&gt;, &lt;code&gt;Retrieving information&lt;/code&gt;, &lt;code&gt;Waiting for approval&lt;/code&gt;, and &lt;code&gt;Generating response&lt;/code&gt; can help users understand the application’s progress.&lt;/p&gt;

&lt;p&gt;Use states that correspond to events the application can observe. Avoid presenting unverifiable internal model reasoning as a factual explanation of what the system is doing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure and debug streaming performance in production
&lt;/h3&gt;

&lt;p&gt;A single &lt;code&gt;request completed&lt;/code&gt; metric does not reveal where users experience friction. Track enough of the interaction to distinguish generation delays from frontend and transport problems.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;What it helps you investigate&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TTFT&lt;/td&gt;
&lt;td&gt;Delay before the first token becomes available at the defined measurement point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to first visible content&lt;/td&gt;
&lt;td&gt;Delay before response content appears in the interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to useful content&lt;/td&gt;
&lt;td&gt;When the response first provides meaningful information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to completion&lt;/td&gt;
&lt;td&gt;Total response duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interruption rate&lt;/td&gt;
&lt;td&gt;How often streams end unexpectedly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cancellation rate&lt;/td&gt;
&lt;td&gt;How often users stop generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry rate&lt;/td&gt;
&lt;td&gt;How often users need another attempt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool-call success and failure&lt;/td&gt;
&lt;td&gt;Reliability of structured AI operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client rendering cost&lt;/td&gt;
&lt;td&gt;Browser work during streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Define each metric consistently and separate server-side timings from browser-side timings when possible. This makes comparisons more useful across requests and releases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Diagnose where the delay occurs
&lt;/h3&gt;

&lt;p&gt;Suppose users report that an assistant feels slow even though backend generation time looks normal.&lt;/p&gt;

&lt;p&gt;Start by comparing TTFT, time to useful content, browser rendering performance, and the timing of data received by the client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High TTFT:&lt;/strong&gt; Investigate request handling, provider queueing, model prefill, retrieval, and network delays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low TTFT but late useful content:&lt;/strong&gt; Inspect the response structure and the sequence of retrieval or tool operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timely content but a sluggish interface:&lt;/strong&gt; Examine rendering frequency, Markdown processing, layout instability, and main-thread work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output arriving in bursts:&lt;/strong&gt; Check buffering between the backend and browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if server logs show that output is generated progressively but browser timestamps show that chunks arrive together, investigate the delivery path before changing the model or rewriting the frontend.&lt;/p&gt;

&lt;p&gt;This approach narrows the investigation before you make changes. The measurements help identify where to look; they do not establish a cause on their own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production checklist for AI streaming UX
&lt;/h2&gt;

&lt;p&gt;Before releasing a streaming AI interface, review the following items.&lt;/p&gt;

&lt;h3&gt;
  
  
  User experience
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Show a clear processing state before the first response arrives.&lt;/li&gt;
&lt;li&gt;Keep the layout stable while content grows.&lt;/li&gt;
&lt;li&gt;Preserve the user’s reading position.&lt;/li&gt;
&lt;li&gt;Provide Stop and Retry controls where appropriate.&lt;/li&gt;
&lt;li&gt;Preserve useful partial output after failures.&lt;/li&gt;
&lt;li&gt;Distinguish completed, canceled, and interrupted responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Streaming and transport
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Handle partial network reads correctly.&lt;/li&gt;
&lt;li&gt;Decode incoming data incrementally and parse complete protocol messages.&lt;/li&gt;
&lt;li&gt;Check proxy and CDN buffering.&lt;/li&gt;
&lt;li&gt;Handle timeouts and interrupted connections.&lt;/li&gt;
&lt;li&gt;Define retry and cancellation behavior.&lt;/li&gt;
&lt;li&gt;Make it clear whether retries restart or resume generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keep provider credentials on the server.&lt;/li&gt;
&lt;li&gt;Treat model output as untrusted input.&lt;/li&gt;
&lt;li&gt;Escape text and sanitize supported rich HTML appropriately.&lt;/li&gt;
&lt;li&gt;Validate structured payloads and tool arguments.&lt;/li&gt;
&lt;li&gt;Authorize privileged actions on the server.&lt;/li&gt;
&lt;li&gt;Log important side effects where required.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create live regions before streamed content arrives.&lt;/li&gt;
&lt;li&gt;Announce meaningful chunks instead of every token.&lt;/li&gt;
&lt;li&gt;Separate response announcements from status messages.&lt;/li&gt;
&lt;li&gt;Test with supported assistive technologies and browsers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Observability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Measure TTFT and time to useful content.&lt;/li&gt;
&lt;li&gt;Track cancellations, retries, and interruptions.&lt;/li&gt;
&lt;li&gt;Monitor browser rendering performance.&lt;/li&gt;
&lt;li&gt;Track structured tool-call failures.&lt;/li&gt;
&lt;li&gt;Compare server generation timings with browser receipt and rendering timings.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: Do all AI applications need streaming?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Streaming is most useful when responses are long, delayed, or useful before completion. For short and predictable outputs, a conventional request-response implementation may be simpler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: Does streaming make an AI application faster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not necessarily. Streaming can improve perceived responsiveness by displaying useful output before the full response is ready. It does not automatically reduce the time required to generate the complete answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: What is the difference between TTFT and time to useful content?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TTFT measures when the first token becomes available at a defined measurement point. Time to useful content measures when the response first provides information the user can act on. Tracking both helps distinguish a quick initial response from an answer that becomes useful quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: When should I use WebSockets instead of HTTP streaming?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use WebSockets when the application needs ongoing, bidirectional communication, such as voice interactions or collaborative sessions. For many text-generation experiences, HTTP streaming is sufficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5: What should happen when a stream fails midway?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Preserve the output already displayed, communicate that the response was interrupted, and provide an appropriate recovery option. Whether the next attempt can resume or must restart depends on the backend and streaming protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6: Is streaming text enough for modern AI interfaces?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not always. Some applications also need structured UI components, tool-call status, citations, approval flows, and recovery from partial results. Choose the interaction model according to what users need to understand and accomplish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Good AI streaming UX is about more than displaying tokens as they arrive. The interface needs to communicate progress, preserve useful output, stay stable as content grows, and give users control when things go wrong.&lt;/p&gt;

&lt;p&gt;Start by measuring TTFT, time to first visible content, and time to useful content. Then make the streaming lifecycle explicit, handle incomplete data correctly, validate structured output, and keep privileged actions on the server. Test accessibility and monitor browser performance alongside backend timings.&lt;/p&gt;

&lt;p&gt;With those foundations in place, you can build AI experiences that remain understandable and useful even when responses take time, involve tools, or fail partway through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-ai-powered-application" rel="noopener noreferrer"&gt;Introducing Syncfusion A2UI: Build Agent-Driven Interfaces with Reusable UI Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-composer-ai-interface" rel="noopener noreferrer"&gt;Build Agent-Driven UI Designs Faster Using Syncfusion A2UI Composer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-essential-studio-2026-vol-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion Essential Studio 2026 Volume 3: A2UI, New JavaScript Document Libraries, and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/sneak-peek-2026-vol-3-document-sdk" rel="noopener noreferrer"&gt;Sneak Peek: 3 New Libraries Coming to Syncfusion Document SDK 2026 Volume 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>syncfusion</category>
      <category>aidevelopment</category>
      <category>frontend</category>
      <category>generativeaiplatform</category>
    </item>
    <item>
      <title>What’s New in Syncfusion .NET MAUI 2026 Volume 3: New Controls and Features</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Fri, 09 Oct 2026 14:47:11 +0000</pubDate>
      <link>https://dev.to/syncfusion/whats-new-in-syncfusion-net-maui-2026-volume-3-new-controls-and-features-49g1</link>
      <guid>https://dev.to/syncfusion/whats-new-in-syncfusion-net-maui-2026-volume-3-new-controls-and-features-49g1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Syncfusion Essential Studio 2026 Volume 3 (v35.1.37) introduces five new preview controls for .NET MAUI: TreeGrid, Data Pager, Diagram, Grid Splitter, and Interactive Viewer. The release also adds a multirow layout for DataGrid, new scheduling and charting capabilities, Markdown and streaming support in AI AssistView, and marks Rich Text Editor as production-ready.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Building .NET MAUI applications involves handling UI requirements such as displaying hierarchical data, navigating large datasets, creating interactive diagrams, and designing flexible layouts. Implementing these capabilities can add to development time and maintenance effort.&lt;/p&gt;

&lt;p&gt;Syncfusion Essential Studio 2026 Volume 3 introduces five new preview controls, TreeGrid, Data Pager, Diagram, Grid Splitter, and Interactive Viewer, to address these needs. The release also enhances DataGrid, Scheduler, Charts, AI AssistView, and SignaturePad, while making Rich Text Editor production-ready.&lt;/p&gt;

&lt;p&gt;Explore the key updates and the breaking changes to review before upgrading to v35.1.37.&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET MAUI 2026 Volume 3 at a glance
&lt;/h2&gt;

&lt;p&gt;Here’s a quick look at the major updates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;What’s new&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TreeGrid (Preview)&lt;/td&gt;
&lt;td&gt;Display hierarchical data in a grid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Pager (Preview)&lt;/td&gt;
&lt;td&gt;Navigate data through pages and load data on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diagram (Preview)&lt;/td&gt;
&lt;td&gt;Create interactive diagrams with nodes and connectors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grid Splitter (Preview)&lt;/td&gt;
&lt;td&gt;Build resizable application layouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive Viewer (Preview)&lt;/td&gt;
&lt;td&gt;Zoom, pan, rotate, and reset content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DataGrid&lt;/td&gt;
&lt;td&gt;Multirow column layout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduler&lt;/td&gt;
&lt;td&gt;Hierarchical resources, appointment overlap controls, and iCalendar support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Charts&lt;/td&gt;
&lt;td&gt;Gradient line series, trackball enhancements, and axis styling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI AssistView&lt;/td&gt;
&lt;td&gt;Markdown, response streaming, and prompt libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SignaturePad&lt;/td&gt;
&lt;td&gt;Check whether a signature has been provided&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rich Text Editor&lt;/td&gt;
&lt;td&gt;Now production-ready, with a grouped toolbar and structured content support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The release introduces five new preview controls and enhancements to existing controls, while making Rich Text Editor production-ready. Let’s explore these updates and how they can help you build .NET MAUI applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. TreeGrid
&lt;/h2&gt;

&lt;p&gt;Have you ever needed to display parent-child data while still giving users the familiar experience of a data grid?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/maui-controls/maui-treegrid" rel="noopener noreferrer"&gt;.NET MAUI TreeGrid&lt;/a&gt; control is designed for this scenario. It displays self-relational and nested data in a tabular format, allowing users to expand and collapse hierarchical records. You can also use features such as sorting, filtering, selection, and frozen columns.&lt;/p&gt;

&lt;p&gt;For example, you could use TreeGrid to display:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organization structures.&lt;/li&gt;
&lt;li&gt;Product categories and subcategories.&lt;/li&gt;
&lt;li&gt;Project tasks and subtasks.&lt;/li&gt;
&lt;li&gt;File or folder hierarchies.&lt;/li&gt;
&lt;li&gt;Other parent-child datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bind self-relational and nested collections.&lt;/li&gt;
&lt;li&gt;Use automatically generated or manually defined columns.&lt;/li&gt;
&lt;li&gt;Expand and collapse nodes interactively or programmatically.&lt;/li&gt;
&lt;li&gt;Sort by one or multiple columns.&lt;/li&gt;
&lt;li&gt;Filter data programmatically.&lt;/li&gt;
&lt;li&gt;Freeze columns.&lt;/li&gt;
&lt;li&gt;Customize the appearance of the grid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv65mz2tw3d29yzwaf9na.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv65mz2tw3d29yzwaf9na.gif" alt="TreeGrid (Preview)" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;
TreeGrid (Preview)



&lt;h2&gt;
  
  
  2. Data Pager
&lt;/h2&gt;

&lt;p&gt;Displaying a large collection at once can make data harder to navigate and require loading more records than users need.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/maui-controls/maui-pager" rel="noopener noreferrer"&gt;.NET MAUI Pager&lt;/a&gt; simplifies navigation through large datasets by dividing records into pages and loading data on demand. It also integrates with data-bound controls such as DataGrid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Standard page navigation.&lt;/li&gt;
&lt;li&gt;On-demand data loading.&lt;/li&gt;
&lt;li&gt;Configurable navigation modes.&lt;/li&gt;
&lt;li&gt;Programmatic page navigation.&lt;/li&gt;
&lt;li&gt;Horizontal and vertical layouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sd5bvlfukduqt7ggtdd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sd5bvlfukduqt7ggtdd.gif" alt="Data Pager (Preview)" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;
Data Pager (Preview)



&lt;h2&gt;
  
  
  3. Diagram
&lt;/h2&gt;

&lt;p&gt;Need to show relationships, processes, connections, or other visual structures?&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://www.syncfusion.com/diagram-sdk/maui-diagram" rel="noopener noreferrer"&gt;Diagram&lt;/a&gt; control provides a canvas for building interactive diagrams using nodes, connectors, annotations, and reusable symbols. It also supports drag-and-drop editing, customizable connection ports, a symbol palette, and serialization.&lt;/p&gt;

&lt;p&gt;You can use it for scenarios such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flowcharts.&lt;/li&gt;
&lt;li&gt;Process diagrams.&lt;/li&gt;
&lt;li&gt;Organization charts.&lt;/li&gt;
&lt;li&gt;Visual editors.&lt;/li&gt;
&lt;li&gt;Relationship diagrams.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Predefined shapes, templates, and paths.&lt;/li&gt;
&lt;li&gt;Straight and orthogonal connectors.&lt;/li&gt;
&lt;li&gt;Annotations and labels.&lt;/li&gt;
&lt;li&gt;Customizable connection ports.&lt;/li&gt;
&lt;li&gt;Symbol palette.&lt;/li&gt;
&lt;li&gt;Save and load support.&lt;/li&gt;
&lt;li&gt;Export support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F71xvcedb2v77zsnvuy41.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F71xvcedb2v77zsnvuy41.webp" alt="Diagram (Preview)" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
Diagram (Preview)



&lt;h2&gt;
  
  
  4. Interactive Viewer
&lt;/h2&gt;

&lt;p&gt;Sometimes a standard image or content view isn’t enough. Users may need to zoom in, move around, or rotate content to inspect details.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/maui-controls/maui-interactive-viewer" rel="noopener noreferrer"&gt;.NET MAUI Interactive Viewer&lt;/a&gt; adds these interactions to .NET MAUI views. You can use it with images, maps, diagrams, documents, and other visual content that benefits from closer inspection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Zoom and pan.&lt;/li&gt;
&lt;li&gt;Rotate content.&lt;/li&gt;
&lt;li&gt;Reset the view.&lt;/li&gt;
&lt;li&gt;Display any .NET MAUI view.&lt;/li&gt;
&lt;li&gt;Support light and dark themes.&lt;/li&gt;
&lt;li&gt;Customize interactions through events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feh0898v76knrg9tn4f3w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feh0898v76knrg9tn4f3w.gif" alt="Interactive Viewer (Preview)" width="432" height="780"&gt;&lt;/a&gt;&lt;/p&gt;
Interactive Viewer (Preview)



&lt;h2&gt;
  
  
  5. Grid Splitter
&lt;/h2&gt;

&lt;p&gt;Do you want users to control how much space different areas of your application use?&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://www.syncfusion.com/maui-controls/maui-gridsplitter" rel="noopener noreferrer"&gt;Grid Splitter&lt;/a&gt; lets users resize adjacent rows or columns by dragging a splitter. You can also allow panes to expand or collapse and set minimum and maximum sizes.&lt;/p&gt;

&lt;p&gt;This makes it useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboards.&lt;/li&gt;
&lt;li&gt;Document editors.&lt;/li&gt;
&lt;li&gt;Image viewers.&lt;/li&gt;
&lt;li&gt;Master-detail layouts.&lt;/li&gt;
&lt;li&gt;Workspace-style applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Interactive pane resizing.&lt;/li&gt;
&lt;li&gt;Expand and collapse actions.&lt;/li&gt;
&lt;li&gt;Horizontal and vertical orientations.&lt;/li&gt;
&lt;li&gt;Minimum and maximum pane sizes.&lt;/li&gt;
&lt;li&gt;Nested splitter layouts.&lt;/li&gt;
&lt;li&gt;Runtime event support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fll5vrpdh145yilaf563k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fll5vrpdh145yilaf563k.gif" alt="Grid Splitter (Preview)" width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;
Grid Splitter (Preview)



&lt;h2&gt;
  
  
  What’s new in existing .NET MAUI controls?
&lt;/h2&gt;

&lt;p&gt;The release also enhances several existing controls with new capabilities for data presentation, AI interactions, scheduling, visualization, and content editing.&lt;/p&gt;

&lt;h3&gt;
  
  
  DataGrid
&lt;/h3&gt;

&lt;p&gt;Wide &lt;a href="https://www.syncfusion.com/maui-controls/maui-datagrid" rel="noopener noreferrer"&gt;grids&lt;/a&gt; can become difficult to use when each record contains many fields.&lt;/p&gt;

&lt;p&gt;The new Multi-Row Column Layout lets you arrange related fields across multiple row levels instead of placing everything into one wide row.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwl59ppd2gt9m19mee1ad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwl59ppd2gt9m19mee1ad.png" alt="Multi-Row Column Layout" width="800" height="730"&gt;&lt;/a&gt;&lt;/p&gt;
Multi-Row Column Layout



&lt;p&gt;For employee, product, or order records with many fields, this layout helps organize related information without forcing every field into a single wide row.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI AssistView
&lt;/h3&gt;

&lt;p&gt;AI responses are no longer limited to plain text. Your application may need to display headings, lists, links, code blocks, and progressively generated responses.&lt;/p&gt;

&lt;p&gt;With the latest &lt;a href="https://www.syncfusion.com/maui-controls/maui-ai-assistview" rel="noopener noreferrer"&gt;AI AssistView&lt;/a&gt; updates, you can render Markdown content and stream AI responses as they are generated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwmvniwdhqk02cl8hcxgq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwmvniwdhqk02cl8hcxgq.gif" alt="Markdown rendering and response streaming" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;
Markdown rendering and response streaming



&lt;p&gt;A new prompt library also helps you organize reusable prompts by categories and topics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0gqdc5vkelzjio2nxnxq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0gqdc5vkelzjio2nxnxq.gif" alt="Prompt library" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;
Prompt library



&lt;p&gt;&lt;strong&gt;What you can now do&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Render Markdown in AI responses.&lt;/li&gt;
&lt;li&gt;Display generated responses progressively through streaming.&lt;/li&gt;
&lt;li&gt;Organize reusable prompts with a prompt library.&lt;/li&gt;
&lt;li&gt;Create more structured AI conversations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SignaturePad
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/maui-controls/maui-signaturepad" rel="noopener noreferrer"&gt;SignaturePad&lt;/a&gt; now includes an &lt;code&gt;IsEmpty&lt;/code&gt; property that lets you determine whether a user has provided a signature.&lt;/p&gt;

&lt;p&gt;You can check this property before moving to the next step or submitting a form. For workflows that require a signature, it provides a simple way to validate whether the user has entered one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduler
&lt;/h3&gt;

&lt;p&gt;If your application manages multiple resources, overlapping appointments, or different calendar systems, the Scheduler now gives you more control.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/scheduler-sdk/maui-smart-scheduler" rel="noopener noreferrer"&gt;.NET MAUI Scheduler&lt;/a&gt; adds support for hierarchical resources, appointment overlap controls, major and minor time-slot ticks, Month Agenda view, and &lt;code&gt;iCalendar&lt;/code&gt; import and export.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Key updates&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Hierarchical resources:&lt;/strong&gt; Organize resources using parent-child relationships.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuz5wu6z7jgwh16h0d2q1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuz5wu6z7jgwh16h0d2q1.gif" alt="Hierarchical resource view" width="800" height="679"&gt;&lt;/a&gt;&lt;/p&gt;
Hierarchical resource view



&lt;p&gt;&lt;strong&gt;Appointment limits:&lt;/strong&gt; Control how many overlapping appointments are displayed at once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2oapi3e2sz3rwp5uncok.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2oapi3e2sz3rwp5uncok.gif" alt="Limit concurrent appointments" width="800" height="680"&gt;&lt;/a&gt;&lt;/p&gt;
Limit concurrent appointments



&lt;p&gt;&lt;strong&gt;Major and minor ticks:&lt;/strong&gt; Make time slots easier to read in scheduling views.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzb7kcjkokqe6delx4xh5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzb7kcjkokqe6delx4xh5.gif" alt="Major and minor time slot ticks" width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;
Major and minor time slot ticks



&lt;p&gt;&lt;strong&gt;Month Agenda:&lt;/strong&gt; View appointments in an agenda-style format within the month view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5asrc1ohfcr29mzepxb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx5asrc1ohfcr29mzepxb.gif" alt="Month agenda view" width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;
Month agenda view



&lt;p&gt;&lt;strong&gt;iCalendar support:&lt;/strong&gt; Import and export appointments using .ics files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fysew3bnwtgd6udv1cae2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fysew3bnwtgd6udv1cae2.gif" alt="Import and export appointments using iCalendar support" width="800" height="682"&gt;&lt;/a&gt;&lt;/p&gt;
Import and export appointments using iCalendar support



&lt;h3&gt;
  
  
  Charts
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.syncfusion.com/maui-controls/maui-cartesian-charts" rel="noopener noreferrer"&gt;Charts&lt;/a&gt; also receive several improvements in this release.&lt;/p&gt;

&lt;p&gt;You can now use gradient support for line series to create smoother color transitions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2qpw7ygm4j64byidk3b6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2qpw7ygm4j64byidk3b6.png" alt="Gradients in line type series" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;
Gradients in line type series



&lt;p&gt;Trackball support has also been extended to stacked column charts, making it easier to inspect and compare data points.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fewmq9wp70kpoziocvgyq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fewmq9wp70kpoziocvgyq.png" alt="Stacked column trackball" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;
Stacked column trackball



&lt;p&gt;Annotation &lt;code&gt;ZIndex&lt;/code&gt; support for controlling layers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F84niooq6fplsfhxaq3sg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F84niooq6fplsfhxaq3sg.png" alt="Annotation ZIndex" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;
Annotation ZIndex



&lt;p&gt;Axis range styling for highlighting specific data ranges.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhtg8a4t53tublcr8wwxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhtg8a4t53tublcr8wwxm.png" alt="Axis range styling" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;
Axis range styling



&lt;p&gt;Additional updates include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Legend title support.&lt;/li&gt;
&lt;li&gt;Accessibility improvements across chart controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rich Text Editor:
&lt;/h3&gt;

&lt;p&gt;One of the important milestones in this release is that the &lt;a href="https://www.syncfusion.com/rich-text-editor-sdk/maui-rich-text-editor" rel="noopener noreferrer"&gt;.NET MAUI Rich Text Editor&lt;/a&gt; is now production-ready.&lt;/p&gt;

&lt;p&gt;The release also adds a mobile-friendly, grouped toolbar through the &lt;code&gt;IsGrouped&lt;/code&gt; property. Related toolbar actions can be presented together in a touch-friendly interface on Android and iOS, helping reduce toolbar clutter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ef13dk2a0w2v6vj15gc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ef13dk2a0w2v6vj15gc.gif" alt="Grouped mobile toolbar" width="378" height="664"&gt;&lt;/a&gt;&lt;/p&gt;
Grouped mobile toolbar



&lt;p&gt;This makes formatting commands easier to access on mobile devices while keeping the editing interface organized.&lt;/p&gt;

&lt;h3&gt;
  
  
  .NET MAUI Rich Text Editor
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/rich-text-editor-sdk/maui-rich-text-editor" rel="noopener noreferrer"&gt;Rich Text Editor&lt;/a&gt; also supports structured content through schema-based block nodes and provides a unified Value property for HTML, plain text, and schema block node content.&lt;/p&gt;

&lt;p&gt;Users can also convert selected text to uppercase or lowercase through the toolbar.&lt;/p&gt;

&lt;p&gt;So, while the five new controls are still in preview, you can now consider the Rich Text Editor for production .NET MAUI applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you upgrade: Breaking changes and .NET support
&lt;/h2&gt;

&lt;p&gt;Planning to move your application to v35.1.37?&lt;/p&gt;

&lt;p&gt;Before updating your packages, review the following changes to identify any updates needed in your existing code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Pager package change
&lt;/h3&gt;

&lt;p&gt;The SfDataPager control has moved from &lt;a href="https://www.nuget.org/packages/Syncfusion.Maui.DataGrid" rel="noopener noreferrer"&gt;Syncfusion.Maui.DataGrid&lt;/a&gt; to &lt;a href="https://www.nuget.org/packages/Syncfusion.Maui.DataPager" rel="noopener noreferrer"&gt;Syncfusion.Maui.DataPager&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Update the package references and namespaces in your &lt;code&gt;C#&lt;/code&gt; and &lt;code&gt;XAML&lt;/code&gt; code to use the new package.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rich Text Editor API changes
&lt;/h3&gt;

&lt;p&gt;The Rich Text Editor now uses a unified Value property for content. The previous HtmlText and Text properties are no longer publicly accessible. The Value property supports HTML, plain text, and schema block node content, with the format determined by the &lt;code&gt;TValue&lt;/code&gt; enumeration.&lt;/p&gt;

&lt;p&gt;Before upgrading, replace references to the previous content properties with Value and the appropriate content format.&lt;/p&gt;

&lt;p&gt;The release notes also identify obsolete configuration APIs, including &lt;code&gt;DefaultTextColor&lt;/code&gt;, &lt;code&gt;DefaultFontFamily&lt;/code&gt;, &lt;code&gt;DefaultFontSize&lt;/code&gt;, and &lt;code&gt;UseHybridWebView&lt;/code&gt;. Check the official migration guidance for their required replacements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Color Picker collection change
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;PaletteColors&lt;/code&gt; property now uses &lt;code&gt;ObservableCollection&lt;/code&gt; instead of &lt;code&gt;List&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Update the collection type and related code wherever &lt;code&gt;PaletteColors&lt;/code&gt; is assigned or modified.&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET 9 support timeline
&lt;/h2&gt;

&lt;p&gt;Syncfusion has announced that support for .NET 9 in its .NET MAUI controls will end following the release of .NET 11 GA in November 2026.&lt;/p&gt;

&lt;p&gt;If your projects target .NET 9, plan a framework upgrade alongside the control upgrade to maintain access to future updates and support.&lt;/p&gt;

&lt;p&gt;For complete migration details, review the official &lt;a href="https://www.syncfusion.com/products/whatsnew" rel="noopener noreferrer"&gt;What’s New&lt;/a&gt; and &lt;a href="https://help.syncfusion.com/maui/release-notes/v35.1.37" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; documentation before upgrading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: Which .NET MAUI controls are production-ready?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Rich Text Editor is now production-ready. The five controls introduced in this release, TreeGrid, Data Pager, Diagram, Grid Splitter, and Interactive Viewer, remain in preview.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: Does .NET MAUI 2026 Volume 3 support .NET 9 and .NET 10?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The release supports both .NET 9 and .NET 10. However, Syncfusion has announced that .NET 9 support will end following the release of .NET 11 GA in November 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: Are there breaking changes in v35.1.37?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Breaking changes affect Data Pager, Rich Text Editor, and Color Picker. Review the package, API, and collection changes described above and consult the official release notes before upgrading your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Syncfusion .NET MAUI 2026 Volume 3 introduces five new controls for hierarchical data, large datasets, diagrams, interactive content, and flexible layouts. It also brings updates to DataGrid, Scheduler, Charts, AI AssistView, SignaturePad, and Rich Text Editor.&lt;/p&gt;

&lt;p&gt;These enhancements give developers more options for building data-driven interfaces, interactive experiences, and content-editing workflows.&lt;/p&gt;

&lt;p&gt;If you’re upgrading to &lt;a href="https://www.syncfusion.com/forums/198591/essential-studio-2026-volume-3-main-release-v35-1-37-is-available-for-download" rel="noopener noreferrer"&gt;Essential Studio&lt;sup&gt;®&lt;/sup&gt; 2026 Volume 3&lt;/a&gt;, review the breaking changes and .NET support timeline before updating your application.&lt;/p&gt;

&lt;p&gt;Explore the latest &lt;a href="https://www.syncfusion.com/maui-controls" rel="noopener noreferrer"&gt;.NET MAUI controls&lt;/a&gt; and try the new features in the interactive &lt;a href="https://www.syncfusion.com/demos/maui" rel="noopener noreferrer"&gt;demos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;New to Syncfusion? Start with the .NET MAUI &lt;a href="https://www.syncfusion.com/downloads/maui/confirm" rel="noopener noreferrer"&gt;free trial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have questions or feedback, reach out through our &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support forums&lt;/a&gt;, &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback portal&lt;/a&gt;, or &lt;a href="https://support.syncfusion.com/" rel="noopener noreferrer"&gt;support portal&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-essential-studio-2026-vol-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion Essential Studio 2026 Volume 3: A2UI, New JavaScript Document Libraries, and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/build-maui-apps-dot-net-11-webinar" rel="noopener noreferrer"&gt;Build Modern .NET MAUI Apps with .NET 11 [Webinar Show Notes]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/fifth-set-open-source-maui-controls" rel="noopener noreferrer"&gt;Introducing the Fifth Set of Open-Source Syncfusion .NET MAUI Controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/mono-vs-coreclr-dotnet-maui" rel="noopener noreferrer"&gt;Mono vs CoreCLR in .NET MAUI: Performance, Compatibility, and What Changes in .NET 11&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>netmaui</category>
      <category>aiassistview</category>
      <category>charts</category>
      <category>datagrid</category>
    </item>
    <item>
      <title>Struggling with Complex Document Tasks? Explore Syncfusion Essential Studio 2026 Volume 3</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Thu, 08 Oct 2026 13:53:20 +0000</pubDate>
      <link>https://dev.to/syncfusion/struggling-with-complex-document-tasks-explore-syncfusion-essential-studio-2026-volume-3-5451</link>
      <guid>https://dev.to/syncfusion/struggling-with-complex-document-tasks-explore-syncfusion-essential-studio-2026-volume-3-5451</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Building collaboration, review, and automation into document solutions can require significant custom development. Syncfusion Essential Studio 2026 Volume 3 adds real-time collaboration, PDF comparison, AI-assisted document operations, form recognition, enhanced review tools, and spreadsheet capabilities across the PDF Viewer, DOCX Editor, and Spreadsheet Editor.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Need more than basic document functionality?
&lt;/h2&gt;

&lt;p&gt;Document solutions often require capabilities that go beyond viewing and editing. Collaboration, document comparison, form recognition, automation, and spreadsheet validation can all become part of the same process.&lt;/p&gt;

&lt;p&gt;Supporting all these capabilities can require multiple layers of custom development.&lt;/p&gt;

&lt;p&gt;Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;Essential Studio 2026 Volume 3&lt;/a&gt; introduces new capabilities across the &lt;a href="https://www.syncfusion.com/pdf-viewer-sdk" rel="noopener noreferrer"&gt;PDF Viewer&lt;/a&gt;, &lt;a href="https://www.syncfusion.com/docx-editor-sdk" rel="noopener noreferrer"&gt;DOCX Editor&lt;/a&gt;, and &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk" rel="noopener noreferrer"&gt;Spreadsheet Editor&lt;/a&gt; to simplify document-centric development.&lt;/p&gt;

&lt;p&gt;From real-time collaboration and PDF comparison to AI-assisted document operations, form recognition, and spreadsheet validation, this release adds capabilities for building more complete document solutions.&lt;/p&gt;

&lt;p&gt;Let’s explore what’s new and how these capabilities can help you build document solutions faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s new in Essential Studio 2026 Volume 3
&lt;/h2&gt;

&lt;p&gt;This release brings new capabilities for collaboration, document review, automation, and spreadsheet data management.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PDF Viewer&lt;/strong&gt;: Real-time collaboration, link annotations, document comparison, AI-assisted document operations, form recognition, digital signatures, thumbnail navigation, and document modification tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOCX Editor&lt;/strong&gt;: Author-based comment highlighting, customizable colors for tracked changes and revision types, and AI-assisted document operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spreadsheet Editor&lt;/strong&gt;: Rich text formatting, text overflow, real-time collaboration, AI-assisted spreadsheet operations, data validation, find and replace, charts, expanded APIs, and worksheet tab controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these updates support key document scenarios such as collaboration, review, automation, and data management.&lt;/p&gt;

&lt;h2&gt;
  
  
  PDF Viewer: Simplify collaboration, review, and document processing
&lt;/h2&gt;

&lt;p&gt;Displaying a PDF is often just the beginning of a broader document process. Once users need to review, approve, annotate, compare, or process documents, additional capabilities become essential.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web PDF Viewer
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Collaborate on PDFs in real time
&lt;/h4&gt;

&lt;p&gt;Contract reviews and approval processes often involve multiple users working with the same document. Managing separate copies can lead to duplicate annotations, conflicting changes, and outdated versions.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/pdf-viewer-sdk/javascript-pdf-viewer" rel="noopener noreferrer"&gt;Web PDF Viewer&lt;/a&gt; now supports real-time collaboration for annotations, form fields, and page organization. Multiple users can work on the same PDF simultaneously, while changes made by one participant are automatically synchronized with others.&lt;/p&gt;

&lt;p&gt;This provides a shared document experience that helps teams review and edit PDFs without managing separate document copies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslbv5629271zyb1zipnw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fslbv5629271zyb1zipnw.gif" alt="Real-time collaborative editing in Web PDF Viewer" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;
Real-time collaborative editing in Web PDF Viewer



&lt;h4&gt;
  
  
  Add links for easier PDF navigation
&lt;/h4&gt;

&lt;p&gt;Long PDFs can be difficult to navigate when users need to move between sections or access related resources.&lt;/p&gt;

&lt;p&gt;The PDF Viewer now supports web-link and document-link annotations. Users can create, edit, and delete interactive links that open external websites or navigate to specific pages and locations within the same PDF.&lt;/p&gt;

&lt;p&gt;This can be useful for technical documentation, manuals, compliance records, and other lengthy documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0c6k0vlkwfltfzqcrt3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0c6k0vlkwfltfzqcrt3.gif" alt="Adding navigation and reference links in PDF Viewer" width="759" height="446"&gt;&lt;/a&gt;&lt;/p&gt;
Adding navigation and reference links in PDF Viewer



&lt;h4&gt;
  
  
  Add custom text stamps to document reviews
&lt;/h4&gt;

&lt;p&gt;Document approval processes often need clear status markers such as &lt;strong&gt;Approved, Rejected,&lt;/strong&gt; or &lt;strong&gt;Needs Revision.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With custom text stamps, users can create personalized stamp annotations using custom text instead of static images. They can configure the stamp title, font, text color, background color, and optional author, date, and time information, with a live preview during creation.&lt;/p&gt;

&lt;p&gt;For example, a compliance reviewer can apply a review-status label directly to a policy document while preserving the review context for future reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz0wyzfnxi72eyln1pl26.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz0wyzfnxi72eyln1pl26.gif" alt="Adding custom text stamps in PDF Viewer" width="760" height="323"&gt;&lt;/a&gt;&lt;/p&gt;
Adding custom text stamps in PDF Viewer



&lt;h4&gt;
  
  
  Compare PDF versions and identify changes
&lt;/h4&gt;

&lt;p&gt;Comparing lengthy versions of contracts or policies manually can make it difficult to identify the changes that matter.&lt;/p&gt;

&lt;p&gt;The PDF Viewer now supports semantic text comparison between two PDF documents. It identifies added, deleted, and modified text and provides detailed difference information through a dedicated API.&lt;/p&gt;

&lt;p&gt;Instead of manually reviewing both documents page by page, users can focus on the content that changed.&lt;/p&gt;

&lt;p&gt;This can be especially useful for legal agreements, HR policies, regulatory documents, and other documents that go through frequent revisions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Automate PDF tasks with AI agents
&lt;/h4&gt;

&lt;p&gt;PDF-related tasks often involve repetitive operations such as content extraction, page navigation, annotation management, form processing, and data redaction.&lt;/p&gt;

&lt;p&gt;With WebMCP tools, AI agents can interact with PDF documents and automate operations such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document navigation&lt;/li&gt;
&lt;li&gt;Content extraction&lt;/li&gt;
&lt;li&gt;Annotation management&lt;/li&gt;
&lt;li&gt;Form field operations&lt;/li&gt;
&lt;li&gt;Redaction&lt;/li&gt;
&lt;li&gt;Document actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These capabilities also support document analysis, summarization, and automated form processing.&lt;/p&gt;

&lt;p&gt;Developers and users remain in control through review, approval, customization, and undo/redo support. This makes it possible to automate routine PDF operations while keeping important decisions with the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blazor Smart PDF Viewer
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Turn scanned forms into interactive documents
&lt;/h4&gt;

&lt;p&gt;Scanned forms often require users or developers to manually recreate fields before the documents can be used digitally.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/pdf-viewer-sdk/ai-powered-blazor-pdf-viewer" rel="noopener noreferrer"&gt;Blazor Smart PDF Viewer&lt;/a&gt; now supports form recognition for scanned or image-based PDF documents. It can detect text boxes, check boxes, radio buttons, and signature fields and convert them into interactive PDF form fields while preserving their positions.&lt;/p&gt;

&lt;p&gt;For registration, onboarding, customer-intake, or application-processing solutions, this can help turn scanned forms into usable digital documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Futr683q6fjir5i60lscm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Futr683q6fjir5i60lscm.gif" alt="Scanning form elements in Blazor PDF Viewer" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;
Scanning form elements in Blazor PDF Viewer



&lt;h3&gt;
  
  
  .NET MAUI Smart PDF Viewer
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Simplify PDF processing with AI
&lt;/h4&gt;

&lt;p&gt;Processing PDFs often involves repetitive tasks such as filling forms, identifying sensitive information, and extracting insights from lengthy documents.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/pdf-viewer-sdk/ai-powered-net-maui-pdf-viewer" rel="noopener noreferrer"&gt;.NET MAUI Smart PDF Viewer&lt;/a&gt;, available in preview, introduces AI-powered capabilities for &lt;strong&gt;Smart Form Fill, Smart Redaction, and Document Summarization&lt;/strong&gt;. These features can help users populate form fields, identify and redact sensitive information, and generate concise summaries directly within their applications.&lt;/p&gt;

&lt;p&gt;This can simplify document processing while helping users work with PDF content more efficiently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbyn9ku8eeutp3c8b3l5g.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbyn9ku8eeutp3c8b3l5g.webp" alt="Adding and validating digital signatures in .NET MAUI PDF Viewer" width="799" height="444"&gt;&lt;/a&gt;&lt;/p&gt;
Adding and validating digital signatures in .NET MAUI PDF Viewer



&lt;h4&gt;
  
  
  Navigate PDFs with thumbnail previews
&lt;/h4&gt;

&lt;p&gt;Large PDFs can be difficult to navigate on smaller screens.&lt;/p&gt;

&lt;p&gt;The .NET MAUI PDF Viewer now supports thumbnail previews of PDF pages. Users can open a thumbnail pane on desktop and tablet devices or a dedicated thumbnail view on mobile devices, then select a thumbnail to jump directly to a page.&lt;/p&gt;

&lt;p&gt;This provides a more visual way to browse and navigate lengthy documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fob62it6sif31efgwr5tc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fob62it6sif31efgwr5tc.webp" alt="Page navigation with thumbnail preview option" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;
Page navigation with thumbnail preview option



&lt;h4&gt;
  
  
  Track document modifications with a unified property
&lt;/h4&gt;

&lt;p&gt;Document-based applications often need to know whether a PDF has changed before allowing users to save, close, or submit it.&lt;/p&gt;

&lt;p&gt;The .NET MAUI PDF Viewer now provides the centralized &lt;strong&gt;IsDocumentModified&lt;/strong&gt; property to indicate whether the loaded PDF has been modified since it was loaded or last saved.&lt;/p&gt;

&lt;p&gt;The property tracks supported changes such as annotation updates, form field edits, signature changes, comments, redactions, and import actions. This removes the need to subscribe to multiple feature-specific events when checking whether a document has changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  DOCX Editor: Improve document review
&lt;/h2&gt;

&lt;p&gt;Managing PDFs is often just one requirement within a larger document solution. DOCX-based applications also need clear review tools when multiple contributors edit and comment on the same document.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web and Blazor DOCX Editors
&lt;/h3&gt;

&lt;p&gt;The DOCX Editor updates in this release focus on author visibility, tracked changes, revision types, and AI-assisted document operations across Web and Blazor platforms.&lt;/p&gt;

&lt;h4&gt;
  
  
  Highlight comments by author
&lt;/h4&gt;

&lt;p&gt;When several people review a document, identifying each person’s comments can become difficult.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/docx-editor-sdk" rel="noopener noreferrer"&gt;DOCX Editor&lt;/a&gt; now supports avatar-based colors for comments. Comment markers and pane borders use the author’s avatar color, while the corresponding commented text is highlighted with the same color when a comment is selected.&lt;/p&gt;

&lt;p&gt;This makes it easier to distinguish and track comments from multiple authors during collaborative review.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F363p3bbupi0r1z513kgo.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F363p3bbupi0r1z513kgo.webp" alt="Avatar-based comment highlighting in DOCX Editor" width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;
Avatar-based comment highlighting in DOCX Editor



&lt;h4&gt;
  
  
  Customize colors for tracked changes
&lt;/h4&gt;

&lt;p&gt;Documents with many revisions can become difficult to review when every change looks the same.&lt;/p&gt;

&lt;p&gt;The DOCX Editor now lets you specify custom colors for tracked changes. Each user’s revisions can be displayed using colors selected from a defined set, providing clearer visual separation between changes made by different authors.&lt;/p&gt;

&lt;p&gt;This can make collaborative review easier to follow in contracts, proposals, policies, and technical documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsb3kaloi6qzllgjob4ux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsb3kaloi6qzllgjob4ux.png" alt="Custom colors for tracked changes in DOCX Editor" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;
Custom colors for tracked changes in DOCX Editor



&lt;h4&gt;
  
  
  Distinguish different revision types
&lt;/h4&gt;

&lt;p&gt;Not all document changes have the same meaning. Insertions, deletions, and table-row changes may need to be identified separately during review.&lt;/p&gt;

&lt;p&gt;The DOCX Editor supports separate colors for insertion and deletion revisions and for inserted and deleted table rows. Each revision type can use a specific color or author-based coloring.&lt;/p&gt;

&lt;p&gt;This provides a clearer way to distinguish the types of edits made during collaborative document review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web DOCX Editor
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Automate DOCX tasks with AI agents
&lt;/h4&gt;

&lt;p&gt;Many document processes involve repetitive editing tasks such as inserting content, replacing text, updating formatting, managing bookmarks, protecting documents, and exporting files.&lt;/p&gt;

&lt;p&gt;With WebMCP tools, AI agents can interact with the &lt;a href="https://www.syncfusion.com/docx-editor-sdk/javascript-docx-editor" rel="noopener noreferrer"&gt;DOCX Editor&lt;/a&gt; to automate these operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content insertion&lt;/li&gt;
&lt;li&gt;Find and replace&lt;/li&gt;
&lt;li&gt;Formatting&lt;/li&gt;
&lt;li&gt;Bookmark management&lt;/li&gt;
&lt;li&gt;Document protection&lt;/li&gt;
&lt;li&gt;File exporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users can then review the changes before they are finalized.&lt;/p&gt;

&lt;p&gt;For example, an HR application could use approved templates to update employee agreements while allowing a reviewer to verify the final document before distribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spreadsheet Editor: Boost spreadsheet productivity
&lt;/h2&gt;

&lt;p&gt;Document management often extend beyond PDFs and DOCX files. Spreadsheets are equally important in applications that manage business data, reporting, budgeting, and operational processes.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk" rel="noopener noreferrer"&gt;Spreadsheet Editor&lt;/a&gt; introduces updates across Web, Blazor, and WinForms for formatting, collaboration, AI-assisted operations, validation, visualization, customization, and worksheet presentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web Spreadsheet Editor
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Create richer spreadsheet content
&lt;/h4&gt;

&lt;p&gt;A single spreadsheet cell may need to contain text with multiple formatting styles.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/javascript-spreadsheet-editor" rel="noopener noreferrer"&gt;Web Spreadsheet Editor&lt;/a&gt; now supports rich text formatting, allowing users to apply bold, italic, underline, strikethrough, font family, font size, and font color to selected portions of text.&lt;/p&gt;

&lt;p&gt;Multiple formatting styles can coexist within the same cell, along with existing subscript and superscript support.&lt;/p&gt;

&lt;h4&gt;
  
  
  Display overflowing cell content
&lt;/h4&gt;

&lt;p&gt;Long cell content can be difficult to read when it is limited by the cell boundary.&lt;/p&gt;

&lt;p&gt;The Web Spreadsheet Editor now supports text overflow, allowing content to extend beyond cell boundaries when adjacent cells are empty. The feature supports plain text, rich text, hyperlinks, and merged cells.&lt;/p&gt;

&lt;p&gt;When neighboring cells contain data, the overflowing content is automatically clipped to maintain a clean worksheet layout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdmw0m284diw49goe2u6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdmw0m284diw49goe2u6l.png" alt="Text overflow support in Spreadsheet Editor" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;
Text overflow support in Spreadsheet Editor



&lt;h4&gt;
  
  
  Collaborate on spreadsheets in real time
&lt;/h4&gt;

&lt;p&gt;When multiple users work with the same workbook, keeping everyone synchronized is essential.&lt;/p&gt;

&lt;p&gt;The Web Spreadsheet Editor now supports collaborative editing, allowing multiple users to view and edit the same spreadsheet while seeing real-time updates, user presence, remote selections, and editing indicators.&lt;/p&gt;

&lt;p&gt;The collaboration support also handles synchronization of workbook actions, late-join synchronization, missing-action recovery, and conflict resolution for concurrent spreadsheet operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcnkdixx6tkqtv13s7c9s.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcnkdixx6tkqtv13s7c9s.gif" alt="Real-time collaboration in Spreadsheet Editor" width="760" height="553"&gt;&lt;/a&gt;&lt;/p&gt;
Text overflow support in Spreadsheet Editor



&lt;h4&gt;
  
  
  Automate spreadsheet operations with AI agents
&lt;/h4&gt;

&lt;p&gt;Beyond data entry, spreadsheet processing often involves repetitive analysis, formatting, calculations, and data transformations.&lt;/p&gt;

&lt;p&gt;With WebMCP tools, AI agents can interact with the Spreadsheet Editor and automate these operations across workbooks. Developers and users can retain control through review, approval, customization, and undo support.&lt;/p&gt;

&lt;p&gt;This provides a way to automate structured spreadsheet processes while keeping AI-assisted changes under user control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blazor Spreadsheet Editor
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Improve spreadsheet data accuracy with validation
&lt;/h4&gt;

&lt;p&gt;A small data-entry mistake can affect calculations and reports across an entire spreadsheet.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/blazor-spreadsheet-editor" rel="noopener noreferrer"&gt;Blazor Spreadsheet Editor&lt;/a&gt; now supports data validation for whole numbers, decimals, dates, times, text length, lists, and custom formulas. These rules can be applied to selected ranges to help ensure that users enter valid data.&lt;/p&gt;

&lt;p&gt;For HR, finance, and operations applications, this allows data rules to be enforced directly within the spreadsheet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70w9pl0de21uvovu9gos.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70w9pl0de21uvovu9gos.gif" alt="Data validation in Blazor Spreadsheet Editor" width="799" height="526"&gt;&lt;/a&gt;&lt;/p&gt;
Data validation in Blazor Spreadsheet Editor



&lt;h4&gt;
  
  
  Find and replace data across worksheets and workbooks
&lt;/h4&gt;

&lt;p&gt;Large workbooks can make it difficult to locate and update specific values manually.&lt;/p&gt;

&lt;p&gt;The Blazor Spreadsheet Editor now supports &lt;strong&gt;find and replace&lt;/strong&gt; feature, allowing users to search within a worksheet or across an entire workbook, navigate to matching cells, and replace individual or all occurrences.&lt;/p&gt;

&lt;p&gt;The feature also supports case-sensitive, exact-match, and row- or column-based searches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcfjwnphqsg1p45m62ajs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcfjwnphqsg1p45m62ajs.gif" alt="Find and Replace in Blazor Spreadsheet Editor" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;
Find and replace in Blazor Spreadsheet Editor



&lt;h4&gt;
  
  
  Visualize spreadsheet data with charts
&lt;/h4&gt;

&lt;p&gt;Spreadsheet data is often easier to understand when it is presented visually.&lt;/p&gt;

&lt;p&gt;The Blazor Spreadsheet Editor now supports creating charts from selected data ranges to highlight trends, comparisons, and relationships. Users can create column, bar, area, pie, line, and scatter charts and customize chart elements, row and column orientation, and themes.&lt;/p&gt;

&lt;p&gt;Charts also update dynamically when the source data changes, helping users keep visualizations aligned with their underlying data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz36jcw58ezfrd3v8ldal.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz36jcw58ezfrd3v8ldal.gif" alt="Charts in Blazor Spreadsheet Editor" width="760" height="523"&gt;&lt;/a&gt;&lt;/p&gt;
Charts in Blazor Spreadsheet Editor



&lt;h4&gt;
  
  
  Customize spreadsheet behavior with expanded APIs
&lt;/h4&gt;

&lt;p&gt;Standard spreadsheet behavior may not always match an application’s requirements.&lt;/p&gt;

&lt;p&gt;The Blazor Spreadsheet Editor now provides expanded event support for spreadsheet lifecycle changes, user actions, menu interactions, and formatting operations. These events allow applications to validate or customize actions, track operation outcomes, and tailor spreadsheet behavior.&lt;/p&gt;

&lt;p&gt;This provides greater control when building interactive spreadsheet experiences for planning, operations, and reporting applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  WinForms Spreadsheet Editor
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Create a more focused spreadsheet workspace
&lt;/h4&gt;

&lt;p&gt;Not every application needs users to move between multiple worksheets.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/winforms-spreadsheet-editor" rel="noopener noreferrer"&gt;WinForms Spreadsheet Editor&lt;/a&gt; now lets you show or hide worksheet tabs. The sheet tab visibility can be controlled through the UI or API, helping reduce visual clutter when users only need to work with a specific sheet.&lt;/p&gt;

&lt;p&gt;For focused data-entry applications, this provides a cleaner workspace with more room for worksheet content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ui1dkhvvbluo0b0zckz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ui1dkhvvbluo0b0zckz.gif" alt="Controlling worksheet tabs in WinForms Spreadsheet Editor" width="760" height="390"&gt;&lt;/a&gt;&lt;/p&gt;
Controlling worksheet tabs in WinForms Spreadsheet Editor



&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: How are comments from different reviewers identified in a DOCX document?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The DOCX Editor uses author-specific avatar colors to highlight comments and related text, making feedback easier to track during reviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: What are the benefits of rich text formatting within spreadsheet cells in the Web Spreadsheet Editor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rich text formatting lets you apply bold, italic, underline, colors, and different font sizes within a single cell. This improves readability and highlights key information without using multiple cells.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: How does the Web PDF Viewer simplify navigation and document reviews in PDF files?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Web PDF Viewer supports web links, document links, and PDF comparison, enabling easier navigation and quick identification of document changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: Can different versions of PDF documents be compared in the Web PDF Viewer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Semantic PDF comparison highlights meaningful changes between documents, helping reviewers quickly identify updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5: What AI-powered document processing features are available in the .NET MAUI PDF Viewer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The .NET MAUI Smart PDF Viewer supports AI-powered form field detection, data extraction, and document understanding to help automate PDF processing with less manual effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6: How is real-time collaborative editing supported for PDF documents and spreadsheets?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Syncfusion supports simultaneous editing with real-time updates, user presence indicators, and synchronized collaboration for PDFs and spreadsheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build complete document solutions with less custom infrastructure
&lt;/h2&gt;

&lt;p&gt;Consider a contract management platform where a scanned agreement needs to become an interactive PDF form. Multiple reviewers need to collaborate on the document, legal teams need to track revisions in DOCX files, and business teams need to manage related information in spreadsheets.&lt;/p&gt;

&lt;p&gt;Supporting these requirements independently can quickly increase development effort.&lt;/p&gt;

&lt;p&gt;Syncfusion &lt;a href="https://www.syncfusion.com/forums/198591/essential-studio-2026-volume-3-main-release-v35-1-37-is-available-for-download" rel="noopener noreferrer"&gt;Essential Studio 2026 Volume 3&lt;/a&gt; brings capabilities across the PDF Viewer, DOCX Editor, and Spreadsheet Editor for collaboration, review, AI-assisted automation, document processing, data validation, and visualization.&lt;/p&gt;

&lt;p&gt;These capabilities help reduce custom development so you can focus on building the application-specific features your users need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explore what’s new in Syncfusion Essential Studio 2026 Volume 3
&lt;/h3&gt;

&lt;p&gt;The 2026 Volume 3 release adds capabilities for building richer document experiences across web, desktop, and mobile applications.&lt;/p&gt;

&lt;p&gt;Explore the complete &lt;a href="https://www.syncfusion.com/products/whatsnew" rel="noopener noreferrer"&gt;release&lt;/a&gt; to see how the latest PDF Viewer, DOCX Editor, and Spreadsheet Editor features can fit into your applications.&lt;/p&gt;

&lt;p&gt;New to Syncfusion? Start a &lt;a href="https://www.syncfusion.com/downloads" rel="noopener noreferrer"&gt;free 30-day trial&lt;/a&gt; to evaluate the latest Essential Studio capabilities in your applications.&lt;/p&gt;

&lt;p&gt;Already using Syncfusion? &lt;strong&gt;Download the latest version&lt;/strong&gt; from the &lt;a href="https://www.syncfusion.com/account/downloads" rel="noopener noreferrer"&gt;License and Downloads page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have questions or suggestions, feel free to reach out through our &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support forum&lt;/a&gt;, &lt;a href="https://support.syncfusion.com/" rel="noopener noreferrer"&gt;support portal&lt;/a&gt;, or &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback portal&lt;/a&gt;. We are always happy to help you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-ai-powered-application" rel="noopener noreferrer"&gt;Introducing Syncfusion A2UI: Build Agent-Driven Interfaces with Reusable UI Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/sneak-peek-2026-vol-3-document-sdk" rel="noopener noreferrer"&gt;Sneak Peek: 3 New Libraries Coming to Syncfusion Document SDK 2026 Volume 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-essential-studio-2026-vol-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion Essential Studio 2026 Volume 3: A2UI, New JavaScript Document Libraries, and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/automate-document-processing-net" rel="noopener noreferrer"&gt;7 Document Automation Tasks Every .NET Application Should Handle Server-Side&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>syncfusion</category>
      <category>documentprocessing</category>
      <category>docxeditor</category>
      <category>fileformats</category>
    </item>
    <item>
      <title>Introducing the .NET MAUI Interactive Viewer Control for Interactive Content</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Wed, 07 Oct 2026 11:47:35 +0000</pubDate>
      <link>https://dev.to/syncfusion/introducing-the-net-maui-interactive-viewer-control-for-interactive-content-509p</link>
      <guid>https://dev.to/syncfusion/introducing-the-net-maui-interactive-viewer-control-for-interactive-content-509p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;The new .NET MAUI Interactive Viewer control makes it easier to explore detailed visual content with built-in zooming, panning, rotation, and view reset. Learn how to add interactive viewing experiences for images, diagrams, technical drawings, floor plans, and other graphical content in .NET MAUI applications.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When visual content needs closer inspection, a single fixed view may not provide enough flexibility. Users may need to zoom in on a specific detail, pan across the content, rotate the view, or return to the starting position. Handling these interactions can add extra implementation work when an application requires a complete viewing experience.&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://www.syncfusion.com/maui-controls/maui-interactive-viewer" rel="noopener noreferrer"&gt;.NET MAUI Interactive Viewer&lt;/a&gt; control, introduced in Essential Studio&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;2026 Volume 3&lt;/a&gt;, brings these capabilities together in a single control. It provides configurable viewing interactions for applications that need users to inspect and navigate detailed visual content in .NET MAUI applications.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll explore the Interactive Viewer’s key capabilities and walk through how to integrate it into a .NET MAUI application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key features
&lt;/h2&gt;

&lt;p&gt;The .NET MAUI Interactive Viewer provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zoom and pan interactions&lt;/li&gt;
&lt;li&gt;Content rotation&lt;/li&gt;
&lt;li&gt;View reset&lt;/li&gt;
&lt;li&gt;Light and dark theme support&lt;/li&gt;
&lt;li&gt;Support for hosting any .NET MAUI view&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at how to use these capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zoom and pan content
&lt;/h2&gt;

&lt;p&gt;Detailed content is often difficult to inspect when it is restricted to its original size. Users may need to zoom into a particular area and then move around the content without leaving the viewing context.&lt;/p&gt;

&lt;p&gt;The Interactive Viewer provides zoom and pan interactions for this scenario, making it suitable for images, diagrams, floor plans, and other detailed content.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;MinimumZoomFactor&lt;/code&gt; and &lt;code&gt;MaximumZoomFactor&lt;/code&gt; properties allow developers to define the minimum and maximum zoom levels. The &lt;code&gt;IsZoomEnabled&lt;/code&gt; and &lt;code&gt;IsPanEnabled&lt;/code&gt; properties can also be used to control zooming and panning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvb4mi99a4akdj7iyt30t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvb4mi99a4akdj7iyt30t.gif" alt="Zoom and Pan in .NET MAUI Interactive Viewer" width="428" height="772"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;
Zoom and Pan in .NET MAUI Interactive Viewer

&lt;h2&gt;
  
  
  Rotate content
&lt;/h2&gt;

&lt;p&gt;Some visual content needs to be viewed from different orientations. For example, users may want to rotate an image, diagram, or technical drawing to inspect it from another orientation.&lt;/p&gt;

&lt;p&gt;The Interactive Viewer provides rotation support alongside its zoom and pan capabilities.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Rotate()&lt;/code&gt; method rotates the hosted content 90 degrees clockwise with each call, allowing you to view the content in different orientations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwibmt0a23ocap7vb53y5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwibmt0a23ocap7vb53y5.png" alt="Rotate content support in .NET MAUI Interactive Viewer" width="607" height="1024"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;
Rotate content support in .NET MAUI Interactive Viewer

&lt;h2&gt;
  
  
  Reset the view
&lt;/h2&gt;

&lt;p&gt;After zooming, panning, or rotating content, users may need to return to its original state.&lt;/p&gt;

&lt;p&gt;The Interactive Viewer provides a reset operation that restores the view by resetting the zoom level, pan position, and rotation changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7l67x2ml6gv0rua0nl3j.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7l67x2ml6gv0rua0nl3j.gif" alt="Reset support in .NET MAUI Interactive Viewer" width="760" height="1315"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;
Reset support in .NET MAUI Interactive Viewer

&lt;h2&gt;
  
  
  Support light and dark themes
&lt;/h2&gt;

&lt;p&gt;The Interactive Viewer supports light and dark application themes, so it integrates with applications that use them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnc3ttrs3v4dybnlige9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqnc3ttrs3v4dybnlige9.png" alt="Dark theme support in .NET MAUI Interactive Viewer" width="800" height="1347"&gt;&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;
Dark theme support in .NET MAUI Interactive Viewer

&lt;h2&gt;
  
  
  Host any .NET MAUI view
&lt;/h2&gt;

&lt;p&gt;The Interactive Viewer is not limited to images. It can host any .NET MAUI view, allowing developers to add interactive viewing capabilities to different types of visual content. For example, you can place a diagram or custom graphical view inside the viewer and explore it using zooming, panning, and rotation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgasgyiy4vag5aaaherwd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgasgyiy4vag5aaaherwd.jpg" alt="Content viewing support in .NET MAUI Interactive Viewer" width="800" height="1441"&gt;&lt;/a&gt;&lt;/p&gt;
Content viewing support in .NET MAUI Interactive Viewer


&lt;h2&gt;
  
  
  Common use cases
&lt;/h2&gt;

&lt;p&gt;Because the Interactive Viewer can host visual content beyond basic image viewing, you can use it in a range of scenarios.&lt;/p&gt;

&lt;p&gt;Common use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large image and photo viewers&lt;/li&gt;
&lt;li&gt;Interactive product image viewing&lt;/li&gt;
&lt;li&gt;Floor plans and diagrams&lt;/li&gt;
&lt;li&gt;Technical drawings and blueprints&lt;/li&gt;
&lt;li&gt;Medical images and diagnostic reports&lt;/li&gt;
&lt;li&gt;Monitoring applications that require detailed visual inspection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers can also host custom layouts and other .NET MAUI views when an application requires interactive handling for custom content.&lt;/p&gt;

&lt;p&gt;For complete API details and customization options, refer to the .NET MAUI Interactive Viewer &lt;a href="https://help.syncfusion.com/maui/interactive-viewer/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting started with the .NET MAUI Interactive Viewer
&lt;/h2&gt;

&lt;p&gt;Let’s add an image to the Interactive Viewer and configure its zoom range.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a project and install the package
&lt;/h3&gt;

&lt;p&gt;Start by creating a new .NET MAUI application in Visual Studio. Then, install the &lt;a href="https://www.nuget.org/packages/Syncfusion.Maui.InteractiveViewer" rel="noopener noreferrer"&gt;Syncfusion.Maui.InteractiveViewer&lt;/a&gt; package using the NuGet Package Manager to add the Interactive Viewer control to your project.&lt;/p&gt;

&lt;p&gt;You can also install the package using the .NET CLI:&lt;/p&gt;

&lt;p&gt;.NET CLI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Syncfusion.Maui.InteractiveViewer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Register Syncfusion Core
&lt;/h3&gt;

&lt;p&gt;Then, register the Syncfusion Core handler in the MauiProgram.cs file:&lt;/p&gt;

&lt;p&gt;C#&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Syncfusion.Maui.Core.Hosting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MauiProgram&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt; &lt;span class="nf"&gt;CreateMauiApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MauiApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureSyncfusionCore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// ...&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Add the namespace and control&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Import the Interactive Viewer namespace into your XAML page:&lt;/p&gt;

&lt;p&gt;XAML&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;xmlns:interactiveViewer="clr-namespace:Syncfusion.Maui.InteractiveViewer;assembly=Syncfusion.Maui.InteractiveViewer"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add the &lt;code&gt;SfInteractiveViewer&lt;/code&gt; control and place an image inside it:&lt;/p&gt;

&lt;p&gt;XAML&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;interactiveViewer:SfInteractiveViewer&lt;/span&gt;
    &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"interactiveViewer"&lt;/span&gt;
    &lt;span class="na"&gt;MinimumZoomFactor=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
    &lt;span class="na"&gt;MaximumZoomFactor=&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Image&lt;/span&gt;
        &lt;span class="na"&gt;Source=&lt;/span&gt;&lt;span class="s"&gt;"interactiveviewerimage.png"&lt;/span&gt;
        &lt;span class="na"&gt;Aspect=&lt;/span&gt;&lt;span class="s"&gt;"AspectFit"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/interactiveViewer:SfInteractiveViewer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;MinimumZoomFactor&lt;/code&gt; and &lt;code&gt;MaximumZoomFactor&lt;/code&gt; define the zoom range for the image.&lt;/p&gt;

&lt;p&gt;Run the application to interact with the image using the supported viewing operations.&lt;/p&gt;

&lt;p&gt;The Interactive Viewer can also host other .NET MAUI views when an application requires interactive handling for custom content&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub reference
&lt;/h2&gt;

&lt;p&gt;To explore the control with a working example, refer to the Interactive Viewer demo on &lt;a href="https://github.com/syncfusion/maui-demos/tree/master/MAUI/InteractiveViewer" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. For API details, configuration options, and additional examples, see the .NET MAUI Interactive Viewer &lt;a href="https://help.syncfusion.com/maui/interactive-viewer/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: When should I use the Interactive Viewer instead of implementing gesture recognizers manually?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Interactive Viewer is useful when an application requires multiple viewing interactions, such as zooming, panning, rotation, and resetting the view. These capabilities are provided through a single control, reducing the need to implement the viewing interactions separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: Can I display custom layouts and controls inside the Interactive Viewer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The Interactive Viewer can host any .NET MAUI view, not just images. This allows developers to use it with layouts, custom controls, dashboards, diagrams, and other visual content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: Can I control the zoom range?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The Interactive Viewer provides zoom-related properties that allow developers to configure the viewing experience. The &lt;code&gt;MinimumZoomFactor&lt;/code&gt; and &lt;code&gt;MaximumZoomFactor&lt;/code&gt; properties define the supported zoom range, while &lt;code&gt;ZoomFactor&lt;/code&gt; can be used to control the current zoom level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: Can I respond to zoom or pan changes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The Interactive Viewer provides the &lt;code&gt;ZoomFactorChanged&lt;/code&gt; and &lt;code&gt;ScrollChanged&lt;/code&gt; events, allowing applications to respond to changes in zoom level and pan position.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5: Can I rotate the content programmatically?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. The &lt;code&gt;Rotate()&lt;/code&gt; method rotates the hosted content 90 degrees clockwise with each call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Interactive content often requires several coordinated operations, including zooming, panning, rotation, and view reset. Implementing these behaviors independently can add gesture and transformation logic to an application.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.syncfusion.com/maui-controls/maui-interactive-viewer" rel="noopener noreferrer"&gt;.NET MAUI Interactive Viewer&lt;/a&gt; brings these interactions together in a single control and can host both images and other .NET MAUI views. With configurable zooming, rotation, reset, events, and theme support, it provides a flexible way to add interactive content viewing to .NET MAUI applications.&lt;/p&gt;

&lt;p&gt;To explore the control further, refer to the Interactive Viewer &lt;a href="https://help.syncfusion.com/maui/interactive-viewer/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and &lt;a href="https://github.com/syncfusion/maui-demos/tree/master/MAUI/InteractiveViewer" rel="noopener noreferrer"&gt;demo&lt;/a&gt;. You can explore the complete &lt;a href="https://www.syncfusion.com/products/whatsnew" rel="noopener noreferrer"&gt;What’s New&lt;/a&gt; and &lt;a href="https://help.syncfusion.com/common/essential-studio/release-notes/v35.1.37" rel="noopener noreferrer"&gt;Release Notes&lt;/a&gt; documentation to discover the features most relevant to your project.&lt;/p&gt;

&lt;p&gt;For existing Syncfusion customers, the latest version of Essential Studio is available from the &lt;a href="https://www.syncfusion.com/sales/pricing" rel="noopener noreferrer"&gt;License and Downloads&lt;/a&gt; page. New users can explore the capabilities through the 30-day free &lt;a href="https://www.syncfusion.com/downloads" rel="noopener noreferrer"&gt;trial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have questions or feedback, reach out through our &lt;a href="https://www.syncfusion.com/forums" rel="noopener noreferrer"&gt;support forums&lt;/a&gt;, &lt;a href="https://www.syncfusion.com/feedback" rel="noopener noreferrer"&gt;feedback portal&lt;/a&gt;, or &lt;a href="https://support.syncfusion.com/" rel="noopener noreferrer"&gt;support portal&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/fifth-set-open-source-maui-controls" rel="noopener noreferrer"&gt;Introducing the Fifth Set of Open-Source Syncfusion .NET MAUI Controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/blazor-spreadsheet-dotnet-maui" rel="noopener noreferrer"&gt;Build an Excel-Like Spreadsheet in .NET MAUI Using Blazor Hybrid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-ai-powered-application" rel="noopener noreferrer"&gt;Introducing Syncfusion A2UI: Build Agent-Driven Interfaces with Reusable UI Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-composer-ai-interface" rel="noopener noreferrer"&gt;Build Agent-Driven UI Designs Faster Using Syncfusion A2UI Composer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnetmaui</category>
      <category>net</category>
      <category>crossplatformdevelop</category>
      <category>interactiveviewer</category>
    </item>
    <item>
      <title>Explore the Syncfusion Showcase App Browser: Discover Application Examples</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Tue, 06 Oct 2026 12:38:02 +0000</pubDate>
      <link>https://dev.to/syncfusion/explore-the-syncfusion-showcase-app-browser-discover-application-examples-139n</link>
      <guid>https://dev.to/syncfusion/explore-the-syncfusion-showcase-app-browser-discover-application-examples-139n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;em&gt;Discover how real-world applications are built using Syncfusion UI components across web, desktop, and mobile platforms. Explore showcase examples by industry, use case, platform, or component, see how controls such as DataGrid, Charts, Scheduler, Diagram, and Document Editor work together, and access demos and source code to accelerate your next project.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Choosing the right UI component is only the beginning. The bigger question is how those components fit together when building a complete application.&lt;/p&gt;

&lt;p&gt;That’s where real application examples can make a difference. The &lt;strong&gt;Syncfusion Showcase&lt;/strong&gt; brings together 50+ examples built using Syncfusion UI components across web, desktop, and mobile platforms, covering scenarios such as sales, healthcare, business process modeling, and document management.&lt;/p&gt;

&lt;p&gt;You can explore these Syncfusion &lt;a href="https://showcase.syncfusion.com/" rel="noopener noreferrer"&gt;Showcase App&lt;/a&gt; based on the platform, component, industry, or use case that interests you. You can also run available live demos, explore application workflows, and view source code where provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than templates: Built with Syncfusion components
&lt;/h2&gt;

&lt;p&gt;Unlike traditional template galleries that primarily provide ready-made UI layouts, the Syncfusion Showcase features applications built using Syncfusion UI components.&lt;/p&gt;

&lt;p&gt;This means you can explore more than the visual design of an application. You can see how reusable components such as DataGrid, Charts, Scheduler, Kanban, Diagram, Document Editor, and PDF Viewer are combined to build complete application experiences.&lt;/p&gt;

&lt;p&gt;Each application also helps you identify the Syncfusion components used, allowing you to move from an application example to the relevant component documentation, demos, and code examples.&lt;/p&gt;

&lt;p&gt;This makes the Showcase useful not only for finding application inspiration, but also for understanding which reusable components you can use to build a similar application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore applications across platforms
&lt;/h2&gt;

&lt;p&gt;The Showcase includes applications associated with React, Angular, JavaScript, Vue, WPF, .NET MAUI, ASP.NET Core, ASP.NET MVC, and Blazor.&lt;/p&gt;

&lt;p&gt;Platform availability varies by application. Each application’s details show the platforms available for live demos and source code.&lt;/p&gt;

&lt;p&gt;For example, the Webmail showcase provides live demos and source code for Angular, JavaScript, JavaScript ES5, ASP.NET Core, and ASP.NET MVC. Other applications offer different platform combinations that you can explore individually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discover applications based on what you need
&lt;/h2&gt;

&lt;p&gt;How you explore the Showcase can depend on where you are starting.&lt;/p&gt;

&lt;p&gt;Explore by &lt;strong&gt;industry&lt;/strong&gt; for scenarios such as sales, healthcare, finance, construction, and retail. Use &lt;strong&gt;use case&lt;/strong&gt; filters to find relevant workflows, or explore by &lt;strong&gt;component&lt;/strong&gt; to see how a Syncfusion component is used across different applications.&lt;/p&gt;

&lt;p&gt;You can also filter by &lt;strong&gt;platform&lt;/strong&gt; to find examples for your preferred technology.&lt;/p&gt;

&lt;p&gt;For example, if you are building a sales application and want to see how Charts are used in that context, you can narrow the collection by the relevant industry and component.&lt;/p&gt;

&lt;p&gt;You can combine these options to narrow the collection around a specific requirement. For example, you can search for sales-related applications that use Charts and then explore the available examples for selected platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  See how components work together
&lt;/h2&gt;

&lt;p&gt;The Showcase provides a broader view of Syncfusion components by bringing multiple capabilities together within an application.&lt;/p&gt;

&lt;p&gt;Let’s take Sales CRM &amp;amp; Pipeline Intelligence as an example. It combines DataGrid, Charts, Kanban, and AI AssistView to support a sales-focused application involving accounts, opportunities, and forecasts.&lt;/p&gt;

&lt;p&gt;Appointment Planner takes a different approach, combining Scheduler, DataGrid, and Charts for a clinic scheduling scenario.&lt;/p&gt;

&lt;p&gt;For workflow visualization, BPMN Editor uses the Diagram component to design, visualize, and refine business processes.&lt;/p&gt;

&lt;p&gt;The Document Explorer brings together File Manager, Document Editor, Spreadsheet, and PDF Viewer to create a shared workspace for browsing, viewing, and editing documents.&lt;/p&gt;

&lt;p&gt;These examples show the range of application scenarios available in the Showcase and how different Syncfusion components can be used together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore the application beyond the interface
&lt;/h2&gt;

&lt;p&gt;An application example can tell you more than what is visible in its interface.&lt;/p&gt;

&lt;p&gt;Open an application’s details page to learn about its purpose, workflows, intended users, business value, and the Syncfusion components used. You can then try the available demo and explore source code where provided.&lt;/p&gt;

&lt;p&gt;The demo and source-code options are listed separately because they can differ by platform. An application may provide a live demo for one platform while offering source code for several platforms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F10%2FSales-CRM-Pipeline-Intelligence-app.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F10%2FSales-CRM-Pipeline-Intelligence-app.png" alt="Sales CRM and Pipeline Intelligence app" width="386" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;Sales CRM and Pipeline Intelligence app&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Continue exploring through component tags
&lt;/h2&gt;

&lt;p&gt;Sometimes, the first application you find leads to another question: &lt;code&gt;Where else is this component being used?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Component tags let you continue that exploration.&lt;/p&gt;

&lt;p&gt;Selecting a component such as &lt;strong&gt;Diagram&lt;/strong&gt; can lead to other showcase applications involving visual modeling. Exploring &lt;strong&gt;&lt;a href="https://showcase.syncfusion.com/?component=data-grid" rel="noopener noreferrer"&gt;DataGrid&lt;/a&gt;&lt;/strong&gt; can reveal applications that use structured data views within different workflows.&lt;/p&gt;

&lt;p&gt;Instead of stopping with one example, you can use these connections to discover related applications across the collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore a few Syncfusion Showcase apps
&lt;/h2&gt;

&lt;p&gt;The collection includes applications covering different scenarios and component combinations. Here are a few examples to explore:&lt;/p&gt;

&lt;h3&gt;
  
  
  Sales CRM and Pipeline Intelligence
&lt;/h3&gt;

&lt;p&gt;Explore a &lt;a href="https://showcase.syncfusion.com/sales-crm/" rel="noopener noreferrer"&gt;sales application&lt;/a&gt; for managing accounts, opportunities, and forecasts with DataGrid, Charts, Kanban, and AI AssistView.&lt;/p&gt;

&lt;h3&gt;
  
  
  Appointment planner
&lt;/h3&gt;

&lt;p&gt;Explore a clinic &lt;a href="https://showcase.syncfusion.com/appointment-planner/" rel="noopener noreferrer"&gt;scheduling application&lt;/a&gt; that combines Scheduler, DataGrid, and Charts for appointment planning and utilization insights.&lt;/p&gt;

&lt;h3&gt;
  
  
  BPMN Editor
&lt;/h3&gt;

&lt;p&gt;Explore &lt;a href="https://showcase.syncfusion.com/bpmn-editor/" rel="noopener noreferrer"&gt;business process modeling&lt;/a&gt; with the &lt;strong&gt;Diagram&lt;/strong&gt; component, including process design and refinement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Document Explorer
&lt;/h3&gt;

&lt;p&gt;Explore a &lt;a href="https://showcase.syncfusion.com/document-explorer/" rel="noopener noreferrer"&gt;document workspace&lt;/a&gt; that combines File Manager, Document Editor, Spreadsheet, PDF Viewer, and Image Editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: Can I Use a Showcase App as the Starting Point for a Commercial Project?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A showcase can provide implementation ideas, but access to its source code does not automatically grant every permission needed for commercial use. Review the repository’s license and the applicable Syncfusion licensing terms before reusing or distributing code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: Are Showcase Applications Ready for Production Deployment?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Treat each showcase as an example for evaluation, not as a guarantee of production readiness. Before deployment, review authentication, authorization, data validation, accessibility, error handling, performance, and dependency security based on your requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: How Should I Compare Implementations on Different Platforms?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Evaluate the same workflow across each available implementation, including behavior, accessibility, responsiveness, and integration needs. Similar application names do not guarantee feature parity, and demo availability may differ from source-code availability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4: Can I Deploy an Example to My Existing Hosting Environment?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That depends on the example’s framework, build output, backend services, and runtime requirements. Follow the repository instructions and confirm that your hosting environment supports those requirements before planning a deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5: Can I Connect an Example to My Own Backend or Database?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That depends on the design of the specific implementation. Review the application’s data layer and API contracts carefully, keep credentials on the server, apply appropriate access controls, and test changes with nonproduction data before connecting business systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6: Should I Enter Real Customer Data Into a Public Demo?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avoid entering confidential, personal, or production data into a public demo unless its data-handling terms explicitly support your use. Synthetic data and controlled environments are safer for evaluating workflows that involve sensitive information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7: Do These Examples Replace Component Documentation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Showcase applications help you understand workflow context. Component documentation remains the best source for supported APIs, configuration details, limitations, and version-specific behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8: How Can My Team Keep an Evaluation Reproducible as the Examples Change?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Record the repository URL, commit or release reference, dependency versions, and the workflows your team evaluated. A saved gallery link can preserve your discovery criteria, but it does not freeze a live demo or repository contents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore the Syncfusion Showcase
&lt;/h2&gt;

&lt;p&gt;The Syncfusion Showcase brings together application examples across platforms, industries, use cases, and components.&lt;/p&gt;

&lt;p&gt;Whether you are exploring a particular technology, looking for a component in a broader application context, or browsing examples for a specific business scenario, the collection provides different ways to discover relevant applications.&lt;/p&gt;

&lt;p&gt;Explore the Syncfusion &lt;a href="https://showcase.syncfusion.com/" rel="noopener noreferrer"&gt;Showcase&lt;/a&gt; and discover application examples built with Syncfusion UI components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Blogs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-essential-studio-2026-vol-3" rel="noopener noreferrer"&gt;What’s New in Syncfusion Essential Studio 2026 Volume 3: A2UI, New JavaScript Document Libraries, and More&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-ai-powered-application" rel="noopener noreferrer"&gt;Introducing Syncfusion A2UI: Build Agent-Driven Interfaces with Reusable UI Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/a2ui-composer-ai-interface" rel="noopener noreferrer"&gt;Design Agent-Driven UIs Faster with Syncfusion A2UI Composer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.syncfusion.com/blogs/post/fifth-set-open-source-maui-controls" rel="noopener noreferrer"&gt;Introducing the Fifth Set of Open-Source Syncfusion .NET MAUI Controls&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>syncfusion</category>
      <category>applicationexamples</category>
      <category>crossplatformdevelop</category>
      <category>developerresources</category>
    </item>
    <item>
      <title>Build Agent-Driven UI Designs Faster Using Syncfusion A2UI Composer</title>
      <dc:creator>Jollen Moyani</dc:creator>
      <pubDate>Thu, 01 Oct 2026 15:20:53 +0000</pubDate>
      <link>https://dev.to/syncfusion/build-agent-driven-ui-designs-faster-using-syncfusion-a2ui-composer-50m2</link>
      <guid>https://dev.to/syncfusion/build-agent-driven-ui-designs-faster-using-syncfusion-a2ui-composer-50m2</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Syncfusion A2UI Composer helps developers turn natural-language prompts into Syncfusion A2UI Composer helps developers turn natural-language prompts into structured A2UI definitions that can be previewed, refined, and used in application experiences.&lt;br&gt;&lt;br&gt;
Start from a prompt or template, preview the result using Syncfusion components, refine it through AI-assisted changes or direct JSON editing, and copy the resulting A2UI definition into your application. Composer handles the design-time experience, while Syncfusion A2UI Components handle runtime rendering across supported environments including React, Angular, Blazor, and .NET MAUI.&lt;/p&gt;

&lt;p&gt;AI applications often need more than text responses.&lt;/p&gt;

&lt;p&gt;A user might ask for last quarter’s sales by region, an inventory overview, a customer summary, or an appointment workflow. In these cases, the useful result may be an interactive grid, chart, form, scheduler, or combination of components rather than another block of text.&lt;/p&gt;

&lt;p&gt;Turning an interface idea into a working experience often takes several iterations. Developers need to decide which components to use, arrange the layout, review the result, and make changes before the interface is ready to use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.syncfusion.com/explore/a2ui-composer/" rel="noopener noreferrer"&gt;A2UI Composer&lt;/a&gt; provides a visual way to simplify this process. Instead of starting with UI implementation, developers can describe the experience they want using a natural-language prompt or start from a ready-made template.&lt;/p&gt;

&lt;p&gt;Composer generates a structured A2UI definition that can be previewed, refined, and edited before it is used in an application. With support for Syncfusion components, developers can explore and shape AI-powered interfaces while working with familiar UI building blocks.&lt;/p&gt;

&lt;p&gt;Composer is the design-time part of the Syncfusion &lt;a href="https://www.syncfusion.com/explore/a2ui/" rel="noopener noreferrer"&gt;A2UI&lt;/a&gt; workflow. It helps you define and refine the surface; the runtime renders that definition inside the application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two halves of the same A2UI workflow
&lt;/h2&gt;

&lt;p&gt;Syncfusion separates interface authoring from application rendering.&lt;/p&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table title=""&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Design time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Runtime&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A2UI Composer&lt;/td&gt;
&lt;td&gt;
&lt;a title="A2UI Components" href="https://www.syncfusion.com/explore/a2ui-components/" rel="noopener noreferrer"&gt;A2UI Components&lt;/a&gt; + renderer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prompt or template → preview → refine → A2UI definition&lt;/td&gt;
&lt;td&gt;Agent/backend emits A2UI messages → renderer maps them to Syncfusion UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Result&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reusable, inspectable interface definition&lt;/td&gt;
&lt;td&gt;Interactive application UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;p&gt;Composer is an optional authoring path. Teams can use it when they want to design and refine the expected interface structure first, then use that definition directly in the application or bind it to the agent workflow.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is A2UI?
&lt;/h2&gt;

&lt;p&gt;A2UI is a declarative approach for describing user interfaces as structured data instead of framework-specific UI code. A renderer interprets the definition and maps it to supported UI components.&lt;/p&gt;

&lt;p&gt;For example, an A2UI definition can describe a button, its content, and an interaction event without defining the complete framework-specific UI implementation.&lt;/p&gt;

&lt;p&gt;This separation is what allows Composer to stay focused on interface authoring rather than on one specific application framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  Meet Syncfusion A2UI Composer
&lt;/h2&gt;

&lt;p&gt;Syncfusion A2UI Composer is an AI-powered interface authoring environment that helps teams transform natural-language prompts into structured, reusable interface definitions.&lt;/p&gt;

&lt;p&gt;It is designed for prompt-to-UI and AI-assisted design workflows, helping developers move from an initial idea to an interface that can be previewed and refined.&lt;/p&gt;

&lt;p&gt;The resulting definition can then be taken into a supported application environment or used as the expected UI structure in an agent workflow&lt;/p&gt;
&lt;h2&gt;
  
  
  How A2UI Composer works
&lt;/h2&gt;

&lt;p&gt;Composer brings together prompts, visual refinement, and structured interface definitions in one workflow.&lt;/p&gt;

&lt;p&gt;A typical Composer flow is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Describe:&lt;/strong&gt; Start by describing the interface you need or selecting a starter template.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create an inventory dashboard showing stock levels, low-inventory alerts, and warehouse performance metrics.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preview:&lt;/strong&gt; See the generated interface rendered with Syncfusion components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refine:&lt;/strong&gt; Adjust the layout, components, and data through AI-assisted changes or direct JSON editing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy:&lt;/strong&gt; Take the resulting A2UI definition into your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Describe → Preview → Refine → Copy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This provides a practical path from an interface idea to a structured definition without starting with a complete application implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faqumwm58tzui1pqm897f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faqumwm58tzui1pqm897f.png" alt="Syncfusion A2UI Composer prompt-to-interface workflow" width="800" height="285"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A2UI Composer workflow&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Explore the A2UI Composer Experience
&lt;/h2&gt;

&lt;p&gt;Composer includes focused areas for generating, refining, exploring, and reviewing A2UI interfaces.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create:&lt;/strong&gt; Generate an interface from a natural-language prompt or a starter template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace:&lt;/strong&gt; Refine the interface with AI, edit A2UI JSON directly, and review the live preview in the same authoring environment.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gallery:&lt;/strong&gt; Browse reusable starter experiences and open them in Composer. Starter examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operations Dashboard&lt;/li&gt;
&lt;li&gt;CRM Overview&lt;/li&gt;
&lt;li&gt;Finance Summary&lt;/li&gt;
&lt;li&gt;Inventory Tracker&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Playground:&lt;/strong&gt; Experiment with individual components and structured definitions without creating a full interface.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Catalog:&lt;/strong&gt; Explore the Syncfusion component adapters currently supported by A2UI&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Composer Workspace
&lt;/h3&gt;

&lt;p&gt;The Workspace provides the tools needed to refine an interface after its initial generation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Assistant&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use natural-language instructions to request interface changes, such as layout updates, additional fields, styling adjustments, or supported changes to the interface. Composer applies the requested changes to the definition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON Editor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For direct control, the JSON Editor provides access to the underlying A2UI definition. Developers can inspect and edit the generated structure while reviewing the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Preview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Live Preview renders the interface as changes are made, allowing developers to review the result without switching between the definition and rendered experience.&lt;/p&gt;

&lt;p&gt;Together, these capabilities create a workflow for creating, reviewing, and refining A2UI interfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70hoxsrv0nuu72l6ik4f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F70hoxsrv0nuu72l6ik4f.png" alt="Building and refining an interface in the A2UI Composer Workspace" width="800" height="381"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Building and refining an interface in the A2UI Composer Workspace&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Build with supported Syncfusion Components
&lt;/h2&gt;

&lt;p&gt;A2UI Composer uses supported Syncfusion components as building blocks for generated interfaces.&lt;/p&gt;

&lt;p&gt;These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data and visualization:&lt;/strong&gt; Data Grid, Tree Grid, Charts, Maps, and HeatMap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input:&lt;/strong&gt; TextBox, ComboBox, and Dropdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling:&lt;/strong&gt; Scheduler, Calendar, and Gantt Chart.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation and layout:&lt;/strong&gt; Menu, Breadcrumb, Pager, Tabs, Cards, and placeholders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documents:&lt;/strong&gt; PDF Viewer, DOCX Editor, and Spreadsheet Editor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Component availability is defined by the supported A2UI catalog. Do not assume that every Syncfusion component is currently available through A2UI; check the catalog when designing an interface around a specific control.&lt;/p&gt;
&lt;h2&gt;
  
  
  From Composer to an application
&lt;/h2&gt;

&lt;p&gt;Composer focuses on creating and refining the interface definition. The resulting A2UI definition can then move into a supported application workflow.&lt;/p&gt;

&lt;p&gt;The process can be represented as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1rv67pkm72842flocbx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1rv67pkm72842flocbx.png" alt=" " width="418" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At runtime, Syncfusion A2UI Components process the structured messages, map them to supported Syncfusion components, connect the required data, and handle user interactions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use Composer definitions across supported platforms
&lt;/h2&gt;

&lt;p&gt;A2UI Composer itself is not tied to React. It produces the structured definition; the runtime platform determines how that definition is rendered.&lt;/p&gt;

&lt;p&gt;Syncfusion currently provides A2UI runtime integration for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Package&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a title="React" href="https://ej2.syncfusion.com/react/documentation/syncfusion-a2ui/supported-components" rel="noopener noreferrer"&gt;React&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;@syncfusion/ej2-react-a2ui&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a title="Angular" href="https://ej2.syncfusion.com/angular/documentation/syncfusion-a2ui/supported-components" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;@syncfusion/ej2-angular-a2ui&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a title="Blazor" href="https://blazor.syncfusion.com/documentation/syncfusion-a2ui/supported-components" rel="noopener noreferrer"&gt;Blazor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Syncfusion.Blazor.A2UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a title=".NET MAUI" href="https://help.syncfusion.com/maui/syncfusion-a2ui/getting-started" rel="noopener noreferrer"&gt;.NET MAUI&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Syncfusion.Maui.A2UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Support for more platforms is planned.&lt;/p&gt;

&lt;p&gt;This means the same Composer workflow can feed supported runtime implementations without making Composer itself framework-specific.&lt;/p&gt;
&lt;h2&gt;
  
  
  How the runtime uses the definition
&lt;/h2&gt;

&lt;p&gt;Once the A2UI definition reaches the application, the runtime takes over.&lt;/p&gt;

&lt;p&gt;Depending on the platform integration, it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate incoming A2UI messages before rendering.&lt;/li&gt;
&lt;li&gt;Map definitions to supported Syncfusion components.&lt;/li&gt;
&lt;li&gt;Connect structured data to interactive controls.&lt;/li&gt;
&lt;li&gt;Process surface updates.&lt;/li&gt;
&lt;li&gt;Route user actions back to the application or agent workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the interface definition separate from the application rendering layer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Platform integration is a separate step
&lt;/h2&gt;

&lt;p&gt;Composer stops at the structured definition. Integration into the application remains an engineering step.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React applications install and configure the React A2UI runtime.&lt;/li&gt;
&lt;li&gt;Angular applications use the Angular A2UI runtime and catalog.&lt;/li&gt;
&lt;li&gt;Blazor applications register the A2UI services and renderer.&lt;/li&gt;
&lt;li&gt;.NET MAUI applications use the corresponding MAUI A2UI package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation lets Composer remain platform-neutral while each supported runtime integrates with its own application environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  How A2UI Composer differs from traditional AI UI Builders
&lt;/h2&gt;

&lt;p&gt;Many AI-powered UI tools focus on generating application code from prompts.&lt;/p&gt;

&lt;p&gt;While this can accelerate prototyping, the generated code still needs to be reviewed, modified, tested, and maintained.&lt;/p&gt;

&lt;p&gt;A2UI Composer takes a different approach by generating structured interface definitions that can be rendered through supported A2UI runtimes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Traditional AI UI generation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Syncfusion A2UI Composer&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generates application UI code&lt;/td&gt;
&lt;td&gt;Generates structured A2UI definitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI implementation is part of the generated output&lt;/td&gt;
&lt;td&gt;Interface definition is separated from rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generated code requires review and maintenance&lt;/td&gt;
&lt;td&gt;Definitions can be refined and reused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Focuses on implementation generation&lt;/td&gt;
&lt;td&gt;Focuses on interface composition&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key difference is the focus on structured interface composition rather than generating a new UI implementation for every request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this pattern matters?
&lt;/h2&gt;

&lt;p&gt;For application teams, the value is not only faster interface creation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured:&lt;/strong&gt; A2UI definitions make the interface explicit and inspectable, giving the agent and application a clearer interface contract.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable:&lt;/strong&gt; Supported components and interface patterns can be reused across multiple agent workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent:&lt;/strong&gt; Agent-driven interfaces can use the same Syncfusion building blocks already used in the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governable:&lt;/strong&gt; The application controls the supported catalog and runtime instead of allowing an agent to generate arbitrary application markup.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  A practical A2UI experience
&lt;/h2&gt;

&lt;p&gt;Consider an inventory application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create an inventory dashboard showing stock levels, low-inventory alerts, and warehouse performance metrics.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Composer generates an initial definition and preview.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  - You can then refine it with requests such as:
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a supplier filter.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Move the low-stock items into a separate section.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The Workspace updates the structured definition, and the Live Preview lets you review the result.&lt;/li&gt;
&lt;li&gt;Once the definition is ready, copy it into a supported application workflow or use it as the UI structure expected from the agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This demonstrates the role of Composer clearly: design and refine the experience before runtime integration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkuqz9k8iixqdp7ok6qli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkuqz9k8iixqdp7ok6qli.png" alt="AI-generated flight search form in the SkyBook application" width="800" height="380"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;AI-generated flight search form in the SkyBook application&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh4ev4f4ofg54fay6n8mn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh4ev4f4ofg54fay6n8mn.png" alt="AI-generated flight results rendered in the SkyBook application" width="800" height="382"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;AI-generated flight results rendered in the SkyBook application&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnq8x7696fmoj2q7m9hvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnq8x7696fmoj2q7m9hvw.png" alt="AI-generated flight booking confirmation in the SkyBook application" width="799" height="381"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;AI-generated flight booking confirmation in the SkyBook application&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;If you want to...&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Start with...&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create an interface from a prompt&lt;/td&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start from an existing example&lt;/td&gt;
&lt;td&gt;Gallery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refine AI output and edit JSON&lt;/td&gt;
&lt;td&gt;Workspace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test individual components&lt;/td&gt;
&lt;td&gt;Playground&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check supported components&lt;/td&gt;
&lt;td&gt;Catalog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Render the resulting definition&lt;/td&gt;
&lt;td&gt;A2UI Components for your application platform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Syncfusion &lt;a href="https://www.syncfusion.com/explore/a2ui/" rel="noopener noreferrer"&gt;A2UI&lt;/a&gt; Composer provides a visual way to turn interface ideas into structured A2UI definitions.&lt;/p&gt;

&lt;p&gt;With prompts, templates, AI-assisted refinement, JSON editing, live preview, and supported Syncfusion components, developers can create and refine reusable interfaces before integrating them into application workflows.&lt;/p&gt;

&lt;p&gt;Composer remains platform-neutral at design time. The resulting A2UI definitions can then be rendered through supported Syncfusion A2UI runtimes for React, Angular, Blazor, and .NET MAUI.&lt;/p&gt;

&lt;p&gt;This separation gives teams a clear workflow: design the surface in Composer when needed, then render it through the application runtime that matches their technology stack.&lt;/p&gt;

&lt;p&gt;Try A2UI Composer&lt;/p&gt;

&lt;p&gt;Open Syncfusion &lt;a href="https://www.syncfusion.com/explore/a2ui-composer/" rel="noopener noreferrer"&gt;A2UI Composer&lt;/a&gt;, start from a prompt or template, preview the result, refine the definition, and take the resulting A2UI JSON into your application workflow.&lt;/p&gt;

&lt;p&gt;Try Syncfusion A2UI Composer and explore a structured path from an interface idea to an agent-driven application experience.&lt;/p&gt;

</description>
      <category>syncfusion</category>
      <category>ai</category>
      <category>aiuibuilder</category>
      <category>syncfusiona2uicompos</category>
    </item>
  </channel>
</rss>
