<?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: Lucy Muturi</title>
    <description>The latest articles on DEV Community by Lucy Muturi (@lucy_muturi).</description>
    <link>https://dev.to/lucy_muturi</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%2F1819420%2F87984bdb-83b5-40f8-8f70-9afbaec9a433.png</url>
      <title>DEV Community: Lucy Muturi</title>
      <link>https://dev.to/lucy_muturi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucy_muturi"/>
    <language>en</language>
    <item>
      <title>How to Convert DOCX to HTML in C# Without Breaking Formatting</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Wed, 17 Jun 2026 11:00:34 +0000</pubDate>
      <link>https://dev.to/syncfusion/how-to-convert-docx-to-html-in-c-without-breaking-formatting-14pm</link>
      <guid>https://dev.to/syncfusion/how-to-convert-docx-to-html-in-c-without-breaking-formatting-14pm</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Need to render Word documents in the browser without broken layouts? Convert DOCX files to clean, responsive HTML in C#, no Microsoft Word required. Preserve formatting, remove messy markup, and ensure consistent performance. Learn how to customize output, control styles, images, and layout with practical examples and best practices.&lt;/p&gt;

&lt;p&gt;You open a Word document, and it looks perfect. Neatly aligned text, consistent fonts, beautifully structured sections. Exactly how you designed it.&lt;/p&gt;

&lt;p&gt;Now, you upload it to your app and render it in a browser. Suddenly… things feel off.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The layout breaks in unexpected places.&lt;/li&gt;
&lt;li&gt;Styles don’t match what you saw in Word.&lt;/li&gt;
&lt;li&gt;The HTML is bloated with unnecessary markup, slowing everything down.&lt;/li&gt;
&lt;li&gt;What once looked polished now feels messy and unreliable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re building something like a &lt;strong&gt;document portal&lt;/strong&gt;, &lt;strong&gt;knowledge base&lt;/strong&gt;, or even a simple &lt;strong&gt;content preview feature&lt;/strong&gt;, this gap isn’t just annoying; it’s a real problem. It impacts performance, user trust, and the overall experience of your app.&lt;/p&gt;

&lt;p&gt;So, you start wondering… &lt;strong&gt;What if there was a better way?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What if you can &lt;strong&gt;convert Word(DOCX) files into clean, lightweight, responsive HTML&lt;/strong&gt;, something that just works in the browser without surprises?&lt;/p&gt;

&lt;p&gt;That’s exactly what we’re going to explore. In this blog, we’ll see how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convert DOCX files to HTML using the Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/document-sdk/net-word-library" rel="noopener noreferrer"&gt;.NET Word Library (DocIO)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Fine-tune the output so it looks polished, loads faster, and blends seamlessly into your app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes the conversion process easier?
&lt;/h2&gt;

&lt;p&gt;A reliable DOCX-to-HTML conversion engine should do more than simply export content. It should help developers maintain consistency between the original document and what users eventually see in the browser.&lt;/p&gt;

&lt;p&gt;Here’s where the Syncfusion .NET Word Library becomes useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preserve document formatting&lt;/strong&gt;. 
Maintain headings, lists, tables, styles, and text formatting so the HTML output closely matches the original Word document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy across modern environments&lt;/strong&gt;.
Whether you’re building apps on Windows, Linux, macOS, in containers, or in the cloud, the library works consistently without requiring Microsoft Office.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control the HTML output&lt;/strong&gt;. 
Customize how styles, images, headers, footers, and form fields are exported so the generated HTML fits your app requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce development complexity&lt;/strong&gt;. 
Instead of manually processing Open XML structures or writing custom converters, developers can handle document conversion with a few API calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started with the .NET Word Library
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a new .NET Core project
&lt;/h3&gt;

&lt;p&gt;Open &lt;a href="https://visualstudio.microsoft.com/downloads/" rel="noopener noreferrer"&gt;Visual Studio&lt;/a&gt; and select the &lt;strong&gt;ASP.NET Core&lt;/strong&gt; template. Enter your project name, choose the desired configuration, and click &lt;strong&gt;Create.&lt;/strong&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F06%2FCreate-a-new-.NET-Core-project.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%2F06%2FCreate-a-new-.NET-Core-project.png" alt="Create a new .NET Core project" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Create a new .NET Core project
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Now, you have a working foundation ready to handle document processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Install the Syncfusion .NET Word Library
&lt;/h3&gt;

&lt;p&gt;Next, install the &lt;a href="https://www.nuget.org/packages/Syncfusion.DocIO.Net.Core" rel="noopener noreferrer"&gt;Syncfusion.DocIO.Net.Core&lt;/a&gt; NuGet package.  &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%2F06%2FInstall-the-Syncfusion-NuGet-package.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%2F06%2FInstall-the-Syncfusion-NuGet-package.png" alt="Install the Syncfusion.DocIO.Net.Core NuGet package" width="799" height="444"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Install the Syncfusion.DocIO.Net.Core NuGet package
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;This package enables your application to &lt;strong&gt;read, process, and convert Word documents&lt;/strong&gt;, including converting them into HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert DOCX files to HTML in C
&lt;/h2&gt;

&lt;p&gt;The DocIO library makes it simple to convert &lt;a href="https://help.syncfusion.com/document-processing/word/conversions/html-conversions#convert-word-to-html" rel="noopener noreferrer"&gt;Word documents (DOCX) into browser-friendly HTML format&lt;/a&gt;, while keeping the structure intact.&lt;/p&gt;

&lt;p&gt;Here’s a simple example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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="n"&gt;FileStream&lt;/span&gt; &lt;span class="n"&gt;fileStreamPath&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;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Template.docx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileShare&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadWrite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//Opens an existing document from the file system through the constructor of the WordDocument class.&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WordDocument&lt;/span&gt; &lt;span class="n"&gt;document&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;WordDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileStreamPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FormatType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Docx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//Saves the docx file to MemoryStream.&lt;/span&gt;
    &lt;span class="n"&gt;MemoryStream&lt;/span&gt; &lt;span class="n"&gt;stream&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;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FormatType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;//Closes the Word document.&lt;/span&gt;
    &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&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;That’s it! Your Word document is now converted into HTML, and ready to be rendered in a browser or embedded into your app.&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%2F06%2FConverting-a-Word-document-DOCX-to-HTML-using-C.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%2F06%2FConverting-a-Word-document-DOCX-to-HTML-using-C.png" alt="Converting a Word document (DOCX) to HTML using C#" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Converting a Word document (DOCX) to HTML using C#
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some advanced Word styling (like borders or background colors) may have limited support in HTML. For edge cases, it’s worth checking the &lt;a href="https://help.syncfusion.com/document-processing/word/word-library/net/html#supported-and-unsupported-items" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the export settings in DOCX to HTML conversion
&lt;/h2&gt;

&lt;p&gt;Here’s where things get more interesting.&lt;/p&gt;

&lt;p&gt;Real-world apps rarely need a “default” conversion; you often need control. And this is exactly where DocIO shines.&lt;/p&gt;

&lt;p&gt;You can fine-tune the output to match your UI, performance goals, and content needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you can customize
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extract images&lt;/strong&gt; to a specified directory for easy management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include headers and footers&lt;/strong&gt; in the exported HTML for complete document fidelity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control editable fields&lt;/strong&gt; by treating text input fields as editable or static text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define CSS styles&lt;/strong&gt; with custom stylesheet types and names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embed images as Base64&lt;/strong&gt; for a single-file HTML output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Omit XML declaration&lt;/strong&gt; for cleaner HTML using the &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.DocIO.DLS.SaveOptions.html#Syncfusion_DocIO_DLS_SaveOptions_HtmlExportOmitXmlDeclaration" rel="noopener noreferrer"&gt;HtmlExportOmitXmlDeclaration&lt;/a&gt; property.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following code example illustrates how to customize the export settings for DOCX to HTML conversion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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="c1"&gt;//Load an existing Word document into the DocIO library instance.&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileStream&lt;/span&gt; &lt;span class="n"&gt;fileStreamPath&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;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Input.docx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileShare&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadWrite&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WordDocument&lt;/span&gt; &lt;span class="n"&gt;document&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;WordDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileStreamPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FormatType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Docx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//The header and footer in the input are exported.&lt;/span&gt;
        &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HtmlExportHeadersFooters&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="c1"&gt;//Export the text form fields as editable.&lt;/span&gt;
        &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HtmlExportTextInputFormFieldAsText&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="c1"&gt;//Set the style sheet type.&lt;/span&gt;
        &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HtmlExportCssStyleSheetType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CssStyleSheetType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;//Set value to omit XML declaration in the exported HTML file.&lt;/span&gt;
        &lt;span class="c1"&gt;//True- to omit XML declaration, otherwise false.&lt;/span&gt;
        &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HtmlExportOmitXmlDeclaration&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="c1"&gt;//Create a file stream.&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileStream&lt;/span&gt; &lt;span class="n"&gt;outputFileStream&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;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WordToHTML.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadWrite&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//Save the HTML file to the file stream.&lt;/span&gt;
            &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputFileStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FormatType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Html&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;See the following image for better visual clarity.&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%2F06%2FCustomize-the-export-settings-in-DOCX-to-HTML-conversion-2-768x396.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%2F06%2FCustomize-the-export-settings-in-DOCX-to-HTML-conversion-2-768x396.png" alt="Customizing the export settings in DOCX to HTML conversion" width="768" height="396"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Customizing the export settings in DOCX to HTML conversion
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/easily-convert-docx-to-html-in-csharp" rel="noopener noreferrer"&gt;Syncfusion Website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>word</category>
      <category>networdlibrary</category>
      <category>documentprocessing</category>
      <category>fileformats</category>
    </item>
    <item>
      <title>State Management in Blazor: Complete Guide to Building Scalable Apps</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Mon, 15 Jun 2026 11:36:05 +0000</pubDate>
      <link>https://dev.to/syncfusion/state-management-in-blazor-complete-guide-to-building-scalable-apps-2cc3</link>
      <guid>https://dev.to/syncfusion/state-management-in-blazor-complete-guide-to-building-scalable-apps-2cc3</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Struggling with lost data after refreshes, messy component communication, or unexpected state leaks in your Blazor app? The problem usually isn’t Blazor; it’s how state is managed. This guide breaks down the most effective state management patterns in Blazor, from component state and dependency injection to browser storage and persistent state, helping you build scalable, reliable apps that behave predictably across every user interaction.&lt;/p&gt;

&lt;p&gt;Your Blazor app works perfectly… until it suddenly doesn’t. Everything feels stable, until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You refresh, and your form data disappears.&lt;/li&gt;
&lt;li&gt;A small change breaks the state across components.&lt;/li&gt;
&lt;li&gt;Or worse, one user sees another user’s data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn’t rare. It’s one of the most common problems in real-world Blazor apps and it makes your app feel unreliable fast.&lt;/p&gt;

&lt;p&gt;Here’s the hard truth: &lt;strong&gt;your app isn’t broken. Your state management is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Blazor gives you a lot of flexibility, but without clear boundaries for where state should live, that flexibility quickly turns into confusion.&lt;/p&gt;

&lt;p&gt;Fix that, and everything changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more lost data on refresh,&lt;/li&gt;
&lt;li&gt;No more property drilling chaos,&lt;/li&gt;
&lt;li&gt;No more cross-user bugs, and&lt;/li&gt;
&lt;li&gt;No more guessing where the state belongs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this guide, you’ll learn the practical state management patterns in Blazor (.NET 10 / ASP.NET Core 10), with real examples, so your apps stay stable, predictable, and easy to scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is state management?
&lt;/h2&gt;

&lt;p&gt;The web is inherently stateless. Anything you don’t deliberately store is gone when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User refreshes (F5),&lt;/li&gt;
&lt;li&gt;Tab closes,&lt;/li&gt;
&lt;li&gt;User navigates away,&lt;/li&gt;
&lt;li&gt;Server circuit reconnects (Blazor Server),&lt;/li&gt;
&lt;li&gt;App switches from prerendered HTML to interactive mode (Blazor Web App).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;state management &lt;/a&gt;is simply about deciding where data should live so it persists across these events without leaking between users or making the app difficult to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does state management work in Blazor?
&lt;/h2&gt;

&lt;p&gt;Blazor gives you the same component model across three hosting approaches, but where and how long the state lives change dramatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/webassembly?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;&lt;strong&gt;Blazor WebAssembly&lt;/strong&gt;&lt;/a&gt; (Pure client-side):
&lt;/h3&gt;

&lt;p&gt;The entire .NET runtime and your app run inside the browser. Here, the state lives entirely in the browser’s memory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Values of fields and properties in component instances.&lt;/li&gt;
&lt;li&gt;The component hierarchy and its latest render output.&lt;/li&gt;
&lt;li&gt;Data is held in DI services (usually registered as singletons, since the app runs in the browser).&lt;/li&gt;
&lt;li&gt;Values set through JavaScript interop and browser storage (localStorage or sessionStorage).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/server?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;&lt;strong&gt;Blazor Server&lt;/strong&gt;&lt;/a&gt; (Server-side with SignalR):
&lt;/h3&gt;

&lt;p&gt;Your components and logic execute on the server. State is tied to a live SignalR circuit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component instances and their in-memory fields are created on the server.&lt;/li&gt;
&lt;li&gt;Scoped DI services that live for the duration of the user’s circuit.&lt;/li&gt;
&lt;li&gt;Any server-side object that survive only while the connection is active.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;&lt;strong&gt;Blazor Web App&lt;/strong&gt;&lt;/a&gt; (Unified .NET 8+ model):
&lt;/h3&gt;

&lt;p&gt;You get static server-side rendering (SSR) + optional interactivity (Server or WebAssembly per component).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State can flow seamlessly between server prerendering and client using &lt;strong&gt;PersistentComponentState&lt;/strong&gt;, with Protected Browser Storage for secure persistence and automatic hydration across render modes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In short&lt;/strong&gt;, effective state management in Blazor means selecting the right technique based on &lt;strong&gt;state lifetime, scope, and hosting model&lt;/strong&gt;, so user data remains consistent and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which state management tool should I use?
&lt;/h2&gt;

&lt;p&gt;The following are the state management types you can implement in Blazor.&lt;/p&gt;

&lt;p&gt;Use this as a first pass:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bookmarkable/shareable state:&lt;/strong&gt;  URL route/query parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI-only state inside one component:&lt;/strong&gt;  Local component fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct parent ↔ child coordination:&lt;/strong&gt; Parameters + EventCallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep subtree configuration/context:&lt;/strong&gt;  Cascading values/parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-page or unrelated components:&lt;/strong&gt;  DI service (scoped/singleton carefully).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactive shared state:&lt;/strong&gt;  State container with change notifications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prerender → interactive continuity:&lt;/strong&gt; [PersistentState] or PersistentComponentState.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure persistence in browser (Server only):&lt;/strong&gt; ProtectedLocalStorage / ProtectedSessionStorage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  URL/query string parameters
&lt;/h2&gt;

&lt;p&gt;For a state that needs to be &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/?view=aspnetcore-10.0#state-management-using-the-url" rel="noopener noreferrer"&gt;bookmarkable, shareable via URL&lt;/a&gt;, or directly controls what is displayed on a page, encoding it in the URL is a robust approach.&lt;/p&gt;

&lt;p&gt;Blazor’s routing mechanism allows you to define parameters directly in the URL path or extract them from the query string.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use cases:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Product ID:&lt;/strong&gt; /products/123&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Category filter:&lt;/strong&gt; /products?category=electronics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search terms:&lt;/strong&gt; /search?q=blazor+state&lt;/li&gt;
&lt;li&gt;Pagination state (/items?page=2&amp;amp;pageSize=10).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pros:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;State is part of the URL, making it bookmarkable and shareable.&lt;/li&gt;
&lt;li&gt;Naturally handles browser navigation (back/forward buttons).&lt;/li&gt;
&lt;li&gt;Stateless from the server’s perspective (good for SEO and caching if server-side rendered).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cons:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Limited to simple data types (strings, numbers, dates).&lt;/li&gt;
&lt;li&gt;Not suitable for large or complex objects.&lt;/li&gt;
&lt;li&gt;Can make URLs long and less readable if too many parameters are used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refer to the &lt;a href="https://github.com/SyncfusionExamples/blazor-state-management-examples/tree/master/url-query-string-parameters" rel="noopener noreferrer"&gt;GitHub example&lt;/a&gt; demonstrating URL-based state management using route parameters and query strings.&lt;/p&gt;

&lt;p&gt;Here, the &lt;strong&gt;{Category}&lt;/strong&gt; route parameter, combined with &lt;strong&gt;[SupplyParameterFromQuery(Name = “searchTerm”)]&lt;/strong&gt;, automatically binds values from both the URL path and query string into component properties. Using the &lt;strong&gt;NavManager.NavigateTo()&lt;/strong&gt; method, along with manual URI construction in &lt;strong&gt;ApplySearch()&lt;/strong&gt;, ensures that any filter change updates the browser URL.&lt;/p&gt;

&lt;p&gt;As a result, refreshing the page, bookmarking the link, or using browser navigation restores the exact filtered view without relying on additional storage, services, or state containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local component state
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/?view=aspnetcore-10.0#in-memory-state-container-service" rel="noopener noreferrer"&gt;local component state&lt;/a&gt; is the simplest form of state management. Any private field or property in a &lt;strong&gt;@code&lt;/strong&gt;  block is local state, where state is contained entirely within a single Blazor component. This is fast, zero-overhead, and perfect for UI-only concerns (toggle flags, temporary form drafts, and animation states).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use cases:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;State for UI elements like a toggle button’s &lt;strong&gt;IsActive&lt;/strong&gt; status.&lt;/li&gt;
&lt;li&gt;Temporary input values in a form that haven’t been submitted yet.&lt;/li&gt;
&lt;li&gt;Any state that is only relevant to the component itself and doesn’t need to be shared.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pros:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Extremely simple and straightforward.&lt;/li&gt;
&lt;li&gt;Highly encapsulated and isolated, reducing side effects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cons:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cannot be directly accessed or shared by other components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details, refer to the code example on the &lt;a href="https://github.com/SyncfusionExamples/blazor-state-management-examples/tree/master/local-component-state" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improvement tip:&lt;/strong&gt; If the same state needs to be shared across sibling components, this approach no longer works; that’s your cue to move to a higher-level state management pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parent-child communication (Parameters &amp;amp; EventCallbacks)
&lt;/h2&gt;

&lt;p&gt;This is the standard way to manage state flow between directly related components in a hierarchical structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/?view=aspnetcore-10.0#cascading-values-and-parameters" rel="noopener noreferrer"&gt;Parameters&lt;/a&gt; &lt;strong&gt;([Parameter]):&lt;/strong&gt; Used by a parent component to pass data to a child component.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.eventcallback-1?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;EventCallbacks&lt;/a&gt; &lt;strong&gt;([Parameter] EventCallback):&lt;/strong&gt; Used by a child component to send notifications or data to its parent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use cases:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Displaying a list of items where each item is rendered by a child component.&lt;/li&gt;
&lt;li&gt;A child component triggering an action in its parent (e.g., a “Save” button in a child form).&lt;/li&gt;
&lt;li&gt;Any scenario where components have a clear parent-child relationship and need to exchange data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pros:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clear and explicit data flow, easy to trace.&lt;/li&gt;
&lt;li&gt;Strongly typed, enhancing code safety.&lt;/li&gt;
&lt;li&gt;Supports two-way binding using Parameter and EventCallback pairs (e.g., Value and ValueChanged).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cons:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prop drilling:&lt;/strong&gt; If data needs to go through many intermediate components that don’t use it, it can lead to verbose and difficult-to-maintain code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find the code example on &lt;a href="https://github.com/SyncfusionExamples/blazor-state-management-examples/tree/master/parent-child-communication" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and see the output image below for a better understanding.&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%2F06%2FParent-and-child-components-communication-in-Blazor-state-management.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%2F06%2FParent-and-child-components-communication-in-Blazor-state-management.png" alt="Parent and child components communication in Blazor state management" width="512" height="492"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;
  &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing state down the tree: Cascading parameters and values
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-10.0#root-level-cascading-values" rel="noopener noreferrer"&gt;cascading parameters&lt;/a&gt; provide an elegant solution for passing data down a component hierarchy without the need for explicit parameter declarations at every level.&lt;/p&gt;

&lt;p&gt;A component wraps a portion of the UI and makes a value available to all components within that &lt;strong&gt;subtree&lt;/strong&gt;. The child components consume this value using &lt;strong&gt;[CascadingParameter]&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For dynamic updates, use the &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-10.0#root-level-cascading-values" rel="noopener noreferrer"&gt;CascadingValueSource&lt;/a&gt;(newer, notification-capable pattern).&lt;/p&gt;

&lt;p&gt;The following component hierarchy diagram with arrows showcases the cascading parameter flow from root to leaves, highlighting reduced prop drilling.&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%2F06%2FCascading-parameter-and-values-flowchart-768x197.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%2F06%2FCascading-parameter-and-values-flowchart-768x197.png" alt="Cascading parameter and values flowchart" width="768" height="197"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;
  &lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use cases:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Providing color schemes, fonts, or UI densities.&lt;/li&gt;
&lt;li&gt;Making the current user’s identity or permissions available.&lt;/li&gt;
&lt;li&gt;Sharing user preferences across many components.&lt;/li&gt;
&lt;li&gt;Avoiding prop drilling for common, deeply nested data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pros:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Eliminates prop drilling for deeply nested components.&lt;/li&gt;
&lt;li&gt;Cleaner component signatures as parameters aren’t explicitly listed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cons:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Less explicit data flow; it can be harder to see where a value originates from.&lt;/li&gt;
&lt;li&gt;Can be misused for non-hierarchical data, leading to less maintainable code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, check out the demo on the &lt;a href="https://github.com/SyncfusionExamples/blazor-state-management-examples/tree/master/sharing-state-down-the-tree" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and see the output below.&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%2F06%2FCascading-parameters-and-value-type-state-management-in-Blazor-output.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%2F06%2FCascading-parameters-and-value-type-state-management-in-Blazor-output.png" alt="Cascading parameters and value type state management in Blazor output" width="427" height="328"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;
  &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Global state with dependency injection services
&lt;/h2&gt;

&lt;p&gt;When a state needs to be shared across unrelated components or pages, using a &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-10.0#root-level-cascading-values" rel="noopener noreferrer"&gt;dependency injection&lt;/a&gt; service is the most practical approach. A shared service allows the state to persist for the lifetime of a user session (Blazor Server) or browser tab (Blazor WebAssembly).&lt;/p&gt;

&lt;p&gt;This is implemented by registering a plain C# class in Blazor’s dependency injection container. The service holds the shared data and typically exposes methods to update it. If needed, it can also notify components about changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Registration scopes:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Choosing the correct lifetime is critical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AddScoped():&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazor Server:&lt;/strong&gt; A new instance per user circuit/session. Ideal for per-user state (e.g., shopping cart, user preferences).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blazor WebAssembly:&lt;/strong&gt; A new instance per browser tab. Ideal for per-tab state.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AddSingleton():&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazor Server:&lt;/strong&gt; A single instance for the entire app. Use with caution, as all users will share the same instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blazor WebAssembly:&lt;/strong&gt; A single instance for the entire app running in the browser tab.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use cases:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Shopping cart accessible from product pages, header, and checkout.&lt;/li&gt;
&lt;li&gt;User profile data that is shared across various authenticated pages.&lt;/li&gt;
&lt;li&gt;Global notifications (toasts, alerts).&lt;/li&gt;
&lt;li&gt;Any complex state that doesn’t fit a parent-child relationship.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pros:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Centralized state management, keeping components lean.&lt;/li&gt;
&lt;li&gt;Decoupled components from state implementation details.&lt;/li&gt;
&lt;li&gt;Highly testable.&lt;/li&gt;
&lt;li&gt;Supports various scopes to match state longevity needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Cons:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Requires manual implementation of change notification (e.g., event, Action delegates, StateHasChanged()).&lt;/li&gt;
&lt;li&gt;Can become complex for very large apps without additional patterns (such as implementing a more structured, centralized state management solution like the  &lt;strong&gt;Flux or Redux patterns&lt;/strong&gt; using libraries like &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-10.0#root-level-cascading-values" rel="noopener noreferrer"&gt;Fluxor&lt;/a&gt;, which enforce a unidirectional data flow and make state changes more predictable and manageable).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CounterState.cs&lt;/strong&gt;&lt;br&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;CounterState&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&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;set&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="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Increment&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;Count&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;void&lt;/span&gt; &lt;span class="nf"&gt;Reset&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;Count&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The registration differs by hosting model, see the following code example for more details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Program.cs&lt;/strong&gt;&lt;br&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="c1"&gt;// Blazor WebAssembly – Program.cs&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;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Blazor Server – Program.cs (.NET 6+)&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;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;CounterState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scoped services in Blazor Server live per user circuit, while singletons in WebAssembly live for the browser tab. Using the wrong scope can lead to memory issues or shared data across users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In a Blazor Web App, when the CounterState class is added to the WebAssembly project, and you are using server-side prerendering, you need to register CounterState in the Server project as well, along with the WebAssembly project. Also, you can avoid prerendering at the server side by setting @rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false)) and &lt;strong&gt;avoid&lt;/strong&gt;  &lt;strong&gt;registering&lt;/strong&gt; the CounterState at the Server project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consumption:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ComponentUsingService.razor&lt;/strong&gt;&lt;br&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="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;CounterState&lt;/span&gt; &lt;span class="n"&gt;CounterState&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;@CounterState&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;&amp;lt;/&lt;/span&gt;&lt;span class="n"&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="n"&gt;button&lt;/span&gt; &lt;span class="n"&gt;@onclick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"() =&amp;gt; CounterState.Increment()"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;+&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&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;
  
  
  &lt;strong&gt;Reactive state containers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Plain services like the above example don’t notify consumers of changes. A lightweight container fixes that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ReactiveStateContainer.cs&lt;/strong&gt;&lt;br&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;ReactiveStateContainer&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_savedString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Property&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_savedString&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="kt"&gt;string&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;set&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_savedString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;NotifyStateChanged&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;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;OnChange&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;void&lt;/span&gt; &lt;span class="nf"&gt;NotifyStateChanged&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;OnChange&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&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;&lt;strong&gt;Component usage (important: always unsubscribe):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StateContainer.razor&lt;/strong&gt;&lt;br&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="n"&gt;@page&lt;/span&gt; &lt;span class="s"&gt;"/reactive-state-container"&lt;/span&gt;
&lt;span class="n"&gt;@rendermode&lt;/span&gt; &lt;span class="n"&gt;InteractiveWebAssembly&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
&lt;span class="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt; &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Reactive&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="n"&gt;Container&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h2&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="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;@ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&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="n"&gt;button&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;btn&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="s"&gt;" @onclick="&lt;/span&gt;&lt;span class="n"&gt;UpdateValue&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;Update from Parent&amp;lt;/button&amp;gt;
&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NestedComponent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnInitialized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnChange&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;StateHasChanged&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;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Updated at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&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="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnChange&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;StateHasChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Prevent memory leak&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;&lt;strong&gt;NestedComponent.razor:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NestedComponent.razor&lt;/strong&gt;&lt;br&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="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
&lt;span class="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt; &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;border&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="n"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;light&lt;/span&gt; &lt;span class="n"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="n"&gt;rounded&lt;/span&gt;&lt;span class="s"&gt;"
&lt;/span&gt;     &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"max-width: 580px;"&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="n"&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="n"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Nested&lt;/span&gt; &lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;Property&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;em&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;@ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;em&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="n"&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="n"&gt;button&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;btn&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="s"&gt;" @onclick="&lt;/span&gt;&lt;span class="n"&gt;UpdateFromNested&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;        &lt;span class="n"&gt;Update&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Nested&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&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="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnInitialized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Subscribe so nested component re-renders on changes&lt;/span&gt;
        &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnChange&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;StateHasChanged&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;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateFromNested&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Updated by Nested at &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;HH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;mm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ss&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="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Unsubscribe on disposal&lt;/span&gt;
        &lt;span class="n"&gt;ReactiveStateContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnChange&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;StateHasChanged&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;After executing the above code examples, the output will resemble the following image.&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%2F06%2FReactive-state-container-demo-output-768x449.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%2F06%2FReactive-state-container-demo-output-768x449.png" alt="Reactive state container demo output" width="768" height="449"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;After clicking the nested component, the property is updated with a formatted date value.&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%2F06%2FSequence-diagram-of-reactive-state-container-workflow-768x728.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%2F06%2FSequence-diagram-of-reactive-state-container-workflow-768x728.png" alt="Sequence diagram of reactive state container workflow" width="768" height="728"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt;
  &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerendered state persistence
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/prerendered-state-persistence?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;prerendered state persistence&lt;/a&gt; in Blazor means saving the UI state created during server prerendering so the client can continue seamlessly without re-running components.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;Blazor Server&lt;/strong&gt;, this prevents the UI from resetting when the SignalR connection is established, giving a smooth transition from prerendered HTML to interactive components.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;Blazor WebAssembly&lt;/strong&gt;, it lets the WASM runtime pick up the prerendered state immediately after downloading, avoiding flicker and improving perceived load performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can persist the prerendered state in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[PersistentState] attribute (Declarative)&lt;/li&gt;
&lt;li&gt;PersistentComponentState service (Manual/imperative)&lt;/li&gt;
&lt;li&gt;Custom serializer PersistentComponentStateSerializer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s explore them in detail!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management/prerendered-state-persistence?view=aspnetcore-10.0#serialize-state-for-services" rel="noopener noreferrer"&gt;[PersistentState] attribute&lt;/a&gt; (Declarative)
&lt;/h3&gt;

&lt;p&gt;Properties that are public, annotated with the [PersistentState] attribute, are serialized during prerendering. This is the simplest declarative approach and recommended for most cases.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Use cases:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Simple read-only or slowly changing data (weather forecasts, user preferences, catch lists).&lt;/li&gt;
&lt;li&gt;Avoiding UI flicker on first load / F5.&lt;/li&gt;
&lt;li&gt;Data that should survive prerender → interactive handover.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Pros:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Extremely clean (one line).&lt;/li&gt;
&lt;li&gt;Automatic registration, cleanup, and restoration.&lt;/li&gt;
&lt;li&gt;New in .NET 10 – officially recommended way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Cons:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Only works on &lt;strong&gt;public properties&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Default JSON serialization (no easy way to encrypt property data without a custom serializer).&lt;/li&gt;
&lt;li&gt;Limited control over timing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;a href="https://github.com/SyncfusionExamples/blazor-state-management-examples/blob/master/prerendered-state-persistence/Weather-PersistentStateAttribute.razor" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; example code, the &lt;strong&gt;Forecasts&lt;/strong&gt; property is declared as public and marked with the [PersistentState] attribute, which will serialize all the four properties of WeatherForecast items.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your data is mostly read‑only and doesn’t change often, you can still enable &lt;strong&gt;AllowUpdates = true&lt;/strong&gt; on the [PersistentState] attribute so that any occasional updates get stored during enhanced navigation.&lt;/li&gt;
&lt;li&gt;This is helpful for scenarios where the data is cached and expensive to retrieve, allowing the app to keep the latest snapshot without refetching or rerunning initialization logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To prevent the state from being restored when the app is prerendered, set &lt;strong&gt;RestoreBehavior&lt;/strong&gt; to &lt;strong&gt;SkipInitialValue&lt;/strong&gt;. If you want the state to refresh after a reconnection instead of using the last saved snapshot, set RestoreBehavior to SkipLastSnapshot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;br&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;PersistentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AllowUpdates&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;public&lt;/span&gt; &lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;[]?&lt;/span&gt; &lt;span class="n"&gt;Forecasts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;PersistentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RestoreBehavior&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RestoreBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SkipInitialValue&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;NoPrerenderedData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;PersistentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RestoreBehavior&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RestoreBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SkipLastSnapshot&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;CounterNotRestoredOnReconnect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;To persist private property or a field, you need to use the PersistentComponentState class manually. Let’s see how to use it.&lt;/p&gt;

&lt;p&gt;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/master-state-management-in-blazor" rel="noopener noreferrer"&gt;Syncfusion Website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>development</category>
      <category>serversiderendering</category>
      <category>statemanagement</category>
    </item>
    <item>
      <title>Free Online PDF Converters for Word, Excel, HTML &amp; Images You Should Try</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Fri, 12 Jun 2026 11:05:31 +0000</pubDate>
      <link>https://dev.to/syncfusion/free-online-pdf-converters-for-word-excel-html-images-you-should-try-3l8o</link>
      <guid>https://dev.to/syncfusion/free-online-pdf-converters-for-word-excel-html-images-you-should-try-3l8o</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Need to convert Word files, spreadsheets, webpages, or images without losing formatting? These free online PDF conversion tools help you create high-quality PDFs and image exports instantly, with accurate rendering, fast performance, and zero software setup.&lt;/p&gt;

&lt;p&gt;PDFs remain the most reliable way to share documents across platforms, but converting files without disrupting layout is still a common challenge. Fonts shift, tables misalign, and images lose clarity, especially with low-quality tools.&lt;/p&gt;

&lt;p&gt;Modern browser-based PDF converters address this with high layout fidelity and predictable output. Whether you’re working with Office files, web content, or images, the right solution ensures your documents render correctly without requiring heavy software.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll explore the most essential conversion types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Office → PDF (Word, Excel, PowerPoint)&lt;/li&gt;
&lt;li&gt;HTML → PDF (URLs, receipts, dashboards, web pages)&lt;/li&gt;
&lt;li&gt;Image → PDF (merge scans, photos, documentation)&lt;/li&gt;
&lt;li&gt;PDF → Image (JPG, PNG, WEBP exports)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All tools referenced here are free online converters powered by enterprise-grade &lt;a href="https://www.syncfusion.com/document-sdk" rel="noopener noreferrer"&gt;document processing engines&lt;/a&gt;, delivering fast, accurate, and reliable results.&lt;/p&gt;

&lt;h2&gt;
  
  
  How free online PDF conversion works
&lt;/h2&gt;

&lt;p&gt;Most tools follow a simple workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upload a file (or paste a URL for web content)&lt;/li&gt;
&lt;li&gt;Select output format or options&lt;/li&gt;
&lt;li&gt;Convert instantly&lt;/li&gt;
&lt;li&gt;Download the result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Behind the scenes, high-quality converters rely on rendering engines that accurately interpret document structure, fonts, images, and layouts, much like professional document SDKs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Office and document to PDF conversion
&lt;/h2&gt;

&lt;p&gt;Converting Office files to PDF is one of the most common use cases, especially when consistency and portability matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Word to PDF
&lt;/h3&gt;

&lt;p&gt;Ideal for resumes, reports, and business documents where formatting must remain stable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preserves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fonts, headings, and spacing&lt;/li&gt;
&lt;li&gt;Tables and embedded images&lt;/li&gt;
&lt;li&gt;Print-ready layout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turn your Word file into a perfect PDF with one smooth &lt;a href="https://www.syncfusion.com/free-pdf-tools/word-to-pdf" rel="noopener noreferrer"&gt;click&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Excel to PDF
&lt;/h3&gt;

&lt;p&gt;Spreadsheets often contain complex visuals and formatting that can easily break during conversion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preserves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Charts, dashboards, and graphs&lt;/li&gt;
&lt;li&gt;Number formats and formulas&lt;/li&gt;
&lt;li&gt;Page scaling and alignment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turn your spreadsheet into a clean, ready-to-share &lt;a href="https://www.syncfusion.com/free-pdf-tools/excel-to-pdf" rel="noopener noreferrer"&gt;PDF&lt;/a&gt; instantly!&lt;/p&gt;

&lt;h3&gt;
  
  
  PowerPoint to PDF
&lt;/h3&gt;

&lt;p&gt;Useful when sharing presentations across devices or distributing as static content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preserves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slide layouts and themes&lt;/li&gt;
&lt;li&gt;Visual elements like icons and charts&lt;/li&gt;
&lt;li&gt;High-resolution images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Transform your slides into a sleek, presentation‑ready &lt;a href="https://www.syncfusion.com/free-pdf-tools/pptx-to-pdf" rel="noopener noreferrer"&gt;PDF&lt;/a&gt; in a flash!&lt;/p&gt;

&lt;h3&gt;
  
  
  XPS to PDF
&lt;/h3&gt;

&lt;p&gt;XPS is a legacy format that’s less supported today. Converting it to PDF ensures long-term accessibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preserves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text and formatting&lt;/li&gt;
&lt;li&gt;Vector elements and annotations&lt;/li&gt;
&lt;li&gt;Overall document structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Convert your XPS file into a clean, modern &lt;a href="https://www.syncfusion.com/free-pdf-tools/xps-to-pdf" rel="noopener noreferrer"&gt;PDF&lt;/a&gt; in seconds!&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML to PDF conversion
&lt;/h3&gt;

&lt;p&gt;HTML-to-PDF is widely used for capturing dynamic or web-based content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invoices, receipts, and reports&lt;/li&gt;
&lt;li&gt;Dashboards and internal tools&lt;/li&gt;
&lt;li&gt;Articles and documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach converts a live web page into a static, printable format while preserving the layout and styling.&lt;/p&gt;

&lt;p&gt;Capture any webpage as a clean &lt;a href="https://www.syncfusion.com/free-pdf-tools/html-to-pdf" rel="noopener noreferrer"&gt;PDF&lt;/a&gt;, just paste the URL and convert!&lt;/p&gt;

&lt;h3&gt;
  
  
  Image to PDF conversion (JPG, PNG)
&lt;/h3&gt;

&lt;p&gt;Combining images into a PDF is useful for organizing visual content into a structured document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical scenarios:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scanned documents and receipts&lt;/li&gt;
&lt;li&gt;Photos or handwritten notes&lt;/li&gt;
&lt;li&gt;Multi-page image collections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key capabilities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reorder and merge images&lt;/li&gt;
&lt;li&gt;Adjust layout and orientation&lt;/li&gt;
&lt;li&gt;Maintain clarity and alignment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turn your &lt;a href="https://www.syncfusion.com/free-pdf-tools/image-to-pdf" rel="noopener noreferrer"&gt;images&lt;/a&gt;, &lt;a href="https://www.syncfusion.com/free-pdf-tools/jpg-to-pdf" rel="noopener noreferrer"&gt;JPG&lt;/a&gt;, and &lt;a href="https://www.syncfusion.com/free-pdf-tools/png-to-pdf" rel="noopener noreferrer"&gt;PNG&lt;/a&gt; into a clean, organized PDF instantly!&lt;/p&gt;

&lt;p&gt;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/free-online-pdf-converters-tools" rel="noopener noreferrer"&gt;Syncfusion Website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>documentprocessing</category>
      <category>freepdftools</category>
      <category>pdftools</category>
    </item>
    <item>
      <title>AI-Powered Natural Language Filtering in .NET MAUI DataGrid</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Thu, 11 Jun 2026 10:58:30 +0000</pubDate>
      <link>https://dev.to/syncfusion/ai-powered-natural-language-filtering-in-net-maui-datagrid-1mki</link>
      <guid>https://dev.to/syncfusion/ai-powered-natural-language-filtering-in-net-maui-datagrid-1mki</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Skip complex filters, let users simply describe what they need. With our .NET MAUI DataGrid and Azure OpenAI, queries like “customers from New York” are instantly converted into accurate filters. The result is a faster, more intuitive, zero learning curve experience built on a clean, scalable architecture for modern cross-platform apps. Ready to see how this works in practice? Let’s dive in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if filtering thousands of rows in a grid felt as simple as typing a sentence?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Traditional filtering forces users into rigid patterns: dropdowns, exact field names, and predefined conditions. Most grids still expect users to think like developers, remember column names, pick operators, and navigate layered menus just to find what they need. It’s slow, unintuitive, and often frustrating.&lt;/p&gt;

&lt;p&gt;Now imagine your users simply typing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Orders above $500,&lt;/li&gt;
&lt;li&gt;Show high-value customers, and&lt;/li&gt;
&lt;li&gt;Products under $100&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and instantly seeing exactly what they asked for.&lt;/p&gt;

&lt;p&gt;No clicks. No confusion. Just results.&lt;/p&gt;

&lt;p&gt;That’s the power of AI‑powered natural language filtering.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll show you how to bring this smart, natural-language filtering to life using Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/maui-controls/maui-datagrid" rel="noopener noreferrer"&gt;.NET MAUI DataGrid&lt;/a&gt; and Azure OpenAI, so your apps feel less like tools and more like intelligent assistants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why natural language filtering matters
&lt;/h2&gt;

&lt;p&gt;Natural language filtering transforms rigid, technical filtering into a simple, conversational experience. It lets users interact with data the way they naturally think, not the way the UI demands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster data discovery.&lt;/strong&gt;
Skip menus and get instant results by simply describing what you need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero learning curve.&lt;/strong&gt;
No need to remember column names, operators, or filter syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intuitive interaction.&lt;/strong&gt;
Users express intent in plain English, and AI handles the complexity behind the scenes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greater confidence.&lt;/strong&gt;
Explore data freely without worrying about making mistakes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;AI-powered filtering bridges the gap between human language and structured data by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding intent,&lt;/li&gt;
&lt;li&gt;Translating it into precise filter logic, and&lt;/li&gt;
&lt;li&gt;Delivering accurate results instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The result:&lt;/strong&gt; Faster insights, happier users, and a dramatically simpler, more engaging data experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Before proceeding, refer to the getting started with &lt;a href="https://help.syncfusion.com/maui/datagrid/getting-started" rel="noopener noreferrer"&gt;.NET MAUI DataGrid documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://visualstudio.microsoft.com/downloads/" rel="noopener noreferrer"&gt;Visual Studio&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://learn.microsoft.com/en-us/dotnet/maui/get-started/first-app?view=net-maui-10.0&amp;amp;tabs=vswin&amp;amp;pivots=devices-android" rel="noopener noreferrer"&gt;.NET MAUI application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://azure.microsoft.com/en-us/products/ai-services/openai-service" rel="noopener noreferrer"&gt;Azure OpenAI&lt;/a&gt; resource and a valid  &lt;strong&gt;API key&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to embed AI-powered natural-language filtering in .NET MAUI DataGrid?
&lt;/h2&gt;

&lt;p&gt;Adding AI-powered filtering to the .NET MAUI DataGrid isn’t just about calling an API, it requires a clean and scalable architecture. By using the &lt;strong&gt;MVVM pattern&lt;/strong&gt;, you can neatly separate UI, business logic, and data handling, while &lt;strong&gt;dependency injection&lt;/strong&gt; ensures your AI services are consistently available across the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create the AI filtering service
&lt;/h3&gt;

&lt;p&gt;At the core of this implementation is the &lt;strong&gt;&lt;code&gt;AIFilterService&lt;/code&gt;&lt;/strong&gt;. Its role is to convert user-friendly, natural-language queries into a structured format your DataGrid can understand and apply.&lt;/p&gt;

&lt;p&gt;For example, a query like:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Female employees with rating ≥ 8 and salary &amp;gt; 5000”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;is transformed into a compact &lt;strong&gt;JSON-based&lt;/strong&gt; filter plan.&lt;/p&gt;

&lt;p&gt;Here’s what happens behind the scenes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;schema-aware prompt&lt;/strong&gt; is sent to Azure OpenAI to ensure responses align with your data structure.&lt;/li&gt;
&lt;li&gt;The AI returns a cleaned &lt;strong&gt;JSON filter definition&lt;/strong&gt; (removing code fences and noise).&lt;/li&gt;
&lt;li&gt;The response is then &lt;strong&gt;validated and parsed&lt;/strong&gt; into strongly typed models such as &lt;strong&gt;FilterPlan&lt;/strong&gt; or &lt;strong&gt;Condition&lt;/strong&gt;, with support for nested filter groups.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If anything goes wrong, such as a missing configuration or invalid JSON, the service safely returns &lt;strong&gt;null&lt;/strong&gt;, ensuring your app remains stable.&lt;/p&gt;

&lt;p&gt;Refer to the following code example for implementation details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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;AIFilterService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IAIFilterService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Converts a natural language prompt into a JSON filter plan via Azure OpenAI.&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;FilterPlan&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreateFilterPlanAsync&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;naturalLanguagePrompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Ensure Azure OpenAI settings are present&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&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;AzureBaseService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&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;AzureBaseService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeploymentName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&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;AzureBaseService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&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="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="c1"&gt;// Clear instructions: JSON filter plans only, aligned with your grid schema&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"You convert plain English filters into strictly valid JSON filter plans for a data grid."&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;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Grid schema:\n&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SchemaText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\n\nUser query:\n&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;naturalLanguagePrompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\n\nReturn JSON only."&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="c1"&gt;// Call Azure OpenAI and get raw content&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;content&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;CallAzureAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Extract compact JSON (removes json fences, trims noise)&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ExtractJsonObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&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="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;json&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Parse JSON into a FilterPlan (parsing implemented elsewhere)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ParseFilterPlanFromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&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="c1"&gt;// Fail safe: return null on network/parse errors&lt;/span&gt;
            &lt;span class="k"&gt;return&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="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;
  
  
  Step 2: Configuring AI services
&lt;/h3&gt;

&lt;p&gt;Once your AI filtering service is ready, the next step is to make it available throughout your app. This is done during app startup by registering your services using dependency injection.&lt;/p&gt;

&lt;p&gt;By configuring your AI service and &lt;strong&gt;ViewModel&lt;/strong&gt; at this stage, you ensure they can be easily accessed wherever needed without manual instantiation or tight coupling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceCollectionExtensions&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;IServiceCollection&lt;/span&gt; &lt;span class="nf"&gt;AddAiServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&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;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAIFilterService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AIFilterService&lt;/span&gt;&lt;span class="p"&gt;&amp;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;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmployeesViewModel&lt;/span&gt;&lt;span class="p"&gt;&amp;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;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MainPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;services&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;
  
  
  Step 3: Integrating Azure OpenAI with your .NET MAUI app
&lt;/h3&gt;

&lt;p&gt;To enable natural language filtering, your app needs to connect to &lt;strong&gt;Azure OpenAI&lt;/strong&gt;, which powers the understanding and interpretation of user queries.&lt;/p&gt;

&lt;p&gt;To do so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, ensure you have access to &lt;a href="https://learn.microsoft.com/en-us/azure/ai-foundry/openai/overview" rel="noopener noreferrer"&gt;Azure OpenAI&lt;/a&gt; and create a deployment in the Azure portal.&lt;/li&gt;
&lt;li&gt;If you do not have access, please refer to the &lt;a href="https://learn.microsoft.com/en-us/azure/ai-foundry/openai/how-to/create-resource?pivots=web-portal" rel="noopener noreferrer"&gt;create and deploy Azure OpenAI service&lt;/a&gt; guide to set up a new account.&lt;/li&gt;
&lt;li&gt;Note down the deployment name, endpoint URL, and API key.&lt;/li&gt;
&lt;li&gt;Make sure you’ve installed the &lt;a href="https://www.nuget.org/packages/Azure.AI.OpenAI/1.0.0-beta.12" rel="noopener noreferrer"&gt;Azure.AI.OpenAI&lt;/a&gt; NuGet package from the &lt;a href="https://www.nuget.org/" rel="noopener noreferrer"&gt;NuGet Gallery&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In your base service class (&lt;strong&gt;&lt;code&gt;AzureBaseService&lt;/code&gt;&lt;/strong&gt;), initialize the &lt;strong&gt;&lt;code&gt;OpenAIClient&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Replace the &lt;strong&gt;&lt;code&gt;Endpoint&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;DeploymentName&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;Key&lt;/code&gt;&lt;/strong&gt; with the actual values from your Azure OpenAI resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;C#&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;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AzureBaseService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YOUR_END_POINT"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;DeploymentName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"DEPLOYMENT_NAME"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"API_KEY"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nf"&gt;AzureBaseService&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;Next, we’ll define the models that make this integration work seamlessly.&lt;/p&gt;

&lt;p&gt;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/natural-language-filtering-maui-grid" rel="noopener noreferrer"&gt;Syncfusion Website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>netmaui</category>
      <category>ai</category>
      <category>crossplatform</category>
      <category>desktop</category>
    </item>
    <item>
      <title>How to Add PDF Annotations in Angular Using a Built-In Toolbar</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Fri, 05 Jun 2026 13:30:43 +0000</pubDate>
      <link>https://dev.to/syncfusion/how-to-add-pdf-annotations-in-angular-using-a-built-in-toolbar-38ip</link>
      <guid>https://dev.to/syncfusion/how-to-add-pdf-annotations-in-angular-using-a-built-in-toolbar-38ip</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Build a review-ready Angular PDF experience with an embedded annotation toolbar, enable highlights, sticky notes, stamps, and shapes to streamline document workflows, improve collaboration, and keep feedback within your application for faster, traceable approvals.&lt;/p&gt;

&lt;p&gt;You’ve added a PDF viewer to your Angular app. Users can scroll, zoom, and search documents, a great start.&lt;/p&gt;

&lt;p&gt;But the moment someone asks, &lt;em&gt;“Can I highlight this section?”&lt;/em&gt; or &lt;em&gt;“Can I leave a comment here?”&lt;/em&gt;, the experience breaks. The viewer works, but the review process doesn’t.&lt;/p&gt;

&lt;p&gt;Most PDF viewers are built for reading, not reviewing. Real-world applications, contracts, approvals, and engineering plans require annotations, not just rendering.&lt;/p&gt;

&lt;p&gt;This is where an embedded PDF annotation experience becomes essential.&lt;/p&gt;

&lt;p&gt;In this blog, you’ll integrate a Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/pdf-viewer-sdk/angular-pdf-viewer" rel="noopener noreferrer"&gt;Angular PDF Viewer&lt;/a&gt; and enable a fully functional &lt;strong&gt;annotation toolbar&lt;/strong&gt; with support for highlights, sticky notes, stamps, and shapes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why PDF review workflows fail without in-app annotation tools
&lt;/h2&gt;

&lt;p&gt;When annotation isn’t part of your application, users are forced into fragmented workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context switching slows everything down&lt;/strong&gt;
Open PDF → download → annotate in another tool → re-upload → send feedback → track manually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No audit trail for compliance&lt;/strong&gt;
Feedback scattered across emails or chat leaves no reliable record of who reviewed what and when.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User experience breaks&lt;/strong&gt;
Users leave your product to complete tasks elsewhere and may not return.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval processes become messy&lt;/strong&gt;
Version conflicts, manual tracking, and disconnected tools introduce delays and errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A viewer alone isn’t enough. The review process needs to stay within your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syncfusion Angular PDF Viewer with built-in annotation toolbar
&lt;/h2&gt;

&lt;p&gt;Instead of building annotation functionality from scratch, you can use our &lt;a href="https://www.syncfusion.com/pdf-viewer-sdk/angular-pdf-viewer" rel="noopener noreferrer"&gt;Angular PDF Viewer&lt;/a&gt; that already includes a configurable annotation toolbar.&lt;/p&gt;

&lt;p&gt;No &lt;strong&gt;third-party overlay&lt;/strong&gt;, no custom annotation engine required.&lt;/p&gt;

&lt;p&gt;This enables you to turn a basic document viewer into a review-ready interface with minimal setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get out of the box&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configurable annotation toolbar&lt;/li&gt;
&lt;li&gt;Multiple annotation types (text markup, notes, stamps, shapes, ink)&lt;/li&gt;
&lt;li&gt;Author tracking and comments panel&lt;/li&gt;
&lt;li&gt;Import/export support (JSON/XFDF)&lt;/li&gt;
&lt;li&gt;Works across desktop and responsive layouts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick setup
&lt;/h2&gt;

&lt;p&gt;Let’s get the viewer running first: 5 steps, under 5 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install the package
&lt;/h3&gt;

&lt;p&gt;Run the command below to install our Angular PDF Viewer &lt;a href="https://www.npmjs.com/package/ng2-pdfjs-viewer" rel="noopener noreferrer"&gt;package&lt;/a&gt; in your Angular application:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm CLI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @syncfusion/ej2-angular-pdfviewer &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add required styles
&lt;/h3&gt;

&lt;p&gt;Add the following Syncfusion Material theme CSS to your style.css file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;style.css&lt;/strong&gt;&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-base/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-buttons/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-inputs/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-popups/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-lists/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-navigations/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-dropdowns/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../node_modules/@syncfusion/ej2-notifications/styles/material.css&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;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../node_modules/@syncfusion/ej2-angular-pdfviewer/styles/material.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Register required services
&lt;/h3&gt;

&lt;p&gt;Import  &lt;strong&gt;PdfViewerModule&lt;/strong&gt;  and the required AnnotationService module in your app.module.ts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.module.ts&lt;/strong&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;PdfViewerModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ToolbarService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;AnnotationService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TextSearchService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;MagnificationService&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-pdfviewer&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;NgModule&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;PdfViewerModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;ToolbarService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;AnnotationService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;TextSearchService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MagnificationService&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;
  
  
  Step 4: Add the PDF Viewer component
&lt;/h3&gt;

&lt;p&gt;Open your component template and add the &lt;strong&gt;&lt;code&gt;&amp;lt;ejs-pdfviewer&amp;gt;&lt;/code&gt;&lt;/strong&gt; tag to render the PDF Viewer component:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.component&lt;/strong&gt;&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;ejs-pdfviewer&lt;/span&gt; 
       &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pdfViewer"&lt;/span&gt; 
       &lt;span class="err"&gt;[documentPath]="'https://cdn.syncfusion.com/content/pdf/pdf-succinctly.pdf'"&lt;/span&gt; 
       &lt;span class="err"&gt;[resourceUrl]="'https://cdn.syncfusion.com/ej2/25.1.35/dist/ej2-pdfviewer-lib'"&lt;/span&gt;
       &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height:640px;display:block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ejs-pdfviewer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Run the app
&lt;/h3&gt;

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

&lt;p&gt;&lt;strong&gt;npm CLI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng serve &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F05%2FSyncfusion-Angular-PDF-Viewer.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%2F05%2FSyncfusion-Angular-PDF-Viewer.png" alt="Angular PDF Viewer" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Angular PDF Viewer
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Explore the setup &lt;a href="https://help.syncfusion.com/document-processing/pdf/pdf-viewer/angular/getting-started" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and live &lt;a href="https://document.syncfusion.com/demos/pdf-viewer/angular/#/tailwind3/pdfviewer/annotations" rel="noopener noreferrer"&gt;demo&lt;/a&gt; to see PDF annotation tools like highlights, notes, and stamps in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core PDF annotation features in Angular
&lt;/h2&gt;

&lt;p&gt;Instead of spreading features across separate workflows, annotations bring everything into one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Text markup annotations (Highlights, Underline, Strikethrough)
&lt;/h3&gt;

&lt;p&gt;Ideal for contract reviews and document validation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highlight key clauses and obligations&lt;/li&gt;
&lt;li&gt;Underline sections for discussion&lt;/li&gt;
&lt;li&gt;Strike out outdated or invalid terms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the review context within the document without relying on external tools.&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%2F05%2FAdding-Text-Markup-annotation.gif" 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%2F05%2FAdding-Text-Markup-annotation.gif" alt="Adding Text Markup annotation" width="479" height="280"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Adding Text Markup annotation
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;You can explore the text markup annotation &lt;a href="https://help.syncfusion.com/document-processing/pdf/pdf-viewer/angular/annotation/text-markup-annotation" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/angular-pdf-annotation-toolbar" rel="noopener noreferrer"&gt;Syncfusion website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>angularpdfviewer</category>
      <category>documentprocessing</category>
      <category>pdfannotation</category>
    </item>
    <item>
      <title>From Read-Only Grids to Real Spreadsheets in React</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Thu, 04 Jun 2026 12:03:02 +0000</pubDate>
      <link>https://dev.to/syncfusion/from-read-only-grids-to-real-spreadsheets-in-react-286d</link>
      <guid>https://dev.to/syncfusion/from-read-only-grids-to-real-spreadsheets-in-react-286d</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; When React apps move beyond data display, read‑only grids start breaking down. This article explores when a true Excel‑like spreadsheet editor makes sense, covering real-world use cases, editing and formula support, Excel import/export, performance at scale, and how it improves UX, productivity, and long-term maintainability for data-heavy applications.&lt;/p&gt;

&lt;p&gt;Modern React applications don’t just display data anymore. Users expect to edit values, apply formulas, format cells, analyze results, and export files, all without leaving the app. That’s where traditional read‑only data grids begin to struggle.&lt;/p&gt;

&lt;p&gt;Grids work well for displaying rows and columns, but as soon as real data work enters the picture, teams end up patching missing features, adding workarounds, or pushing users back to Excel. At that point, it’s worth asking whether a grid is still the right foundation.&lt;/p&gt;

&lt;p&gt;This article looks at when grids fall short, what defines a real spreadsheet experience, and how React apps can move to an Excel‑like &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/react-spreadsheet-editor" rel="noopener noreferrer"&gt;Spreadsheet Editor&lt;/a&gt; without rewriting the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why read-only data grids fall short for modern React apps
&lt;/h2&gt;

&lt;p&gt;Data grids are optimized for viewing and light interaction, not for data manipulation. That limitation becomes clear as soon as users need to do more than scroll and filter.&lt;/p&gt;

&lt;p&gt;Common breaking points include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No built‑in formula support for calculations or derived values&lt;/li&gt;
&lt;li&gt;Limited or no cell‑level formatting&lt;/li&gt;
&lt;li&gt;Missing Excel workflows like autofill, copy/paste ranges, or multi‑cell edits&lt;/li&gt;
&lt;li&gt;No native way to import or export real Excel files&lt;/li&gt;
&lt;li&gt;Increasing customization cost as every new requirement turns into custom code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When users repeatedly export data just to finish their work in Excel, the grid no longer helps; it slows the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a real Excel-like Spreadsheet editor requires
&lt;/h2&gt;

&lt;p&gt;A spreadsheet experience is more than editable cells. It’s a &lt;strong&gt;cohesive set of capabilities&lt;/strong&gt; that allow users to work with data naturally, the same way they already do in Excel.&lt;/p&gt;

&lt;p&gt;At a baseline, a production‑ready React spreadsheet editor needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smooth inline editing with keyboard navigation&lt;/li&gt;
&lt;li&gt;Multi‑cell selection, copy/paste, and autofill&lt;/li&gt;
&lt;li&gt;Formula support with automatic recalculation&lt;/li&gt;
&lt;li&gt;Rich formatting (fonts, borders, alignment, number formats)&lt;/li&gt;
&lt;li&gt;Reliable Excel import and export without breaking structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For data‑heavy or enterprise applications, additional capabilities matter just as much:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi‑sheet workbooks for complex datasets&lt;/li&gt;
&lt;li&gt;Charts and visualizations generated from sheet data&lt;/li&gt;
&lt;li&gt;Data validation to control inputs&lt;/li&gt;
&lt;li&gt;Sheet‑ and workbook‑level protection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When these features exist natively inside the app, users stop switching tools, and productivity improves immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syncfusion Spreadsheet Editor
&lt;/h2&gt;

&lt;p&gt;One of the first questions developers ask is whether a spreadsheet can handle large datasets. Performance becomes critical when you’re dealing with thousands of rows, formulas, and multiple sheets.&lt;/p&gt;

&lt;p&gt;The Syncfusion &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/react-spreadsheet-editor" rel="noopener noreferrer"&gt;React Spreadsheet Editor&lt;/a&gt; is exactly that, and it scales. It handles large datasets efficiently using &lt;strong&gt;virtualization&lt;/strong&gt;, keeping performance fast even with thousands of rows and columns.  For large workbooks, enable virtualization and follow the React Spreadsheet performance &lt;a href="https://help.syncfusion.com/document-processing/excel/spreadsheet/react/performance-best-practices" rel="noopener noreferrer"&gt;best practices&lt;/a&gt;, including virtual scrolling, chunked responses, and controlled formula recalculation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes the upgrade smoother:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unified API across React, Angular, Vue, Blazor, and other major frameworks, ideal for teams with mixed stacks.&lt;/li&gt;
&lt;li&gt;Consistent behavior across tech stacks, low learning curve for your team.&lt;/li&gt;
&lt;li&gt;Maintainable codebase, one component that scales as your product grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether your team works with a single framework or a mixed stack, the &lt;strong&gt;Syncfusion Spreadsheet Editor&lt;/strong&gt; is available across all major frameworks, so everyone works with the same component, the same API, and the same behavior.&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%2F06%2FReact-Spreadsheet-Editor.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%2F06%2FReact-Spreadsheet-Editor.png" alt="React Spreadsheet Editor" width="799" height="352"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;React Spreadsheet Editor
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Ready to explore everything the &lt;strong&gt;React Spreadsheet Editor&lt;/strong&gt; can do? Jump into the &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/react-spreadsheet-editor" rel="noopener noreferrer"&gt;feature tour&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Syncfusion React Spreadsheet editor delivers out of the box
&lt;/h2&gt;

&lt;p&gt;Switching from a grid to a spreadsheet isn’t just about editing; it’s about unlocking workflows that weren’t possible before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core spreadsheet features&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cell and range selection with real keyboard support&lt;/li&gt;
&lt;li&gt;Inline WYSIWYG editing&lt;/li&gt;
&lt;li&gt;Undo/redo, copy/paste, and autofill&lt;/li&gt;
&lt;li&gt;Multi‑line cell editing&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F06%2FEditing-in-React-Spreadsheet.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%2F06%2FEditing-in-React-Spreadsheet.png" alt="Editing in React Spreadsheet Editor" width="756" height="478"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Editing in React Spreadsheet Editor
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Excel-level calculation and formatting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built‑in formulas with automatic recalculation&lt;/li&gt;
&lt;li&gt;Number formats for currency, percentages, dates, and time&lt;/li&gt;
&lt;li&gt;Conditional formatting and aggregates&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F06%2FAdding-a-formula-in-React-Spreadsheet.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%2F06%2FAdding-a-formula-in-React-Spreadsheet.png" alt="Adding a formula in React Spreadsheet Editor" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Adding a formula in React Spreadsheet Editor
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data organization and analysis&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sorting and filtering&lt;/li&gt;
&lt;li&gt;Data validation&lt;/li&gt;
&lt;li&gt;Freeze panes and resize rows or columns&lt;/li&gt;
&lt;li&gt;Insert, delete, hide, or merge cells and sheets&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F06%2FApplying-data-validation-in-React-Spreadsheet.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%2F06%2FApplying-data-validation-in-React-Spreadsheet.png" alt="Applying data validation in React Spreadsheet" width="750" height="616"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Applying data validation in React Spreadsheet
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advanced spreadsheet capabilities&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Charts created directly from sheet data&lt;/li&gt;
&lt;li&gt;Images and hyperlinks&lt;/li&gt;
&lt;li&gt;Sheet and workbook protection&lt;/li&gt;
&lt;li&gt;Customizable UI and templates&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F06%2FProtect-sheet-support-in-React-Spreadsheet.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%2F06%2FProtect-sheet-support-in-React-Spreadsheet.png" alt="Protect sheet support in React Spreadsheet" width="665" height="551"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Protect sheet support in React Spreadsheet
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Global, accessible, production-ready&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Localization and RTL layouts&lt;/li&gt;
&lt;li&gt;Global number and date formats&lt;/li&gt;
&lt;li&gt;Full WAI‑ARIA compliance with keyboard navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these features come together in a single component, reducing custom development and long-term maintenance.&lt;/p&gt;

&lt;p&gt;Want to see all these spreadsheet features in action? Try the Syncfusion React Spreadsheet &lt;a href="https://document.syncfusion.com/demos/spreadsheet-editor/react/#/tailwind3/spreadsheet/default" rel="noopener noreferrer"&gt;demo.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to integrate Syncfusion Spreadsheet into a React app
&lt;/h2&gt;

&lt;p&gt;Adding our ** Spreadsheet React** component to your app is quick and straightforward. With just a few steps, you can replace your grid with a fully interactive Excel‑like editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a new React application
&lt;/h3&gt;

&lt;p&gt;You can quickly set up a React app using &lt;strong&gt;Vite&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm CLI&lt;/strong&gt;&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
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Install the Syncfusion React Spreadsheet Editor package
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;npm CLI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @syncfusion/ej2-react-spreadsheet &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/react-excel-like-spreadsheet-editor" rel="noopener noreferrer"&gt;Syncfusion website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>datagridaialternatives</category>
      <category>frontend</category>
      <category>spreadsheeteditor</category>
    </item>
    <item>
      <title>Why Nested ListView Breaks Down Faster Than You Expect in .NET MAUI</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Wed, 03 Jun 2026 15:06:30 +0000</pubDate>
      <link>https://dev.to/syncfusion/why-nested-listview-breaks-down-faster-than-you-expect-in-net-maui-2h26</link>
      <guid>https://dev.to/syncfusion/why-nested-listview-breaks-down-faster-than-you-expect-in-net-maui-2h26</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Nested ListView in .NET MAUI is often introduced as layouts grow more complex. This article explores where that pattern creates challenges and examines the alternative list structuring approaches commonly used instead.&lt;/p&gt;

&lt;p&gt;Using .NET MAUI ListView to build complex, data-heavy layouts often starts smoothly, especially when the UI structure closely mirrors the data model. The friction shows up later, usually when lists grow, layouts become interactive, and scrolling needs to stay fluid across devices.&lt;/p&gt;

&lt;p&gt;This guide walks through what actually happens inside Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/maui-controls/maui-listview" rel="noopener noreferrer"&gt;.NET MAUI ListView&lt;/a&gt; when lists are nested, why those internal behaviors lead to gesture conflicts and layout thrashing, and how common replacement patterns, such as grouping, expand/collapse rows, and composite templates, change the performance profile of a page.&lt;/p&gt;

&lt;p&gt;Along the way, it also looks at where nesting sometimes cannot be avoided, what trade-offs it introduces, and how tuning options like sizing strategy and incremental loading affect real-world scrolling behavior in Syncfusion ListView.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you should avoid nested ListView
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Competing virtualization pipelines&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each List View maintains its own recycling, measurement, and layout lifecycle. When one ListView is nested inside another, both attempt to virtualize independently.&lt;/p&gt;

&lt;p&gt;Any change in the inner list can force the outer list to remeasure, triggering cascading layout passes. As item counts grow, this directly impacts scroll smoothness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Re-entrant layout and memory pressure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Expandable inner lists, dynamic item heights, or late data loading trigger repeated layout invalidations. On mobile hardware, that translates into extra CPU work and short-lived allocations, issues that surface as stutter or frame drops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Gesture and scroll ownership conflicts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When both parent and child List View controls believe they own vertical scrolling, gesture arbitration becomes unpredictable. Users experience sticky scrolling, missed swipes, or inconsistent touch handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Accessibility and focus side effects&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keyboard navigation and accessibility tools struggle with nested scroll regions. Managing focus across multiple ListView instances often requires additional customization that offsets the original layout simplicity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing hierarchy without Nesting Syncfusion ListView
&lt;/h2&gt;

&lt;p&gt;The key architectural shift is this: hierarchy does not require nested ListView controls.&lt;/p&gt;

&lt;p&gt;With Syncfusion’s ListView features, you can preserve structure while keeping a single, predictable virtualization and scrolling pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grouping
&lt;/h3&gt;

&lt;p&gt;Grouping is the most scalable alternative to nested lists. It is an organizational pattern that buckets items into labelled sections based on a shared key.&lt;/p&gt;

&lt;p&gt;Instead of embedding ListViews, bind a single &lt;strong&gt;SfListView&lt;/strong&gt; to a flat collection and group items using a shared key (for example, &lt;code&gt;CategoryName&lt;/code&gt;). Group headers render inline, optionally as sticky headers, while all items scroll within a single virtualized surface.&lt;/p&gt;

&lt;p&gt;Why grouping works well in Syncfusion ListView:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One virtualization pipeline.&lt;/li&gt;
&lt;li&gt;Predictable measurement behavior.&lt;/li&gt;
&lt;li&gt;Clear visual hierarchy without layout recursion.&lt;/li&gt;
&lt;li&gt;Stable performance with large datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern fits catalogs, settings pages, and any screen where sectioning matters more than strict parent-child interaction.&lt;/p&gt;

&lt;p&gt;Let’s implement the grouping feature in Syncfusion .NET MAUI ListView.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Creating a data model
&lt;/h3&gt;

&lt;p&gt;Start by creating a data model to bind to the ListView. Define a Product class with Name, Price, and CategoryName (used as the grouping key) in a &lt;strong&gt;&lt;code&gt;Product.cs&lt;/code&gt;&lt;/strong&gt; file, as shown in the code example below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="kt"&gt;string&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;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;CategoryName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="kt"&gt;string&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;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsHeader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BindableObject&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="kt"&gt;string&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;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isExpanded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsExpanded&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;isExpanded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;set&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;isExpanded&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;value&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="n"&gt;isExpanded&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;OnPropertyChanged&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;public&lt;/span&gt; &lt;span class="n"&gt;ObservableCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&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;new&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;
  
  
  Step 2: Populate the model collection in the ViewModel
&lt;/h3&gt;

&lt;p&gt;Next, create a view model to supply grouped data. Build Categories and flatten the data into &lt;strong&gt;&lt;code&gt;GroupedProducts&lt;/code&gt;&lt;/strong&gt; (&lt;strong&gt;&lt;code&gt;ObservableCollection&lt;/code&gt;&lt;/strong&gt;) in &lt;strong&gt;&lt;code&gt;CatalogViewModel.cs&lt;/code&gt;&lt;/strong&gt;, assigning &lt;strong&gt;&lt;code&gt;CategoryName&lt;/code&gt;&lt;/strong&gt; to each item so grouping can be resolved at the list level, as shown in the code example below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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;CatalogViewModel&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;ObservableCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Categories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&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="n"&gt;ObservableCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GroupedProducts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&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;CatalogViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Categories&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;ObservableCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;Category&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Featured"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;// Add products here&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;flat&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;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;cat&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Categories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&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;Product&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;CategoryName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;IsHeader&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;p&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&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;Product&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;CategoryName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&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;GroupedProducts&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;ObservableCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;flat&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;
  
  
  Step 3: Binding the ViewModel and defining the item template
&lt;/h3&gt;

&lt;p&gt;Define the XAML that renders a single, grouped list. Bind Syncfusion .NET MAUI ListView to &lt;strong&gt;&lt;code&gt;GroupedProducts&lt;/code&gt;&lt;/strong&gt;, configure grouping using &lt;strong&gt;&lt;code&gt;CategoryName&lt;/code&gt;&lt;/strong&gt; through a &lt;strong&gt;&lt;code&gt;GroupDescriptor&lt;/code&gt;&lt;/strong&gt;, and provide templates for group headers and list items, including optional sticky headers, as shown in the code example below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- Grouped List View with sticky headers (details in GitHub demo) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;sfListView:SfListView&lt;/span&gt; &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding GroupedProducts}"&lt;/span&gt;
                       &lt;span class="na"&gt;IsStickyGroupHeader=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt;
                       &lt;span class="na"&gt;SelectionMode=&lt;/span&gt;&lt;span class="s"&gt;"None"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- sfListView:SfListView.DataSource with sfData:GroupDescriptor --&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- GroupHeaderTemplate: shows {Binding Key} --&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ItemTemplate: shows product fields --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/sfListView:SfListView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s a quick demo of the feature in action:&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%2F05%2FGrouping-.NET-MAUI-ListView.gif" 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%2F05%2FGrouping-.NET-MAUI-ListView.gif" alt=".NET MAUI ListView with Grouping Enabled" width="720" height="404"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;.NET MAUI ListView with Grouping Enabled
  &lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Expand/Collapse items using a single SfListView
&lt;/h3&gt;

&lt;p&gt;Expand and collapse is a disclosure pattern that works particularly well with &lt;strong&gt;Syncfusion .NET MAUI ListView&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of pushing users into nested lists or secondary views, parent rows reveal or hide related content inline. The ListView remains continuous, and users never lose scroll context.&lt;/p&gt;

&lt;p&gt;To implement this feature, bind Syncfusion .NET MAUI List View to the Categories collection through the page’s &lt;strong&gt;&lt;code&gt;BindingContext&lt;/code&gt;&lt;/strong&gt;. Then define an &lt;strong&gt;&lt;code&gt;ItemTemplate&lt;/code&gt;&lt;/strong&gt; that renders a tappable category header and conditionally displays its child items when &lt;strong&gt;&lt;code&gt;IsExpanded&lt;/code&gt;&lt;/strong&gt; is true, as shown in the code example below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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="c"&gt;&amp;lt;!-- reuse the Model and ViewModel from the Grouping section. --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Expandable rows (details in GitHub demo) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;sfListView:SfListView&lt;/span&gt; &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Categories}"&lt;/span&gt;
                       &lt;span class="na"&gt;SelectionMode=&lt;/span&gt;&lt;span class="s"&gt;"None"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ItemTemplate:
        - Header row with tap gesture or command
        - Divider line
        - Child items in a BindableLayout shown when IsExpanded --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/sfListView:SfListView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F05%2FExpandCollapse-items-in-.NET-MAUI-ListView.gif" 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%2F05%2FExpandCollapse-items-in-.NET-MAUI-ListView.gif" alt="Expand/Collapse items in .NET MAUI ListView" width="760" height="423"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Expand/Collapse items in .NET MAUI ListView
  &lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Composite item template
&lt;/h3&gt;

&lt;p&gt;Some screens are heterogeneous rather than hierarchical.&lt;/p&gt;

&lt;p&gt;Using a &lt;strong&gt;&lt;code&gt;DataTemplateSelector&lt;/code&gt;&lt;/strong&gt; with Syncfusion ListView allows you to render different row types, headers, items, and dividers—inside a single ListView instance. The control still maintains one recycling and measurement path.&lt;/p&gt;

&lt;p&gt;This pattern is ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feed-style layouts.&lt;/li&gt;
&lt;li&gt;Mixed content lists.&lt;/li&gt;
&lt;li&gt;Scenarios where row structure varies by item state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You gain flexibility without fragmenting virtualization across multiple controls.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Defining the template selector
&lt;/h4&gt;

&lt;p&gt;Create a template selector to render header and item rows differently within a single ListView. Define &lt;strong&gt;&lt;code&gt;ProductTemplateSelector&lt;/code&gt;&lt;/strong&gt; (e.g., &lt;strong&gt;&lt;code&gt;ProductTemplateSelector.cs&lt;/code&gt;&lt;/strong&gt;) that switches between &lt;strong&gt;&lt;code&gt;HeaderTemplate&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;ItemTemplate&lt;/code&gt;&lt;/strong&gt; based on the &lt;strong&gt;&lt;code&gt;IsHeader&lt;/code&gt;&lt;/strong&gt; property, as shown in the code example below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductTemplateSelector&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DataTemplateSelector&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;DataTemplate&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;HeaderTemplate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="n"&gt;DataTemplate&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ItemTemplate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;DataTemplate&lt;/span&gt; &lt;span class="nf"&gt;OnSelectTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BindableObject&lt;/span&gt; &lt;span class="n"&gt;container&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;item&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsHeader&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;HeaderTemplate&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;ItemTemplate&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;DataTemplate&lt;/span&gt;&lt;span class="p"&gt;(()&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;ContentView&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;ItemTemplate&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;HeaderTemplate&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;DataTemplate&lt;/span&gt;&lt;span class="p"&gt;(()&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;ContentView&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;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/nested-listview-dotnet-maui" rel="noopener noreferrer"&gt;Syncfusion website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>netmaui</category>
      <category>listview</category>
      <category>mobileuidesign</category>
      <category>uiperformance</category>
    </item>
    <item>
      <title>The Evolution of Syncfusion Agentic UI Builder with Agent Skills</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Mon, 01 Jun 2026 15:27:21 +0000</pubDate>
      <link>https://dev.to/syncfusion/the-evolution-of-syncfusion-agentic-ui-builder-with-agent-skills-5g8f</link>
      <guid>https://dev.to/syncfusion/the-evolution-of-syncfusion-agentic-ui-builder-with-agent-skills-5g8f</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; The latest Syncfusion Agentic UI Builder introduces embedded Syncfusion Agent Skills that help AI generate more organized UIs within the project. By grounding generation on the available Syncfusion component intelligence, developers can reduce post-generation cleanup and accelerate UI realization.&lt;/p&gt;

&lt;p&gt;AI-assisted development has rapidly changed how developers build application UI. Modern coding assistants can generate layouts, dashboards, forms, and data experiences in seconds using natural-language prompts.&lt;/p&gt;

&lt;p&gt;But as AI-generated UI becomes part of real-world enterprise processes, developers increasingly expect more than isolated snippets or visual scaffolding. They need generated UIs that are structured, framework-aware, responsive, maintainable, and ready for integration into actual applications.&lt;/p&gt;

&lt;p&gt;That is the direction driving the latest evolution of Syncfusion&lt;sup&gt;® &lt;/sup&gt;&lt;a href="https://www.syncfusion.com/explore/agentic-ui-builder" rel="noopener noreferrer"&gt;Agentic UI Builder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The newest release introduces embedded Syncfusion &lt;a href="https://www.syncfusion.com/explore/agent-skills/" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt;, a project-aware architecture designed to help AI generate more uniform, enterprise-focused interfaces using locally available Syncfusion component intelligence.&lt;/p&gt;

&lt;p&gt;This evolution builds on the earlier orchestration capabilities of the UI Builder experience and enhances how AI understands, configures, and composes Syncfusion-powered app interfaces directly inside the developer pipeline.&lt;/p&gt;

&lt;p&gt;The result is a more streamlined path from prompt to usable UI with improved consistency, reduced setup effort, and a smoother refinement experience for development teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why enterprise UI generation requires more than snippets
&lt;/h2&gt;

&lt;p&gt;While AI tools can quickly generate UI fragments, enterprise applications demand a much higher level of completeness and reliability.&lt;/p&gt;

&lt;p&gt;The challenge is not generating UI, it’s making it work correctly within a real app context.&lt;/p&gt;

&lt;p&gt;Enterprise interfaces must handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex layouts that adapt across devices,&lt;/li&gt;
&lt;li&gt;Accurate and complete feature configuration,&lt;/li&gt;
&lt;li&gt;Cohesive integration with app architecture,&lt;/li&gt;
&lt;li&gt;Accessibility and compliance requirements, and&lt;/li&gt;
&lt;li&gt;Long-term maintainability as the app evolves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, developers often spend significant time bridging the gap between generated output and production-ready UI. This includes fixing incomplete configurations, aligning layouts, wiring data interactions, and ensuring consistency across the application.&lt;/p&gt;

&lt;p&gt;For example, a generated dashboard may include visual elements like charts or grids, but still require additional work to ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components are fully configured and functional,&lt;/li&gt;
&lt;li&gt;Layout behavior remains consistent across screen sizes,&lt;/li&gt;
&lt;li&gt;Features are properly integrated with the app, and&lt;/li&gt;
&lt;li&gt;UI patterns align with project standards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gap between generated snippets and usable app interfaces is where most development effort is still spent and where improvements in AI-assisted UI generation make the biggest impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing embedded Syncfusion Agent Skills
&lt;/h2&gt;

&lt;p&gt;The latest Syncfusion UI Builder introduces embedded Syncfusion &lt;a href="https://www.syncfusion.com/explore/agent-skills/" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; that provide locally available implementation guidance within the project environment.&lt;/p&gt;

&lt;p&gt;Think of these skills as verified instruction packs that help AI understand how Syncfusion components should be configured and organized inside real applications.&lt;/p&gt;

&lt;p&gt;Each skill can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component APIs,&lt;/li&gt;
&lt;li&gt;Setup guidance,&lt;/li&gt;
&lt;li&gt;Required imports,&lt;/li&gt;
&lt;li&gt;Feature configuration references,&lt;/li&gt;
&lt;li&gt;Framework-specific patterns,&lt;/li&gt;
&lt;li&gt;Accessibility recommendations, and&lt;/li&gt;
&lt;li&gt;Layout composition guidance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When UI Builder generates interfaces containing components such as DataGrid, Charts, Scheduler, Navigation Drawer, or Forms, the relevant Syncfusion Component Skills are automatically applied during generation.&lt;/p&gt;

&lt;p&gt;This helps developers receive more cohesive UI scaffolding with minimized rework cycles.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Syncfusion Agentic UI Builder has evolved
&lt;/h2&gt;

&lt;p&gt;Earlier versions of Syncfusion UI Builder focused heavily on runtime coordination to help AI understand component behavior during generation.&lt;/p&gt;

&lt;p&gt;That coordination model helped establish the foundation for AI-assisted Syncfusion UI generation.&lt;/p&gt;

&lt;p&gt;As adoption expanded, teams increasingly looked for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More project-aware generation,&lt;/li&gt;
&lt;li&gt;Stronger customization capabilities,&lt;/li&gt;
&lt;li&gt;Deeper alignment with internal standards, and&lt;/li&gt;
&lt;li&gt;More streamlined local development experiences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The addition of Agent Skills further evolves the architecture by bringing implementation guidance directly into the project environment.&lt;/p&gt;

&lt;p&gt;This enables generation that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More grounded in the project context,&lt;/li&gt;
&lt;li&gt;Easier to customize, and&lt;/li&gt;
&lt;li&gt;Better aligned with production-scale applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than changing the developer experience conceptually, the updated architecture enhances how UI Builder supports AI-assisted UI generation at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture comparison
&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;Aspect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Earlier Runtime Retrieval Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Skill-Based UI Builder&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Component intelligence source&lt;/td&gt;
&lt;td&gt;External services&lt;/td&gt;
&lt;td&gt;Local embedded skills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API resolution&lt;/td&gt;
&lt;td&gt;Runtime fetch&lt;/td&gt;
&lt;td&gt;Local skill grounding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise compatibility&lt;/td&gt;
&lt;td&gt;Environment dependent&lt;/td&gt;
&lt;td&gt;Improved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging effort&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Reduced&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F06%2FMCP-based-architecture.webp" 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%2F06%2FMCP-based-architecture.webp" alt="MCP-based architecture" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;MCP-based architecture
  &lt;p&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F06%2FEmbedded-Agent-Skills-based-architecture.webp" 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%2F06%2FEmbedded-Agent-Skills-based-architecture.webp" alt="Embedded Agent Skills-based architecture" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Embedded Agent Skills-based architecture
  &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the new architecture works
&lt;/h2&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%2F06%2FHigh-level-flow-in-Agentic-UI-Builder.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%2F06%2FHigh-level-flow-in-Agentic-UI-Builder.png" alt="High-level flow of Agentic UI Builder architecture" width="800" height="1202"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;High-level flow of Agentic UI Builder architecture
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;This helps improve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generation uniformity,&lt;/li&gt;
&lt;li&gt;Adaptability across devices,&lt;/li&gt;
&lt;li&gt;Reliability,&lt;/li&gt;
&lt;li&gt;Customization flexibility, and&lt;/li&gt;
&lt;li&gt;Enterprise readiness.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What developers can build faster?
&lt;/h2&gt;

&lt;p&gt;With the evolved architecture, Syncfusion UI Builder can scaffold more complete app UIs with significantly less manual setup.&lt;/p&gt;

&lt;p&gt;You can quickly generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SaaS admin dashboards,&lt;/li&gt;
&lt;li&gt;Analytics workspaces,&lt;/li&gt;
&lt;li&gt;Reporting systems,&lt;/li&gt;
&lt;li&gt;Approval workflows,&lt;/li&gt;
&lt;li&gt;CRUD management portals,&lt;/li&gt;
&lt;li&gt;Responsive forms, and&lt;/li&gt;
&lt;li&gt;Data-heavy business applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of manually assembling layouts, charts, grids, filters, themes, and navigation patterns, teams can generate production-ready UI foundations from natural-language prompts.&lt;/p&gt;

&lt;p&gt;This allows developers to spend more time building product functionality and less time assembling repetitive UI infrastructure.&lt;/p&gt;

&lt;p&gt;Read the full blog post on the &lt;a href="https://www.syncfusion.com/blogs/post/syncfusion-ui-builder-with-agent-skills" rel="noopener noreferrer"&gt;Syncfusion website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>web</category>
      <category>ai</category>
      <category>aiagenttools</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Angular Spreadsheet Freezing on Large Excel Imports? Here’s the Fix</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Fri, 17 Apr 2026 12:10:31 +0000</pubDate>
      <link>https://dev.to/syncfusion/angular-spreadsheet-freezing-on-large-excel-imports-heres-the-fix-2904</link>
      <guid>https://dev.to/syncfusion/angular-spreadsheet-freezing-on-large-excel-imports-heres-the-fix-2904</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Large Excel imports in Angular Spreadsheet freeze the UI because synchronous XLSX parsing (styles, formats, objects) blocks the main thread. Optimize imports by disabling style/format parsing, enforcing server-side cell and file size thresholds, and using openFromJson with selective deserialization for predictable performance and lower memory usage.&lt;/p&gt;

&lt;p&gt;Have you ever uploaded an Excel file and watched your web app freeze instantly? It happens more often when users try to import Excel files in Angular. A large workbook can easily slow down the browser, trigger memory spikes, or get stuck while reading the file, making the entire page feel unresponsive.&lt;/p&gt;

&lt;p&gt;Most Angular apps freeze during large Excel imports because the browser tries to parse every cell, style, formula, and object on the main thread. This leads to long pauses, high memory usage, and unpredictable “page unresponsive” errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/angular-spreadsheet-editor" rel="noopener noreferrer"&gt;Syncfusion&lt;sup&gt;®&lt;/sup&gt; Angular Spreadsheet Editor&lt;/a&gt; avoids these issues by loading only what’s necessary, enforcing file size and data limits, and allowing large workbooks to open quickly via lightweight JSON loading rather than full XLSX parsing.&lt;/p&gt;

&lt;p&gt;In this blog, you’ll learn three specific techniques to fix this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skip heavy formatting using &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.WorkbookParseOptions.html#Syncfusion_EJ2_Spreadsheet_WorkbookParseOptions_IgnoreStyle" rel="noopener noreferrer"&gt;IgnoreStyle &lt;/a&gt;and &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.WorkbookParseOptions.html#Syncfusion_EJ2_Spreadsheet_WorkbookParseOptions_IgnoreFormat" rel="noopener noreferrer"&gt;IgnoreFormat&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Prevent server overload using &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.ThresholdLimit.html#Syncfusion_EJ2_Spreadsheet_ThresholdLimit_MaximumDataLimit" rel="noopener noreferrer"&gt;MaximumDataLimit &lt;/a&gt;and &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.ThresholdLimit.html#Syncfusion_EJ2_Spreadsheet_ThresholdLimit_MaximumFileSize" rel="noopener noreferrer"&gt;MaximumFileSize&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Load large workbooks faster using &lt;a href="https://ej2.syncfusion.com/angular/documentation/api/spreadsheet/index-default#openfromjson" rel="noopener noreferrer"&gt;openFromJson&lt;/a&gt; with selective deserialization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive deeper into these techniques that make large Excel imports fast and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why large Excel imports silently break web apps
&lt;/h2&gt;

&lt;p&gt;Large Excel files don’t just “take longer.” They often trigger a chain reaction that feels like a crash:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The page freezes&lt;/strong&gt; because parsing blocks the UI thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory jumps&lt;/strong&gt;  due to styles, formats, images, and workbook metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Imports time out&lt;/strong&gt; on slower machines or remote environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users get no actionable error&lt;/strong&gt;, just a stuck screen or killed tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially common in enterprise scenarios where “normal” spreadsheets contain hundreds of thousands of cells plus formatting rules, validations, and embedded objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Syncfusion Angular Spreadsheet eliminates large file import failures
&lt;/h2&gt;

&lt;p&gt;Many apps fail because they load everything at once. &lt;a href="https://www.syncfusion.com/spreadsheet-editor-sdk/angular-spreadsheet-editor" rel="noopener noreferrer"&gt;Syncfusion Angular Spreadsheet Editor&lt;/a&gt; is designed to avoid that pattern by letting you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load only what you need (data vs. styling/features)&lt;/li&gt;
&lt;li&gt;Enforce thresholds before a file overwhelms the system&lt;/li&gt;
&lt;li&gt;Open workbooks from lightweight JSON for faster startup&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F04%2FInventory-data-with-formulas-and-conditional-formatting-in-Angular-Spreadsheet-1.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%2F04%2FInventory-data-with-formulas-and-conditional-formatting-in-Angular-Spreadsheet-1.png" alt="Angular Spreadsheet with Styles and Formatting" width="779" height="343"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Angular Spreadsheet with Styles and Formatting
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Curious to see the Angular Spreadsheet in action?&lt;/strong&gt; Explore the live &lt;a href="https://document.syncfusion.com/demos/spreadsheet-editor/angular/#/tailwind3/spreadsheet/default" rel="noopener noreferrer"&gt;demo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsing options: Load only what you need
&lt;/h3&gt;

&lt;p&gt;Most Excel imports fail not because of data volume alone, but because of &lt;strong&gt;formatting overhead&lt;/strong&gt;. Styles, number formats, and empty-cell metadata dramatically increase parsing cost, even when your app only needs raw values.&lt;/p&gt;

&lt;p&gt;Syncfusion solves this with &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.WorkbookParseOptions.html" rel="noopener noreferrer"&gt;WorkbookParseOptions&lt;/a&gt;. By enabling &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.WorkbookParseOptions.html#Syncfusion_EJ2_Spreadsheet_WorkbookParseOptions_IgnoreStyle" rel="noopener noreferrer"&gt;IgnoreStyle&lt;/a&gt; and &lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.WorkbookParseOptions.html#Syncfusion_EJ2_Spreadsheet_WorkbookParseOptions_IgnoreFormat" rel="noopener noreferrer"&gt;IgnoreFormat&lt;/a&gt; properties on the server, the spreadsheet loads only raw data, skipping the formatting overhead entirely.&lt;/p&gt;

&lt;p&gt;Here is a server-side example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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="n"&gt;IActionResult&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;IFormCollection&lt;/span&gt; &lt;span class="n"&gt;openRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;OpenRequest&lt;/span&gt; &lt;span class="n"&gt;open&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;OpenRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// Enable parsing options to skip styles and formats for faster loading&lt;/span&gt;
    &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseOptions&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;WorkbookParseOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;IgnoreStyle&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="n"&gt;IgnoreFormat&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="p"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// Process and return the parsed workbook data&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Content&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="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;open&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;By disabling style and format parsing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only raw cell values are processed&lt;/li&gt;
&lt;li&gt;JSON payload size is reduced&lt;/li&gt;
&lt;li&gt;Memory usage drops significantly&lt;/li&gt;
&lt;li&gt;Import time improves immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These options are ideal when styles and number formats aren’t important for your use case, and the goal is to load the Excel data as quickly as possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you gain&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster file loading&lt;/li&gt;
&lt;li&gt;Lower memory usage&lt;/li&gt;
&lt;li&gt;Smaller JSON payloads sent to the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is ideal for “data pipeline” imports where formatting is irrelevant (finance exports, HR records, inventory loads).&lt;/p&gt;

&lt;h3&gt;
  
  
  Threshold limits: Stop the file before it crashes the app
&lt;/h3&gt;

&lt;p&gt;Large Excel files don’t just overload the browser; they can spike server memory too. Without a safety check, a single oversized upload can silently degrade your entire application’s performance.&lt;/p&gt;

&lt;p&gt;Syncfusion’s threshold limits give you a clear control point. You can define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.ThresholdLimit.html#Syncfusion_EJ2_Spreadsheet_ThresholdLimit_MaximumDataLimit" rel="noopener noreferrer"&gt;MaximumDataLimit&lt;/a&gt;: The maximum number of cells allowed.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://help.syncfusion.com/cr/document-processing/Syncfusion.EJ2.Spreadsheet.ThresholdLimit.html#Syncfusion_EJ2_Spreadsheet_ThresholdLimit_MaximumFileSize" rel="noopener noreferrer"&gt;MaximumFileSize&lt;/a&gt;: The maximum file size in bytes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What happens when a limit is exceeded&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An alert message appears: &lt;strong&gt;&lt;code&gt;The file is large&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Cancel&lt;/code&gt;&lt;/strong&gt; stops the import cleanly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;OK&lt;/code&gt;&lt;/strong&gt; continues (only if your app logic permits it)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This single check prevents crashes, timeouts, and memory overloads caused by unexpectedly large uploads, and gives users clarity rather than confusion.&lt;/p&gt;

&lt;p&gt;You can configure the thresholds API on the server side using the following code example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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="n"&gt;IActionResult&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;IFormCollection&lt;/span&gt; &lt;span class="n"&gt;openRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;OpenRequest&lt;/span&gt; &lt;span class="n"&gt;open&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;OpenRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Files&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="n"&gt;open&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="n"&gt;openRequest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Guid"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Set maximum allowed number of cells&lt;/span&gt;
    &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ThresholdLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaximumDataLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1,000,000 cells&lt;/span&gt;

    &lt;span class="c1"&gt;// Set maximum allowed file size in bytes (e.g., 5MB)&lt;/span&gt;
    &lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ThresholdLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MaximumFileSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;5000000&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;openbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Content&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="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;open&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;openbook&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 above code example enforces a safety gate before parsing becomes expensive, protecting both the user experience and backend stability.&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%2F04%2FFile-size-warning-in-Angular-Spreadsheet.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%2F04%2FFile-size-warning-in-Angular-Spreadsheet.png" alt="File size warning in Angular Spreadsheet" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;File size warning in Angular Spreadsheet
  &lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON serialization: Parse once, open instantly
&lt;/h3&gt;

&lt;p&gt;Parsing a full &lt;strong&gt;&lt;code&gt;XLSX&lt;/code&gt;&lt;/strong&gt; file on every file open is expensive. If the workbook includes charts, images, conditional formatting, or complex styles, that overhead adds up fast.&lt;/p&gt;

&lt;p&gt;Syncfusion Angular Spreadsheet solves this with &lt;a href="https://help.syncfusion.com/document-processing/excel/spreadsheet/angular/performance-best-practices#configure-json-serialization-options-during-open" rel="noopener noreferrer"&gt;JSON serialization options&lt;/a&gt;. These let you exclude specific features, such as styles, formats, charts, images, wrap, and more, from the Workbook JSON object when opening it via the &lt;a href="https://ej2.syncfusion.com/angular/documentation/api/spreadsheet/index-default#openfromjson" rel="noopener noreferrer"&gt;openFromJson&lt;/a&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why JSON-based loading is faster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;&lt;code&gt;openFromJson&lt;/code&gt;&lt;/strong&gt;, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid repeated XLSX parsing&lt;/li&gt;
&lt;li&gt;Exclude features your app doesn’t need&lt;/li&gt;
&lt;li&gt;Reduce JSON size and processing time&lt;/li&gt;
&lt;li&gt;Improve load speed for large or complex workbooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Syncfusion also supports &lt;a href="https://help.syncfusion.com/document-processing/excel/spreadsheet/angular/open-save#configure-json-deserialization-options" rel="noopener noreferrer"&gt;selective deserialization&lt;/a&gt;. You can choose exactly which parts of the JSON to ignore during loading. Previously, &lt;strong&gt;&lt;code&gt;openFromJson&lt;/code&gt;&lt;/strong&gt; always loaded every element: styles, formulas, conditional formatting, charts, images, validations, and Notes. Now you control that explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client-side example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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="c1"&gt;// Load workbook JSON — ignore styles for faster rendering of the spreadsheet&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openFromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fileJson&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ignoreStyle&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you fine-grained control over the loading process, exactly what you need when you import large Excel files in Angular apps with complex structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to know more details about these techniques?&lt;/strong&gt; Explore the full Angular Spreadsheet &lt;a href="https://help.syncfusion.com/document-processing/excel/spreadsheet/angular/overview" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Syncfusion Angular Spreadsheet for large file imports
&lt;/h2&gt;

&lt;p&gt;Here’s how to integrate the Syncfusion Spreadsheet into your Angular app from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install Angular CLI
&lt;/h3&gt;

&lt;p&gt;You can use &lt;a href="https://github.com/angular/angular-cli" rel="noopener noreferrer"&gt;Angular CLI&lt;/a&gt; to set up your Angular applications. To install Angular CLI, use the following command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @angular/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create a new Angular application
&lt;/h3&gt;

&lt;p&gt;You can create a new Angular application using the following Angular CLI command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose your preferred stylesheet format (&lt;strong&gt;&lt;code&gt;CSS/SCSS&lt;/code&gt;&lt;/strong&gt;) and &lt;strong&gt;&lt;code&gt;SSR&lt;/code&gt;&lt;/strong&gt; options when prompted.&lt;/p&gt;

&lt;p&gt;For more details, refer to the blog post at &lt;a href="https://www.syncfusion.com/blogs/post/angular-spreadsheet-freezing-large-excel-import" rel="noopener noreferrer"&gt;Syncfusion.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>angularspreadsheet</category>
      <category>excelimport</category>
      <category>excellikespreadsheet</category>
    </item>
    <item>
      <title>.NET MAUI State Management: From ViewModels to App Stores</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Thu, 16 Apr 2026 13:46:15 +0000</pubDate>
      <link>https://dev.to/syncfusion/net-maui-state-management-from-viewmodels-to-app-stores-4al9</link>
      <guid>https://dev.to/syncfusion/net-maui-state-management-from-viewmodels-to-app-stores-4al9</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Start with MVVM and data binding for page-level state, apply Visual State Manager for visual-only changes, and rely on DI-backed app-wide stores for shared state. For highly interactive or complex flows, MVU or reducer-style unidirectional updates offer better predictability.&lt;/p&gt;

&lt;p&gt;State is the backbone of every .NET MAUI application. It determines what users see, how the UI reacts to interactions, and how consistently data flows across screens. Whether it’s the text inside an Entry, the enabled state of a button, or the user session shared across pages, effective state management ensures your UI remains predictable, testable, and easy to maintain.&lt;/p&gt;

&lt;p&gt;.NET MAUI does not enforce a single state management solution. Instead, it provides flexible building blocks that allow you to choose the right approach based on your app’s complexity. This article walks through the most practical and production-ready ways to manage state in .NET MAUI, from simple &lt;strong&gt;ViewModel&lt;/strong&gt; state to shared app-wide stores, and helps you decide when to use each.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why state management in MAUI matters
&lt;/h2&gt;

&lt;p&gt;In real apps, state stops being “just a couple of properties” fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication affects navigation, headers, API calls, and cached data.&lt;/li&gt;
&lt;li&gt;Offline support introduces queues and retry logic.&lt;/li&gt;
&lt;li&gt;Multiple pages need the same data (profile, cart, preferences).&lt;/li&gt;
&lt;li&gt;Background sync and push notifications mutate state while the user is elsewhere.&lt;/li&gt;
&lt;li&gt;Multi-window scenarios need a shared but safe global state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If state changes can happen anywhere, debugging becomes guesswork. The goal is not a perfect architecture; it’s predictable updates and a UI that stays consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended state management approaches in .NET MAUI
&lt;/h2&gt;

&lt;p&gt;Most real-world MAUI apps rely on a combination of the following patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MVVM with data binding&lt;/strong&gt; for page-level and feature-level state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MVU-style updates&lt;/strong&gt; for predictable, unidirectional state transitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual State Manager (VSM)&lt;/strong&gt; for purely visual UI changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Injection (DI)&lt;/strong&gt; for shared and app-wide state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than treating these as competing patterns, think of them as complementary tools. The key is applying each one where it fits best.&lt;/p&gt;

&lt;h2&gt;
  
  
  MVVM + INotifyPropertyChanged: The foundation
&lt;/h2&gt;

&lt;p&gt;MVVM (Model-View-ViewModel) is the most widely used pattern in .NET MAUI and forms the foundation of state management for most applications.&lt;/p&gt;

&lt;p&gt;In MVVM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;View&lt;/strong&gt; defines the UI using XAML.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;ViewModel&lt;/strong&gt; holds the state and exposes it through bindable properties.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Model&lt;/strong&gt; represents your domain or business data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The View never directly manipulates state. Instead, it binds to ViewModel properties. When the ViewModel changes, the UI updates automatically through data binding.&lt;/p&gt;

&lt;p&gt;Here’s the example MVVM implementation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ViewModel.cs&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;ViewModel&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="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;set&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;name&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nf"&gt;OnPropertyChanged&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="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="n"&gt;PropertyChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnPropertyChanged&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;CallerMemberName&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;propertyName&lt;/span&gt; &lt;span class="p"&gt;=&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;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PropertyChanged&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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;PropertyChangedEventArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propertyName&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;&lt;strong&gt;MainPage.xaml&lt;/strong&gt;&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;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Name}"&lt;/span&gt; &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MainPage.xaml.cs&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;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainPage&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ContentPage&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;MainPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;InitializeComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;BindingContext&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;ViewModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Maui"&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;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forms, lists, and CRUD screens&lt;/li&gt;
&lt;li&gt;Input validation and command-based interactions&lt;/li&gt;
&lt;li&gt;Navigation flows and page-level logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MVVM is ideal when you want strong separation of concerns, easy unit testing, and straightforward data binding.&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%2F04%2FMVVM-architecture-flow-in-.NET-MAUI.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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F04%2FMVVM-architecture-flow-in-.NET-MAUI.jpg" alt="MVVM architecture flow in .NET MAUI" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;MVVM architecture flow in .NET MAUI
  &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  MVU (Model-View-Update): Predictable state flow
&lt;/h2&gt;

&lt;p&gt;MVU introduces a functional, unidirectional approach to state management. Instead of mutating properties across different objects, MVU treats state as an immutable model that changes only through explicit updates.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model → View → Message → Update → Model&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt; represents the entire state snapshot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View&lt;/strong&gt; renders UI based solely on the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update&lt;/strong&gt; receives user actions or events messages and returns a new model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C#&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;record&lt;/span&gt; &lt;span class="nc"&gt;AppModel&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;Count&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;enum&lt;/span&gt; &lt;span class="n"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Increment&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;AppModel&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppModel&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Msg&lt;/span&gt; &lt;span class="n"&gt;msg&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;msg&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Increment&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;with&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="n"&gt;model&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="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because all state changes occur in a single Update function, MVU delivers predictable behavior and prevents unintended state mutations. It works best for UIs with complex, interdependent interactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highly interactive screens&lt;/li&gt;
&lt;li&gt;Complex workflows with many dependent UI updates&lt;/li&gt;
&lt;li&gt;Scenarios where unidirectional data flow is preferred&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MVU is less common than MVVM in MAUI, but it shines in advanced use cases and pairs well with frameworks like &lt;strong&gt;&lt;code&gt;Comet&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;MauiReactor&lt;/code&gt;&lt;/strong&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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F04%2FMVU-state-flow-in-.NET-MAUI.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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F04%2FMVU-state-flow-in-.NET-MAUI.jpg" alt="MVU architecture flow in .NET MAUI" width="780" height="432"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;MVU architecture flow in .NET MAUI
  &lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Visual State Manager (VSM): Declarative UI states
&lt;/h2&gt;

&lt;p&gt;Visual State Manager (VSM) is designed specifically for visual changes, not business logic. It allows you to define &lt;strong&gt;named visual states&lt;/strong&gt;, such as &lt;strong&gt;&lt;code&gt;Normal&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;Pressed&lt;/code&gt;&lt;/strong&gt;, or &lt;strong&gt;&lt;code&gt;Selected&lt;/code&gt;&lt;/strong&gt;, and let VSM handle the transitions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The View (XAML)&lt;/strong&gt; declares &lt;strong&gt;&lt;code&gt;VisualStateGroups&lt;/code&gt;&lt;/strong&gt; and individual &lt;strong&gt;&lt;code&gt;VisualStates&lt;/code&gt;&lt;/strong&gt;, each containing the appearance rules for that state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State changes&lt;/strong&gt; are triggered through bindings, behaviors, or code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of manually toggling properties across controls, you switch visual states and let VSM handle the transitions. This keeps your UI logic organized, consistent, and easy to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;ContentView&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;BoxView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"box"&lt;/span&gt;
             &lt;span class="na"&gt;WidthRequest=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt;
             &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;VisualStateGroup&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"SelectionStates"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"Normal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;TargetName=&lt;/span&gt;&lt;span class="s"&gt;"box"&lt;/span&gt;
                            &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"BackgroundColor"&lt;/span&gt;
                            &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"LightGray"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;VisualState&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"Selected"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;VisualState.Setters&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;Setter&lt;/span&gt; &lt;span class="na"&gt;TargetName=&lt;/span&gt;&lt;span class="s"&gt;"box"&lt;/span&gt;
                            &lt;span class="na"&gt;Property=&lt;/span&gt;&lt;span class="s"&gt;"BackgroundColor"&lt;/span&gt;
                            &lt;span class="na"&gt;Value=&lt;/span&gt;&lt;span class="s"&gt;"LightBlue"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/VisualState.Setters&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/VisualState&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/VisualStateGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/VisualStateManager.VisualStateGroups&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual feedback such as selection, focus, or disabled states.&lt;/li&gt;
&lt;li&gt;Simple animations or appearance changes.&lt;/li&gt;
&lt;li&gt;Keeping UI logic declarative and XAML-focused.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid placing business logic inside visual state 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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F04%2FVisual-State-Manager-flow-in-.NET-MAUI.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%2Fwww.syncfusion.com%2Fblogs%2Fwp-content%2Fuploads%2F2026%2F04%2FVisual-State-Manager-flow-in-.NET-MAUI.jpg" alt="Visual State Manager flow in .NET MAUI" width="780" height="505"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Visual State Manager flow in .NET MAUI
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;For more details, refer to the blog post at &lt;a href="https://www.syncfusion.com/blogs/post/managing-state-in-dotnet-maui" rel="noopener noreferrer"&gt;Syncfusion.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>netmaui</category>
      <category>dependencyinjection</category>
      <category>maui</category>
      <category>mvvm</category>
    </item>
    <item>
      <title>.NET MAUI ListView vs CollectionView: How Syncfusion ListView Performs Better</title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Thu, 16 Apr 2026 12:16:44 +0000</pubDate>
      <link>https://dev.to/syncfusion/net-maui-listview-vs-collectionview-how-syncfusion-listview-performs-better-4fj9</link>
      <guid>https://dev.to/syncfusion/net-maui-listview-vs-collectionview-how-syncfusion-listview-performs-better-4fj9</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Many .NET MAUI lists falter under real-world demands. This comparison reveals how Syncfusion .NET MAUI ListView confidently manages grouping, gestures, infinite scroll, and large data, delivering smoother scrolling, fewer allocations, and less glue code than CollectionView.&lt;/p&gt;

&lt;h2&gt;
  
  
  When CollectionView hits its limits, here’s the ListView built for real apps
&lt;/h2&gt;

&lt;p&gt;If you’ve built more than a demo app with .NET MAUI, you’ve probably hit this point:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“The list worked great—until we added more features.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/collectionview/?view=net-maui-10.0" rel="noopener noreferrer"&gt;.NET MAUI CollectionView&lt;/a&gt; is a solid starting point, but it starts to show strain as an app grows.&lt;/p&gt;

&lt;p&gt;Add grouped data, sticky headers, swipe actions, drag‑and‑drop, or infinite scrolling, and CollectionView often turns into a web of event handlers, state management, and performance trade‑offs. The result is familiar: more code to maintain, tougher debugging, and a list of experiences that feel sluggish on real devices.&lt;/p&gt;

&lt;p&gt;Syncfusion&lt;sup&gt;®&lt;/sup&gt; &lt;a href="https://www.syncfusion.com/maui-controls/maui-listview" rel="noopener noreferrer"&gt;.NET MAUI ListView&lt;/a&gt; takes a different approach.&lt;/p&gt;

&lt;p&gt;Instead of making you piece together common list behaviors, it delivers them out of the box using built‑in properties, MVVM‑friendly commands, and a virtualization engine designed for large datasets. The payoff is immediate: cleaner code, smoother scrolling, and significantly lower memory usage.&lt;/p&gt;

&lt;p&gt;This post compares both controls using the same real‑world scenario and shows where Syncfusion .NET MAUI ListView saves time, code, and memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Syncfusion .NET MAUI ListView outshines Collection View
&lt;/h2&gt;

&lt;p&gt;When comparing these two controls, the Syncfusion &lt;a href="https://help.syncfusion.com/maui/listview/getting-started" rel="noopener noreferrer"&gt;.NET MAUI ListView&lt;/a&gt; stands out with built‑in features that reduce extra code and deliver smoother performance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Grouping with sticky headers:&lt;/strong&gt; Pins the current group header at the top while scrolling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swipe actions:&lt;/strong&gt; Reveals quick actions by swiping left or right on an item.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drag-and-drop reorder:&lt;/strong&gt; Users can reorder items by dragging them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental loading:&lt;/strong&gt; Loads the page on demand for faster, lighter lists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout choices:&lt;/strong&gt; Switches between list and grid presentations to fit the content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Item sizing and virtualization:&lt;/strong&gt; Uses fixed or measured row heights to keep scrolling smooth.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Syncfusion .NET MAUI ListView vs .NET MAUI CollectionView: Real feature comparison
&lt;/h2&gt;

&lt;p&gt;To keep this comparison honest, we are going to test both controls using the &lt;strong&gt;same MVVM book list&lt;/strong&gt;, no shortcuts, no artificial demos. Each section answers the kinds of questions developers actually run into when building production apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much effort does sticky grouping really take?&lt;/li&gt;
&lt;li&gt;How quickly does swipe support get messy?&lt;/li&gt;
&lt;li&gt;What breaks when you add drag‑and‑drop reordering?&lt;/li&gt;
&lt;li&gt;And what happens to memory usage as the list grows?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Along the way, you’ll see where Syncfusion ListView handles these scenarios with built‑in properties and templates, and where Collection View requires extra event wiring, state tracking, and custom logic.&lt;/p&gt;

&lt;p&gt;Instead of theory or feature checklists, this walkthrough shows real code, real UI behavior, and real performance numbers, so you can decide which control fits your feature needs and performance goals based on facts rather than assumptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Grouping with sticky headers
&lt;/h3&gt;

&lt;p&gt;To see the difference clearly, let’s compare how each control handles this feature:&lt;/p&gt;

&lt;h4&gt;
  
  
  a) Syncfusion .NET MAUI ListView
&lt;/h4&gt;

&lt;p&gt;Sticky headers are a built-in feature in Syncfusion .NET MAUI ListView. As you scroll, the current group title stays pinned at the top, so users always know where they are. A single setting enables it, and grouping by the first letter feels effortless.&lt;/p&gt;

&lt;p&gt;Here’s the code you need:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;listView:SfListView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"listView"&lt;/span&gt;
                      &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookInfo}"&lt;/span&gt;
                      &lt;span class="na"&gt;IsStickyGroupHeader=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;listView:SfListView.GroupHeaderTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#F2F2F2"&lt;/span&gt; &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"8,4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Key}"&lt;/span&gt; &lt;span class="na"&gt;VerticalTextAlignment=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&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;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/listView:SfListView.GroupHeaderTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/listView:SfListView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&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="c1"&gt;// Group items by the first character of BookName (uppercase).&lt;/span&gt;
&lt;span class="n"&gt;listView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GroupDescriptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&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;GroupDescriptor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PropertyName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"BookName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;KeySelector&lt;/span&gt; &lt;span class="p"&gt;=&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;obj1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;item&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;BookInfo&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;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BookName&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="nf"&gt;ToString&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;h4&gt;
  
  
  b) .NET MAUI CollectionView
&lt;/h4&gt;

&lt;p&gt;You need to manage sticky headers manually. On every scroll, you recalculate which group is visible and update the header. It works but requires extra housekeeping.&lt;/p&gt;

&lt;p&gt;Refer to the following code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;Grid&lt;/span&gt; &lt;span class="na"&gt;RowDefinitions=&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;Border&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#F2F2F2"&lt;/span&gt; &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"8,4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding CurrentGroupName}"&lt;/span&gt; &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Border&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;CollectionView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"List"&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;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookGroups}"&lt;/span&gt;
                    &lt;span class="na"&gt;IsGrouped=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt;
                    &lt;span class="na"&gt;SelectionMode=&lt;/span&gt;&lt;span class="s"&gt;"Multiple"&lt;/span&gt;
                    &lt;span class="na"&gt;ItemSizingStrategy=&lt;/span&gt;&lt;span class="s"&gt;"MeasureFirstItem"&lt;/span&gt;
                    &lt;span class="na"&gt;Scrolled=&lt;/span&gt;&lt;span class="s"&gt;"OnCollectionViewScrolled"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;CollectionView.GroupHeaderTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#f2f2f2"&lt;/span&gt; &lt;span class="na"&gt;Padding=&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;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Name}"&lt;/span&gt; &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&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;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/CollectionView.GroupHeaderTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/CollectionView&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;&lt;strong&gt;C#&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="n"&gt;Loaded&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnLoaded&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;void&lt;/span&gt; &lt;span class="nf"&gt;OnLoaded&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="nf"&gt;UpdateCurrentGroupHeader&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;void&lt;/span&gt; &lt;span class="nf"&gt;OnCollectionViewScrolled&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;ItemsViewScrolledEventArgs&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="nf"&gt;UpdateCurrentGroupHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstVisibleItemIndex&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;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateCurrentGroupHeader&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;firstVisibleIndex&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="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;Vm&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BookGroups&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BookGroups&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;0&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;firstVisibleIndex&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="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;firstVisibleIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Map to group by flattening groups until we cover firstVisibleIndex&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursor&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;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BookGroups&lt;/span&gt;&lt;span class="p"&gt;)&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;groupCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;group&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;groupCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&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;cursor&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;groupCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Fallback&lt;/span&gt;
    &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentGroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BookGroups&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="n"&gt;Name&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;
  
  
  2. Swipe actions
&lt;/h3&gt;

&lt;p&gt;Both controls support swipe gestures, but the way they implement and simplify quick actions differs significantly:&lt;/p&gt;

&lt;h4&gt;
  
  
  a) Syncfusion .NET MAUI ListView
&lt;/h4&gt;

&lt;p&gt;Swipe gestures reveal sleek, ready-to-use actions like &lt;strong&gt;Favorite&lt;/strong&gt; and &lt;strong&gt;Delete&lt;/strong&gt;. Templates keep the UI consistent, and commands bind directly to your ViewModel. The experience feels native across platforms.&lt;/p&gt;

&lt;p&gt;Refer to the following code example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;listView:SfListView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"listView"&lt;/span&gt;
                      &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookInfo}"&lt;/span&gt;
                      &lt;span class="na"&gt;AllowSwiping=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;listView:SfListView.StartSwipeTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#E8F5E9"&lt;/span&gt; &lt;span class="na"&gt;Padding=&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;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Favourite"&lt;/span&gt;
                        &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"12"&lt;/span&gt;
                        &lt;span class="na"&gt;Padding=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt;
                        &lt;span class="na"&gt;TextColor=&lt;/span&gt;&lt;span class="s"&gt;"White"&lt;/span&gt;
                        &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#2E7032"&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;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/listView:SfListView.StartSwipeTemplate&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;listView:SfListView.EndSwipeTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Grid&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#FFEBEE"&lt;/span&gt; &lt;span class="na"&gt;Padding=&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;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Delete"&lt;/span&gt;
                        &lt;span class="na"&gt;TextColor=&lt;/span&gt;&lt;span class="s"&gt;"White"&lt;/span&gt;
                        &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#C62828"&lt;/span&gt;
                        &lt;span class="na"&gt;Command=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BindingContext.DeleteCommand, Source={x:Reference listView}}"&lt;/span&gt;
                        &lt;span class="na"&gt;CommandParameter=&lt;/span&gt;&lt;span class="s"&gt;"{Binding .}"&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;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/listView:SfListView.EndSwipeTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/listView:SfListView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  b) .NET MAUI CollectionView
&lt;/h4&gt;

&lt;p&gt;You wrap each item in a Swipe View and define left/right actions yourself. Flexible, but repetitive, more template code for the same result.&lt;/p&gt;

&lt;p&gt;Here’s how to implement this feature:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;CollectionView&lt;/span&gt; &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookInfo}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;SwipeView&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;SwipeView.LeftItems&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;SwipeItems&lt;/span&gt; &lt;span class="na"&gt;Mode=&lt;/span&gt;&lt;span class="s"&gt;"Reveal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;SwipeItem&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Fav"&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#FFE08A"&lt;/span&gt;
                       &lt;span class="na"&gt;Command=&lt;/span&gt;&lt;span class="s"&gt;"{Binding FavoriteCommand}"&lt;/span&gt; &lt;span class="na"&gt;CommandParameter=&lt;/span&gt;&lt;span class="s"&gt;"{Binding .}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/SwipeItems&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/SwipeView.LeftItems&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;SwipeView.RightItems&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;SwipeItems&lt;/span&gt; &lt;span class="na"&gt;Mode=&lt;/span&gt;&lt;span class="s"&gt;"Reveal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;SwipeItem&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Delete"&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"#FFCDD2"&lt;/span&gt;
                       &lt;span class="na"&gt;Command=&lt;/span&gt;&lt;span class="s"&gt;"{Binding DeleteCommand}"&lt;/span&gt; &lt;span class="na"&gt;CommandParameter=&lt;/span&gt;&lt;span class="s"&gt;"{Binding .}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/SwipeItems&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/SwipeView.RightItems&amp;gt;&lt;/span&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;"12,8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookName}"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/Grid&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/SwipeView&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CollectionView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Drag-and-drop reordering
&lt;/h3&gt;

&lt;p&gt;This feature shows a clear difference in how each control handles item movement and user interaction:&lt;/p&gt;

&lt;h4&gt;
  
  
  a) Syncfusion .NET MAUI ListView
&lt;/h4&gt;

&lt;p&gt;Reordering is built in. You can press, drag, and release; items move exactly where expected without juggling gestures or indexes, as shown in the code below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;listView:SfListView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"listView"&lt;/span&gt;
                      &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookInfo}"&lt;/span&gt;
                      &lt;span class="na"&gt;DragStartMode=&lt;/span&gt;&lt;span class="s"&gt;"OnHold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  b) .NET MAUI CollectionView
&lt;/h4&gt;

&lt;p&gt;You wire up drag/drop events, manage indices, and refresh grouping after reordering. Powerful, but more moving parts.&lt;/p&gt;

&lt;p&gt;Here’s how you can do it in code:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;CollectionView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"List"&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;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookGroups}"&lt;/span&gt;
                &lt;span class="na"&gt;IsGrouped=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt;
                &lt;span class="na"&gt;SelectionMode=&lt;/span&gt;&lt;span class="s"&gt;"Multiple"&lt;/span&gt;
                &lt;span class="na"&gt;ItemSizingStrategy=&lt;/span&gt;&lt;span class="s"&gt;"MeasureFirstItem"&lt;/span&gt;
                &lt;span class="na"&gt;Scrolled=&lt;/span&gt;&lt;span class="s"&gt;"OnCollectionViewScrolled"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;DataTemplate&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;SwipeView&amp;gt;&lt;/span&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;"12"&lt;/span&gt; &lt;span class="na"&gt;RowDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,Auto"&lt;/span&gt; &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,*"&lt;/span&gt; &lt;span class="na"&gt;HeightRequest=&lt;/span&gt;&lt;span class="s"&gt;"70"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;Grid.GestureRecognizers&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;DropGestureRecognizer&lt;/span&gt; &lt;span class="na"&gt;AllowDrop=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt; &lt;span class="na"&gt;DragOver=&lt;/span&gt;&lt;span class="s"&gt;"OnDragOver"&lt;/span&gt; &lt;span class="na"&gt;Drop=&lt;/span&gt;&lt;span class="s"&gt;"OnDrop"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/Grid.GestureRecognizers&amp;gt;&lt;/span&gt;

                    &lt;span class="c"&gt;&amp;lt;!-- Drag handle icon --&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Grid.RowSpan=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;VerticalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="na"&gt;Margin=&lt;/span&gt;&lt;span class="s"&gt;"0,0,8,0"&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"≡≡"&lt;/span&gt; &lt;span class="na"&gt;FontSize=&lt;/span&gt;&lt;span class="s"&gt;"18"&lt;/span&gt; &lt;span class="na"&gt;Opacity=&lt;/span&gt;&lt;span class="s"&gt;"0.6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;Label.GestureRecognizers&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;DragGestureRecognizer&lt;/span&gt; &lt;span class="na"&gt;CanDrag=&lt;/span&gt;&lt;span class="s"&gt;"True"&lt;/span&gt; &lt;span class="na"&gt;DragStarting=&lt;/span&gt;&lt;span class="s"&gt;"OnDragStarting"&lt;/span&gt; &lt;span class="na"&gt;DropCompleted=&lt;/span&gt;&lt;span class="s"&gt;"OnDropCompleted"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/Label.GestureRecognizers&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/Label&amp;gt;&lt;/span&gt;

                    &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookName}"&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;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;FontAttributes=&lt;/span&gt;&lt;span class="s"&gt;"Bold"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;Label&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookDescription}"&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;Grid.Column=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;Opacity=&lt;/span&gt;&lt;span class="s"&gt;"0.7"&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;span class="nt"&gt;&amp;lt;/SwipeView&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/DataTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/CollectionView.ItemTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CollectionView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&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;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnDragStarting&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;DragStartingEventArgs&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Element&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BindingContext&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;BookInfo&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&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;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnDrop&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;DropEventArgs&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Vm&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&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;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;payload&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;BookInfo&lt;/span&gt; &lt;span class="n"&gt;source&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Element&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BindingContext&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;BookInfo&lt;/span&gt; &lt;span class="n"&gt;target&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="nf"&gt;ReferenceEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&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;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BookInfo&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;sourceIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&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;targetIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&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;sourceIndex&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;targetIndex&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="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Normalize target index when removing earlier item affects index&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;sourceIndex&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;targetIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;targetIndex&lt;/span&gt;&lt;span class="p"&gt;--;&lt;/span&gt;

            &lt;span class="n"&gt;list&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;sourceIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// Update stable order indices and rebuild grouping to reflect new order in the UI&lt;/span&gt;
            &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReindexOrders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;Vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RefreshGroups&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;h3&gt;
  
  
  4. Incremental loading
&lt;/h3&gt;

&lt;p&gt;Handling large datasets highlights how each control approaches infinite scrolling and data fetch efficiency:&lt;/p&gt;

&lt;h4&gt;
  
  
  a) Syncfusion .NET MAUI ListView
&lt;/h4&gt;

&lt;p&gt;Infinite scrolling is simple. The control fetches more items as you near the end, with a built-in busy indicator. A single command controls when and how much to load.&lt;/p&gt;

&lt;p&gt;Refer to the following code examples for feature implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;listView:SfListView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"listView"&lt;/span&gt;
                      &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookInfo}"&lt;/span&gt;
                      &lt;span class="na"&gt;LoadMoreOption=&lt;/span&gt;&lt;span class="s"&gt;"Manual"&lt;/span&gt;
                      &lt;span class="na"&gt;LoadMorePosition=&lt;/span&gt;&lt;span class="s"&gt;"End"&lt;/span&gt;
                      &lt;span class="na"&gt;LoadMoreCommand=&lt;/span&gt;&lt;span class="s"&gt;"{Binding LoadMoreItemsCommand}"&lt;/span&gt;
                      &lt;span class="na"&gt;LoadMoreCommandParameter=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Source={x:Reference listView}}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&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="n"&gt;ICommand&lt;/span&gt; &lt;span class="n"&gt;LoadMoreItemsCommand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;LoadMoreItemsCommand&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;Command&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;LoadMoreItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CanLoadMoreItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;CanLoadMore&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;BookInfo&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;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;totalItems&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;CanLoadMoreItems&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;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CanLoadMore&lt;/span&gt;&lt;span class="p"&gt;()&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;IsLoading&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;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LoadMoreItems&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;obj&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="nf"&gt;CanLoadMore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;IsLoading&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="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;listView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsLazyLoading&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;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;listView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsLazyLoading&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="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;AddBooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;currentIndex&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;PageSize&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;h4&gt;
  
  
  b) .NET MAUI CollectionView
&lt;/h4&gt;

&lt;p&gt;You implement a “ &lt;strong&gt;Load More&lt;/strong&gt; ” footer and spinner, track remaining items, and manage progress state. Clear and explicit, but heavier on logic.&lt;/p&gt;

&lt;p&gt;Here’s the code you need for quick implementation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XAML&lt;/strong&gt;&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;CollectionView&lt;/span&gt; &lt;span class="na"&gt;x:Name=&lt;/span&gt;&lt;span class="s"&gt;"List"&lt;/span&gt;
                 &lt;span class="na"&gt;ItemsSource=&lt;/span&gt;&lt;span class="s"&gt;"{Binding BookGroups}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Manual Load More footer to mirror List View's LoadMoreOption=Manual at End --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CollectionView.Footer&amp;gt;&lt;/span&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;"12"&lt;/span&gt; &lt;span class="na"&gt;BackgroundColor=&lt;/span&gt;&lt;span class="s"&gt;"Transparent"&lt;/span&gt; &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt; &lt;span class="na"&gt;RowDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,Auto"&lt;/span&gt; &lt;span class="na"&gt;ColumnDefinitions=&lt;/span&gt;&lt;span class="s"&gt;"Auto,Auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Button&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Load More"&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;Grid.ColumnSpan=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
                    &lt;span class="na"&gt;HorizontalOptions=&lt;/span&gt;&lt;span class="s"&gt;"Center"&lt;/span&gt;
                    &lt;span class="na"&gt;IsEnabled=&lt;/span&gt;&lt;span class="s"&gt;"{Binding HasMoreItems}"&lt;/span&gt;
                    &lt;span class="na"&gt;Command=&lt;/span&gt;&lt;span class="s"&gt;"{Binding LoadMoreItemsCommand}"&lt;/span&gt;
                    &lt;span class="na"&gt;CommandParameter=&lt;/span&gt;&lt;span class="s"&gt;"{Binding Source={x:Reference List}}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;ActivityIndicator&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;Grid.ColumnSpan=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;
                               &lt;span class="na"&gt;IsVisible=&lt;/span&gt;&lt;span class="s"&gt;"{Binding IsLoading}"&lt;/span&gt;
                               &lt;span class="na"&gt;IsRunning=&lt;/span&gt;&lt;span class="s"&gt;"{Binding IsLoading}"&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;span class="nt"&gt;&amp;lt;/CollectionView.Footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/CollectionView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C#&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="n"&gt;ICommand&lt;/span&gt; &lt;span class="n"&gt;LoadMoreItemsCommand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;LoadMoreItemsCommand&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;Command&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;LoadMoreItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CanLoadMoreItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;CanLoadMore&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;BookInfo&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;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;totalItems&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;CanLoadMoreItems&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;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CanLoadMore&lt;/span&gt;&lt;span class="p"&gt;()&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;IsLoading&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;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LoadMoreItems&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;obj&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="nf"&gt;CanLoadMore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;IsLoading&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="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// CollectionView path uses the footer ActivityIndicator bound to IsLoading&lt;/span&gt;
        &lt;span class="n"&gt;IsLoading&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;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;IsLoading&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="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;AddBooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;currentIndex&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;PageSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;RebuildGroups&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;For more details, refer to the blog post at &lt;a href="https://www.syncfusion.com/blogs/post/dotnet-maui-listview-vs-collectionview" rel="noopener noreferrer"&gt;Syncfusion.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>netmaui</category>
      <category>crossplatform</category>
      <category>desktop</category>
      <category>maui</category>
    </item>
    <item>
      <title>Spec-Driven Development with Markdown for AI Workflows </title>
      <dc:creator>Lucy Muturi</dc:creator>
      <pubDate>Thu, 02 Apr 2026 07:23:10 +0000</pubDate>
      <link>https://dev.to/syncfusion/spec-driven-development-with-markdown-for-ai-workflows-39c4</link>
      <guid>https://dev.to/syncfusion/spec-driven-development-with-markdown-for-ai-workflows-39c4</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Spec-driven development uses Markdown specifications and Code Studio prompt files as a single source of truth, letting AI reliably generate, test, review, and deploy code while preserving context. This blog shows practical prompt files and workflows, plus project structure and best practices for building a real-time project.&lt;/p&gt;

&lt;p&gt;AI revolutionizes software development but loses context between chats, forgets earlier architectural decisions, and produces contradictory implementations unless you keep re-explaining the same requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spec-driven development&lt;/strong&gt; treats Markdown specifications as your single source of truth. Rather than repeating requirements in AI conversations, you maintain a single living document that captures what your application does, how it behaves, and why.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.syncfusion.com/code-studio/" rel="noopener noreferrer"&gt;Syncfusion&lt;/a&gt;&lt;a href="https://www.syncfusion.com/document-sdk/net-word-library" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;sup&gt;®&lt;/sup&gt;&lt;/strong&gt;&lt;/a&gt;&lt;a href="https://www.syncfusion.com/code-studio/" rel="noopener noreferrer"&gt;Code Studio&lt;/a&gt; makes this even easier with a &lt;strong&gt;&lt;code&gt;prompt.md&lt;/code&gt;&lt;/strong&gt; files (and &lt;a href="https://help.syncfusion.com/code-studio/reference/configure-properties/custom-agents" rel="noopener noreferrer"&gt;Custom Agents&lt;/a&gt;), workflow files that tell your AI assistant exactly what to do at each development stage. This enables seamless specification-to-code compilation, code reviews, testing, and deployment workflows.&lt;/p&gt;

&lt;p&gt;This blog post will guide you through implementing a practical spec-to-code workflow using &lt;strong&gt;&lt;code&gt;prompt.md&lt;/code&gt;&lt;/strong&gt; templates while building a real-world analytics dashboard example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI-assisted development breaks down
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The core problem: AI context loss
&lt;/h3&gt;

&lt;p&gt;When working with traditional AI‑assisted development, the AI often generates code in isolation without retaining or referencing the broader project context.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Example&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You ask for a feature (e.g., authentication).&lt;/li&gt;
&lt;li&gt;The AI generates code in isolation.&lt;/li&gt;
&lt;li&gt;Later, you ask for changes, and the AI forgets earlier constraints (tenancy, RBAC, security rules, naming conventions).&lt;/li&gt;
&lt;li&gt;You repeat requirements and still get drift.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What is spec-driven development?
&lt;/h2&gt;

&lt;p&gt;Spec-driven development is a methodology where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The specification is the source code. Your Markdown files describe the complete application design (behavior, APIs, constraints, and acceptance criteria) in plain English mixed with structured data. AI generates implementation; Code Studio’s Custom Agents compile your specification into working code.&lt;/li&gt;
&lt;li&gt;Every AI task references the same spec. You reduce contradictions and rework.&lt;/li&gt;
&lt;li&gt;Documentation stays aligned. The spec evolves alongside the code rather than becoming stale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;User:&lt;/strong&gt; Build me a user authentication module.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;AI:&lt;/strong&gt; &lt;em&gt;Generates authentication code&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Spec‐driven development ensures the AI always has access to the relevant specifications. This means the generated code is not only functional but also aligned with architectural requirements, security standards, and organizational workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# User Authentication Module Specification&lt;/span&gt;
&lt;span class="c"&gt;## Overview&lt;/span&gt;
Multi-tenant SaaS application requiring OAuth 2.0 with role-based access control &lt;span class="o"&gt;(&lt;/span&gt;RBAC&lt;span class="o"&gt;)&lt;/span&gt; 
across 10,000+ organizations.
&lt;span class="c"&gt;## RBAC Structure&lt;/span&gt;
- Organization Admin: Full access to org settings
- Team Lead: Manage team members and reports
- Member: Read-only access to team data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This format makes requirements explicit, so code generation and reviews can consistently validate against it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How spec-driven development with Markdown works in Code Studio
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What are prompt.md files?
&lt;/h3&gt;

&lt;p&gt;Prompt files are workflow specifications written in Markdown that tell Code Studio’s AI what to do at each stage of development. They are the primary tool for automating your development process.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repeatable instructions&lt;/strong&gt;: Store prompts and reusable specifications to ensure consistent execution across projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task automation&lt;/strong&gt;: Automate critical stages such as compilation, testing, code review, and deployment for faster delivery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool access&lt;/strong&gt;: Provide AI with direct access to file operations, terminal commands, and web search for comprehensive workflow support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable workflows&lt;/strong&gt;: Share and apply prompt files across teams and projects to standardize development practices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version control&lt;/strong&gt;: Manage and track prompt files in Git alongside source code for full history and collaboration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prompt.md file format (template)
&lt;/h3&gt;

&lt;p&gt;All prompt files follow this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;---&lt;/span&gt;
name: prompt file name 
mode: agent
description: Brief description of what this prompt does
&lt;span class="nt"&gt;---&lt;/span&gt;

&lt;span class="c"&gt;# Main Task Title&lt;/span&gt;
Step-by-step instructions &lt;span class="k"&gt;for &lt;/span&gt;the AI.
- Use bullet points &lt;span class="k"&gt;for &lt;/span&gt;clarity
- Include specific file paths
- Reference specifications
- Define expected outputs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure is simple, but it’s the key to making AI output consistent and reviewable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec-driven development with Markdown: Project setup (dashboard example)
&lt;/h2&gt;

&lt;p&gt;Creating a complete dashboard project with the help of &lt;strong&gt;&lt;code&gt;prompt.md&lt;/code&gt;&lt;/strong&gt; files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended project structure
&lt;/h3&gt;

&lt;p&gt;analytics-dashboard/&lt;br&gt;&lt;br&gt;
├──. codestudio/&lt;br&gt;&lt;br&gt;
│ └── prompts/&lt;br&gt;&lt;br&gt;
│ ├── compile.prompt.md&lt;br&gt;&lt;br&gt;
│ ├── lint.prompt.md&lt;br&gt;&lt;br&gt;
│ └── test.prompt.md&lt;br&gt;&lt;br&gt;
├── src/&lt;br&gt;&lt;br&gt;
│ ├── components/&lt;br&gt;&lt;br&gt;
│ │ ├── Dashboard.tsx&lt;br&gt;&lt;br&gt;
│ │ ├── Chart.tsx&lt;br&gt;&lt;br&gt;
│ │ └── Widget.tsx&lt;br&gt;&lt;br&gt;
│ └── types/&lt;br&gt;&lt;br&gt;
│ └── dashboard. types.ts&lt;br&gt;&lt;br&gt;
├── tests/&lt;br&gt;&lt;br&gt;
│ ├── dashboard.test.ts&lt;br&gt;&lt;br&gt;
│ └── api.test.ts&lt;br&gt;&lt;br&gt;
├── package. json&lt;br&gt;&lt;br&gt;
└── tsconfig. json&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Keep prompts in &lt;strong&gt;&lt;code&gt;.codestudio/prompts/&lt;/code&gt;&lt;/strong&gt; so they’re easy to find, review, and run consistently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create your prompt files
&lt;/h3&gt;

&lt;p&gt;To create Custom Prompts in Code Studio, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Chat Window.&lt;/li&gt;
&lt;li&gt;Select Prompt Files.&lt;/li&gt;
&lt;li&gt;Click + New Prompt File and choose the location based on your preference.&lt;/li&gt;
&lt;li&gt;Type prompt file name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more insights, see our &lt;a href="https://help.syncfusion.com/code-studio/reference/configure-properties/custom-prompt" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; on configuring custom prompts.&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%2F04%2FCreating-Custom-Prompt-Files-in-Code-Studio.gif" 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%2F04%2FCreating-Custom-Prompt-Files-in-Code-Studio.gif" alt="Creating Custom Prompt Files in Code Studio" width="1920" height="1020"&gt;&lt;/a&gt;&lt;/p&gt;&lt;br&gt;Creating Custom Prompt Files in Code Studio
  &lt;p&gt;&lt;/p&gt;

&lt;p&gt;Create the &lt;strong&gt;&lt;code&gt;.codestudio/prompts/&lt;/code&gt;&lt;/strong&gt; folder and create these files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;compile.prompt.md&lt;/code&gt;&lt;/strong&gt;: Compile spec to code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dashboard.prompt.md&lt;/code&gt;&lt;/strong&gt;: The dashboard specification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;review.prompt.md&lt;/code&gt;&lt;/strong&gt;: Code review against spec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;security.prompt.md&lt;/code&gt;&lt;/strong&gt;: Security audit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;test.prompt.md&lt;/code&gt;&lt;/strong&gt;: Test generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lint-spec.prompt.md&lt;/code&gt;&lt;/strong&gt;: Spec linting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can find all these sample prompt files in this &lt;a href="https://github.com/syncfusion/code-studio-library/tree/master/prompts/sample-dashboard/" rel="noopener noreferrer"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more details, refer to the blog post at &lt;a href="https://www.syncfusion.com/blogs/post/ai-agents-vs-chatbots-key-differences" rel="noopener noreferrer"&gt;Syncfusion.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codestudio</category>
      <category>aiassistant</category>
      <category>aichatbots</category>
      <category>aicodingtools</category>
    </item>
  </channel>
</rss>
