<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christopher Olsen, Author at DMC, Inc.</title>
	<atom:link href="https://static.dmcinfo.com/blog/author/christophero/feed/index.xml" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Fri, 04 Sep 2026 21:40:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://static.dmcinfo.com/wp-content/uploads/2025/04/site-icon-150x150.png</url>
	<title>Christopher Olsen, Author at DMC, Inc.</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL Server Performance Tuning: When a Join Isn&#8217;t a Join</title>
		<link>https://static.dmcinfo.com/blog/47090/sql-server-join-performance-logical-vs-physical/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Wed, 15 Jul 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Digital Workplace Solutions]]></category>
		<category><![CDATA[Server Performance]]></category>
		<category><![CDATA[SQL]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=47090</guid>

					<description><![CDATA[<p>Recently I was deploying an update to a client’s system and ran into a performance issue with the SQL script to apply database changes, an issue that hadn’t shown up in the test environment. I had to troubleshoot the slow query on the spot and solve it quickly. The cause of the problem turned out [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/47090/sql-server-join-performance-logical-vs-physical/">SQL Server Performance Tuning: When a Join Isn&#8217;t a Join</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph" id="h-recently-i-was-deploying-an-update-to-a-client-s-system-and-ran-into-a-performance-issue-with-the-sql-script-to-apply-database-changes-an-issue-that-hadn-t-shown-up-in-the-test-environment-i-had-to-troubleshoot-the-slow-query-on-the-spot-and-solved-it-quickly-the-cause-of-the-problem-turned-out-to-be-a-useful-demonstration-of-some-important-nuances-in-database-performance">Recently I was deploying an update to a client’s system and ran into a performance issue with the SQL script to apply database changes, an issue that hadn’t shown up in the test environment. I had to troubleshoot the slow query on the spot and solve it quickly. The cause of the problem turned out to be a useful demonstration of some important nuances in database performance.</p>



<p class="wp-block-paragraph">In this article, you’ll learn the difference between logical and physical joins, the factors SQL Server considers when deciding how to execute a join, and two approaches to resolving a slow SQL query.</p>



<h2 id="h-the-query" class="wp-block-heading">The Query</h2>



<p class="wp-block-paragraph">Here’s the SQL query that was running slowly. I’ve changed the table names and column names to obscure details about my client. After 5 minutes of running the query without finishing, I decided to abort it and troubleshoot it.</p>



<pre class="wp-block-preformatted">SELECT<br>&nbsp; &nbsp; v.CustomerID,<br>&nbsp; &nbsp; COUNT(v.ID) AS NumberOfVisits<br>FROM Customers AS c<br>INNER JOIN Visits AS v ON c.ID = v.CustomerID<br>WHERE v.VisitDate BETWEEN '2024-01-01' AND '2024-02-01'<br>GROUP BY v.CustomerID</pre>



<p class="wp-block-paragraph">It’s a simple query: it just joins customers to visits and selects the number of visits that each customer made within the date range specified.</p>



<p class="wp-block-paragraph">It’s important to note that the customers table is tiny, about 4,000 rows, and the visits table is large, several hundred <em>million </em>rows, so it’s understandable for this query to be performance-intensive. This exact query performed very fast in staging, but there was very little data for that date range in staging, so it makes sense for it to perform differently.</p>



<p class="wp-block-paragraph">What really confused me is that this query was a glorified copy-paste from a query that we had been using in production to run ad-hoc reports. When we ran it, it was dealing with production-scale data, so why did this version perform so poorly?</p>



<h2 id="h-the-execution-plan" class="wp-block-heading">The Execution Plan</h2>



<p class="wp-block-paragraph">The first step to troubleshooting a slow SQL query is to get an actual execution plan (if you can). This will show you exactly what steps SQL Server took to execute your query, and runtime stats such as how much time and CPU each step took, how many rows were returned by each step, etc.</p>



<p class="wp-block-paragraph">In SQL Server Management Studio (SSMS), press CTRL+M, then run your query. By default, this shows the execution plan only after the query finishes, which can be an issue for very slow queries. If you don’t want to wait, you can press CTRL + M <em>and</em> click the button near the top of SSMS labeled “Include Live Query Statistics”. This not only shows you the execution plan right away, but it also shows you the runtime stats (rows read, elapsed time of each step, etc.) in real time, so it updates as you watch it.</p>



<p class="wp-block-paragraph">I can’t share the screenshot of the execution plan from production, but here’s what it looked like in a development environment when I ran it later. Table and column names have been changed to obscure client details.</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img fetchpriority="high" decoding="async" width="1226" height="228" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1.png" alt="This image shows a SQL Server query execution plan, which is a graphical representation of how the database engine retrieves or modifies data." class="wp-image-47111" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:1200px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1.png 1226w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1-300x56.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1-1024x190.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-1-768x143.png 768w" sizes="(max-width: 1226px) 100vw, 1226px" /></figure>



<p class="wp-block-paragraph">I was profoundly surprised by this execution plan because there’s no physical join operator, despite the fact that there’s a logical join in the SQL query.</p>



<p class="wp-block-paragraph">When you go to a restaurant and order <em>steak au poivre</em>, you don’t give the waiter instructions to cook it (“crush some peppercorns, pat the steak dry, set the grill to medium-high…”). You just ask for steak au poivre, maybe specify medium-rare, and the chef makes it happen.</p>



<p class="wp-block-paragraph">SQL is the same way. SQL is a declarative language. When we write a SELECT query, we’re not writing instructions for <em>how</em> to get the data; we’re declaring <em>what data we want</em>. SQL Server needs to make decisions about how to execute it, such as which indexes to use or whether to go parallel.</p>



<p class="wp-block-paragraph">Similarly, when we write any kind of JOIN in a SQL query, what we’re writing is a <em>logical join. </em>Examples include INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, CROSS JOIN. SQL Server needs to figure out how to make your join happen, and it will do that by using one of several <em>physical join operators</em>. The most common physical join operators are Nested Loops, Hash Match, and Merge Join, so every execution plan you look at for queries involving logical joins will include at least one of those three operators.</p>



<h2 id="h-when-a-logical-join-does-not-require-a-physical-join" class="wp-block-heading">When a Logical Join Does Not Require a Physical Join</h2>



<p class="wp-block-paragraph">Our slow SQL query is an exception to that rule. Our query clearly has a logical join (INNER JOIN) between two tables, but the SQL Server chooses an execution plan with no physical joins.</p>



<p class="wp-block-paragraph">Why?</p>



<p class="wp-block-paragraph">The first thing that came to mind is that the SQL query, despite joining the Customers table and the Visits table, only includes fields from the Visits table in the SELECT clause, WHERE clause, and GROUP BY clause. We’re not using any fields from the Customers table other than its ID field as the join key.</p>



<p class="wp-block-paragraph">That’s part of the explanation, but it doesn’t explain all of it. The whole point of an INNER JOIN is to return only the records that have matches in both tables. So, even though we’re only SELECTing fields from one table, SQL Server still has to determine whether a given key value is present in <em>both</em> tables. How can it do that without performing a physical join?</p>



<p class="wp-block-paragraph">The answer is the foreign key reference that <em>enforces</em> the relationship between these two tables. Specifically, we have a foreign key on the Visits table on the CustomerID field, referencing the ID field of the Customers table. This foreign key enforces that no record can exist in the Visits table without a corresponding record in the Customers table.</p>



<p class="wp-block-paragraph">So, let’s look at this from SQL Server’s perspective:</p>



<ol class="wp-block-list">
<li>The user is asking me to perform an INNER JOIN.</li>



<li>He’s asking me to filter and group based on fields from only one of the tables.</li>



<li>He’s only asking for fields from that same table.</li>



<li>I know for a <em>fact</em> that all records in Visits have a corresponding Customer record.</li>



<li>Why the heck would I bother with a physical join? I’ll just read from the Visits table and ignore Customers.</li>
</ol>



<p class="wp-block-paragraph">Physical joins can be memory-intensive and time-consuming. Generally, if we can avoid a physical join, we’ll get our results faster. So, the execution plan makes sense.</p>



<p class="wp-block-paragraph">This isn’t just theory, either. I tested it in a development environment by removing the foreign key and running the SQL query. As expected, the execution plan included a physical join. Adding the foreign key constraint back to that field and running the query again resulted once more in an execution plan with no joins.</p>



<h2 id="h-why-is-the-query-slow-if-we-re-avoiding-a-join" class="wp-block-heading">Why Is the Query Slow If We&#8217;re Avoiding a Join?</h2>



<p class="wp-block-paragraph">Now that we’ve identified why the execution plan has no physical join, let’s circle back to the problem at hand: the query is slow. Why is it slow, and how do we fix it?</p>



<p class="wp-block-paragraph">Let’s take another look at the execution plan, specifically the first step.</p>



<figure class="wp-block-image size-full has-custom-border"><img decoding="async" width="325" height="197" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-2.png" alt="This image shows an SQL Server execution plan indicating a NonClustered Index Scan on a table named &quot;Visits&quot;." class="wp-image-47543" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-2.png 325w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-2-300x182.png 300w" sizes="(max-width: 325px) 100vw, 325px" /></figure>



<p class="wp-block-paragraph">SQL Server is choosing to do an index scan. That means it starts at the very “top” of the index and reads through it to the end. It chose this index, “IX_Visits_CustomerID_VisitDate,” because it includes all the fields our query needs. This index contains a couple of fields of each row, which means SQL Server can fit more rows on a single page of data, allowing it to read more rows per page.</p>



<p class="wp-block-paragraph">However, the order of the keys in this index is hurting performance. CustomerID is the first key, and VisitDate is the second key. That means the rows in the index are sorted first by CustomerID,<em> then</em> by VisitDate. If it was the other way around, all the rows that match our WHERE clause would be right next to each other in the index and SQL Server would be able to do a “seek” to read only the minimal pages it needs to get the data we’re asking for.</p>



<h2 id="h-solution-1-add-a-new-index" class="wp-block-heading">Solution 1: Add a New Index</h2>



<p class="wp-block-paragraph">The most obvious solution is to add a new index with VisitDate as the first key and CustomerID as the second. Here’s the resulting execution plan:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="820" height="125" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3.png" alt="SQL Server execution plan showing query processing steps, where a NonClustered Index Seek accounts for 97% of the query cost, with parallelism and hash aggregation used to improve performance and group results." class="wp-image-47131" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;aspect-ratio:6.560177671478603;width:925px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3.png 820w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3-300x46.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-3-768x117.png 768w" sizes="(max-width: 820px) 100vw, 820px" /></figure>



<p class="wp-block-paragraph">Even in this development environment, where the data is much smaller, this resulted in a 100x speed boost—the query finished in about 0.1 seconds instead of 10 seconds.</p>



<p class="wp-block-paragraph">However, indexes aren’t free, especially on large tables. The more indexes you have on a table, the longer it takes to write to that table when rows are updated or inserted. This table already has more indexes than I’d like, so I’d prefer to avoid adding a new one.</p>



<p class="wp-block-paragraph">Additionally, this query runs once a month as part of an automated report, so it feels silly to add a whole index just to make this one query faster.</p>



<p class="wp-block-paragraph">When we solve a slow query by adding, modifying, or removing indexes, that’s called “index tuning”.</p>



<h2 id="h-solution-2-change-the-query" class="wp-block-heading">Solution 2: Change the Query</h2>



<p class="wp-block-paragraph">In general, if you can make a query faster by rewriting the query rather than adding indexes, you should do so. I ran the old version of the query that was previously used to generate the ad-hoc reports and found that it used a physical join operator. The reason was simple: the original query joined the Visits table to a temp table representing Customers rather than the table itself. The Visits table obviously did not have a foreign key reference to this temp table, so it was forced to perform a physical join.</p>



<p class="wp-block-paragraph">For the new version of the query, I forced a physical join by selecting a field from the Customers table instead of the Visits table:</p>



<pre class="wp-block-preformatted">SELECT<br>&nbsp; &nbsp; c.ID AS CustomerID,<br>&nbsp; &nbsp; COUNT(v.ID) AS NumberOfVisits<br>FROM Customers AS c<br>INNER JOIN Visits AS v ON c.ID = v.CustomerID<br>WHERE v.VisitDate BETWEEN '2024-01-01' AND '2024-02-01'<br>GROUP BY c.ID</pre>



<p class="wp-block-paragraph">In this version of the query, I’m using the Customers table’s ID field for both the GROUP BY and one of the fields in the SELECT, while still using fields from the Visits table in the WHERE clause and the SELECT clause. This forces SQL Server to do a physical join, and the execution plan looks like this:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="1330" height="314" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4.png" alt="SQL Server query execution plan showing a parallel execution strategy with Hash Match, Nested Loops, and Index Scan/Seek operators, where a Nested Loops join contributes a significant portion of the query cost." class="wp-image-47133" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:1200px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4.png 1330w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4-300x71.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4-1024x242.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-4-768x181.png 768w" sizes="(max-width: 1330px) 100vw, 1330px" /></figure>



<p class="wp-block-paragraph">In my development environment, the query takes 0.455. In production, it takes a couple of seconds, which is fast enough for our purposes. It runs as part of an automated report and must complete within 30 seconds to avoid timeouts.</p>



<p class="wp-block-paragraph">Let’s zoom in on that physical join operator:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="598" height="299" src="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-5.png" alt="SQL Server execution plan highlighting an Index Seek on the Visits table that accounts for 95% of the total query cost, with additional Nested Loops join and Clustered Index Scan operations processing a large volume of rows." class="wp-image-47134" style="border-top-left-radius:20px;border-top-right-radius:20px;border-bottom-left-radius:20px;border-bottom-right-radius:20px;width:552px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-5.png 598w, https://static.dmcinfo.com/wp-content/uploads/2026/07/sql-server-joins-5-300x150.png 300w" sizes="(max-width: 598px) 100vw, 598px" /></figure>



<p class="wp-block-paragraph">SQL Server chose the Nested Loops join. This physical join operator is very fast and efficient when you have a small result set on one side of the join compared to the result set on the other side. We have just a few thousand rows in our Customers table and a couple hundred million rows in our Visits table, so Nested Loops is the right choice.</p>



<p class="wp-block-paragraph">The way this join works is that SQL Server loops through the smaller data set, the Customers table, and for each row, performs a seek on the Visits table to get the rows corresponding to that CustomerID value. These seeks are fast because CustomerID is the first key in the index.</p>



<p class="wp-block-paragraph">Because this version of the query meets our performance needs without requiring a change to the database’s structure (e.g., by adding an index), it’s the solution we chose.</p>



<p class="wp-block-paragraph">When we solve a slow query by rewriting or tweaking the query text itself, as we did here, that’s called “query tuning”.</p>



<h2 id="h-the-takeaway" class="wp-block-heading">The Takeaway</h2>



<p class="wp-block-paragraph">What have we learned? We can take away the following lessons about SQL Server:</p>



<ol class="wp-block-list">
<li>SQL Server does not always need to perform a physical join to execute a logical join.</li>



<li>Foreign key references, or lack thereof, can impact whether SQL Server performs a physical join.</li>



<li>If you only use fields from one table in your SELECT, WHERE, ORDER BY, or GROUP BY clauses, SQL Server might not bother with a physical join.</li>



<li>When trying to speed up a slow query, you should first try to rewrite the query before resorting to adding or changing indexes.</li>



<li>Sometimes an unusual solution is the best one. In rare cases, adding a join can improve performance.</li>
</ol>



<p class="wp-block-paragraph">This was a deep dive into a fascinating detail of how SQL Server decides when to do a physical join and how that impacts performance.</p>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--50)">If you rely on SQL Server or Azure SQL for your business-critical applications, you need database experts who can calmly work under pressure to solve tricky problems like the one described in this article.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Have an upcoming project? DMC can help your team take the next step.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Take your database performance to the next level with engineering solutions from DMC and learn more about <a href="https://static.dmcinfo.com/services/digital-workplace-solutions/">Digital Workspace Solutions</a>.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/47090/sql-server-join-performance-logical-vs-physical/">SQL Server Performance Tuning: When a Join Isn&#8217;t a Join</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Next.js 16 + Material UI Setup Guide for App Router + RSC: Dev Diary #2</title>
		<link>https://static.dmcinfo.com/blog/44831/material-ui-nextjs-16-app-router/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Mon, 01 Jun 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[User Interface Design]]></category>
		<category><![CDATA[Next.js]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=44831</guid>

					<description><![CDATA[<p>In Part 1 of this two-part series, we walked through scaffolding a brand-new React application with Next.js. The result was a bare bones application with no styling to speak of. Early in any web application project, you have an important decision to make: are you going to build the look and feel of your app [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/44831/material-ui-nextjs-16-app-router/">Next.js 16 + Material UI Setup Guide for App Router + RSC: Dev Diary #2</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In <a href="https://static.dmcinfo.com/blog/41208/building-a-dba-dashboard-with-react-and-next-js-part-1/">Part 1</a> of this two-part series, we walked through scaffolding a brand-new React application with Next.js. The result was a bare bones application with no styling to speak of. Early in any web application project, you have an important decision to make: are you going to build the look and feel of your app from scratch with custom CSS and JavaScript, or are you going to leverage a third-party solution to achieve a more professional-looking UI than you would with a custom solution and with less development effort? Most people choose the latter, and we will too.</p>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--20)">At DMC, our preferred UI library is Material UI, a component library maintained by Google. I first used Material UI nearly a decade ago, before it even reached version 1.0. Since then, the library has evolved significantly and now includes first-class support for Next.js and React Server Components. In this post, we&#8217;ll walk through how to add Material UI to a new Next.js 16 application. At the time of writing, Material UI v9.0.0 is the version in use.</p>



<h2 class="wp-block-heading" id="h-installing-the-npm-packages-for-material-ui">Installing the NPM Packages for Material UI</h2>



<p class="wp-block-paragraph">First, run the following command to install the core Material UI packages using Yarn:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>yarn add @mui/material @emotion/react @emotion/styled


</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">yarn add @mui/material @emotion/react @emotion/styled</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">The Material UI documentation recommends installing <code>@fontsource/roboto</code> to use the Roboto font in your application. However, &nbsp;at the time of writing this post, that package causes the TypeScript server in my project to crash, resulting in the loss of IntelliSense support in Visual Studio Code. Because of this, I recommend skipping that package. Later in this post, we’ll import the Roboto font using a different approach.</p>



<p class="wp-block-paragraph">Next, run the following command to install the packages required to integrate Material UI with Next.js App Router:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>yarn add @mui/material-nextjs @emotion/cache


</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">yarn add @mui/material-nextjs @emotion/cache</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<h2 class="wp-block-heading" id="h-setting-up-a-material-ui-theme" style="padding-top:var(--wp--preset--spacing--30)">Setting Up a Material UI Theme</h2>



<p class="wp-block-paragraph">One of Material UI&#8217;s most useful features is its customizable themes. Material UI includes a default theme that can be extensively customized—everything from spacing, color palettes, and even border radius can be modified based on your design sensibilities.</p>



<p class="wp-block-paragraph">For this application, we&#8217;ll primarily use the default &nbsp;Material UI theme while enabling built-in light/dark mode functionality. We can initialize the theme using the <code>createTheme()</code> function:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// src/theme.tsx
"use client";
import { createTheme } from "@mui/material/styles";

const theme = createTheme({
  typography: {
    fontFamily: "var(--font-roboto)",
  },
  cssVariables: {
    colorSchemeSelector: "class",
  },
  colorSchemes: {
    dark: true,
  },
});

export default theme;</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// src/theme.tsx</span></span>
<span class="line"><span style="color: #CE9178">&quot;use client&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import { createTheme } from </span><span style="color: #CE9178">&quot;@mui/material/styles&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> theme = </span><span style="color: #DCDCAA">createTheme</span><span style="color: #D4D4D4">({</span></span>
<span class="line"><span style="color: #D4D4D4">  typography: {</span></span>
<span class="line"><span style="color: #D4D4D4">    fontFamily: </span><span style="color: #CE9178">&quot;var(--font-roboto)&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  },</span></span>
<span class="line"><span style="color: #D4D4D4">  cssVariables: {</span></span>
<span class="line"><span style="color: #D4D4D4">    colorSchemeSelector: </span><span style="color: #CE9178">&quot;class&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  },</span></span>
<span class="line"><span style="color: #D4D4D4">  colorSchemes: {</span></span>
<span class="line"><span style="color: #D4D4D4">    dark: </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  },</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">export default theme;</span></span></code></pre></div>



<p class="wp-block-paragraph">This theme configuration does three things:</p>



<ol class="wp-block-list">
<li>Sets Roboto as the default font family for all Typography components.</li>



<li>Enables dark mode support. Light mode is enabled by default.</li>



<li>Enables CSS variables and configures the colorSchemeSelector to use CSS classes.</li>
</ol>



<p class="wp-block-paragraph">The final configuration helps prevent SSR flickering, which can occur because the server cannot detect a user’s preferred color mode during the initial render. With this setup, Material UI applies either a light or dark class to the &lt;html&gt; element on the client side and uses CSS variables to apply the appropriate theme colors. This approach provides a smoother transition between server-side rendering and client-side hydration while maintaining support for light and dark themes.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" width="912" height="520" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-1.png" alt="Screenshot of a JavaScript code snippet manages theme switching between light and dark modes for a webpage." class="wp-image-44851" style="width:850px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-1.png 912w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-1-300x171.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-1-768x438.png 768w" sizes="(max-width: 912px) 100vw, 912px" /></figure>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" width="1055" height="292" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-2.png" alt="Image displays CSS code using the Material UI (mui) framework to style an app bar component when the application is in dark mode." class="wp-image-44852" style="aspect-ratio:3.6131774707757707;width:850px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-2.png 1055w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-2-300x83.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-2-1024x283.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-2-768x213.png 768w" sizes="(max-width: 1055px) 100vw, 1055px" /></figure>



<h2 class="wp-block-heading" id="h-color-mode-picker" style="padding-top:var(--wp--preset--spacing--30)">Color Mode Picker</h2>



<p class="wp-block-paragraph">Speaking of color mode (light/dark), it can be nice to offer users a way to switch between light mode and dark mode. With the way we&#8217;ve configured our theme, Material UI will automatically set the color mode based on the user&#8217;s system preference, set in their web browser. So if a user has configured &#8220;dark&#8221; mode in Chrome, for example, when they visit our application, they&#8217;ll automatically get the dark-mode version of our UI.</p>



<p class="wp-block-paragraph">Some users may want dark mode specifically for our application and light mode elsewhere, so to provide that flexibility, we&#8217;ll build a simple color mode selector. For this, we&#8217;ll use the tools Material UI provides, including input and label components, along with tools for customizing them.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// colorThemePicker.tsx
"use client";
import FormControl from "@mui/material/FormControl";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import { useColorScheme } from "@mui/material/styles";
import { FormLabel } from "@mui/material";

export default function ColorThemePicker() {
  const { mode, setMode } = useColorScheme();
  if (!mode) {
    return null;
  }
  const handleChange = (event: SelectChangeEvent) => {
    setMode(event.target.value as "system" | "light" | "dark");
  };

  return (
    &lt;FormControl
      sx={{
        display: "flex",
        flexDirection: "row",
        alignItems: "center",
        gap: 1,
      }}
    >
      &lt;FormLabel id="theme-select-label">Theme&lt;/FormLabel>
      &lt;Select
        labelId="theme-select-label"
        aria-labelledby="theme-select-label"
        id="theme-select"
        value={mode}
        label="Theme"
        onChange={handleChange}
        sx={{ minWidth: "10rem" }}
      >
        &lt;MenuItem value="system">System&lt;/MenuItem>
        &lt;MenuItem value="light">Light&lt;/MenuItem>
        &lt;MenuItem value="dark">Dark&lt;/MenuItem>
      &lt;/Select>
    &lt;/FormControl>
  );
}

</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// colorThemePicker.tsx</span></span>
<span class="line"><span style="color: #CE9178">&quot;use client&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import FormControl from </span><span style="color: #CE9178">&quot;@mui/material/FormControl&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import Select, { SelectChangeEvent } from </span><span style="color: #CE9178">&quot;@mui/material/Select&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import MenuItem from </span><span style="color: #CE9178">&quot;@mui/material/MenuItem&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import { useColorScheme } from </span><span style="color: #CE9178">&quot;@mui/material/styles&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import { FormLabel } from </span><span style="color: #CE9178">&quot;@mui/material&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">export default function </span><span style="color: #DCDCAA">ColorThemePicker</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> { mode, setMode } = </span><span style="color: #DCDCAA">useColorScheme</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!mode) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> null;</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> handleChange = (event: SelectChangeEvent) =&gt; {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">setMode</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">event</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">target</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4"> as </span><span style="color: #CE9178">&quot;system&quot;</span><span style="color: #D4D4D4"> | </span><span style="color: #CE9178">&quot;light&quot;</span><span style="color: #D4D4D4"> | </span><span style="color: #CE9178">&quot;dark&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;FormControl</span></span>
<span class="line"><span style="color: #D4D4D4">      sx={{</span></span>
<span class="line"><span style="color: #D4D4D4">        display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        flexDirection: </span><span style="color: #CE9178">&quot;row&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        alignItems: </span><span style="color: #CE9178">&quot;center&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        gap: </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      }}</span></span>
<span class="line"><span style="color: #D4D4D4">    &gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;FormLabel id=</span><span style="color: #CE9178">&quot;theme-select-label&quot;</span><span style="color: #D4D4D4">&gt;Theme&lt;/FormLabel&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;Select</span></span>
<span class="line"><span style="color: #D4D4D4">        labelId=</span><span style="color: #CE9178">&quot;theme-select-label&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        aria-labelledby=</span><span style="color: #CE9178">&quot;theme-select-label&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        id=</span><span style="color: #CE9178">&quot;theme-select&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        value={mode}</span></span>
<span class="line"><span style="color: #D4D4D4">        label=</span><span style="color: #CE9178">&quot;Theme&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        onChange={handleChange}</span></span>
<span class="line"><span style="color: #D4D4D4">        sx={{ minWidth: </span><span style="color: #CE9178">&quot;10rem&quot;</span><span style="color: #D4D4D4"> }}</span></span>
<span class="line"><span style="color: #D4D4D4">      &gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;MenuItem value=</span><span style="color: #CE9178">&quot;system&quot;</span><span style="color: #D4D4D4">&gt;System&lt;/MenuItem&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;MenuItem value=</span><span style="color: #CE9178">&quot;light&quot;</span><span style="color: #D4D4D4">&gt;Light&lt;/MenuItem&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;MenuItem value=</span><span style="color: #CE9178">&quot;dark&quot;</span><span style="color: #D4D4D4">&gt;Dark&lt;/MenuItem&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/Select&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/FormControl&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">I&#8217;ll explain this from the top down.</p>



<p class="wp-block-paragraph">We need a piece of state to track the user&#8217;s color mode selection: light, dark, or system (whatever they&#8217;ve selected in their browser settings). We also need a <strong>setter</strong> for that state so that we can update it based on the user&#8217;s actions. For that, we use a hook provided by Material UI: <a href="https://mui.com/material-ui/customization/dark-mode/#toggling-color-mode">useColorScheme()</a>. The <code>mode</code> returned by this hook is always undefined on the first render, so we handle that with a simple falsiness check:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>if(!mode) {
  return null;
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">if</span><span style="color: #D4D4D4">(!mode) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> null;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">Next, we have our return statement where we declare the UI that this component should render. We&#8217;re making use of a few components from Material UI:</p>



<ul class="wp-block-list">
<li><a href="https://mui.com/material-ui/api/form-control/" target="_blank" rel="noreferrer noopener">FormControl</a></li>



<li><a href="https://mui.com/material-ui/api/form-label/" target="_blank" rel="noreferrer noopener">FormLabel</a></li>



<li><a href="https://mui.com/material-ui/react-select/" target="_blank" rel="noreferrer noopener">Select and MenuItem</a></li>
</ul>



<p class="wp-block-paragraph">The latter two are kind of self-explanatory if you have a basic background in HTML. FormControl is a nice-to-have component that manages some state related to the input element that it wraps, such as focused state, filled state, error state, required state, etc.</p>



<p class="wp-block-paragraph">Something I want to point out is the use of the sx prop on the FormControl component. This is a CSS-in-JS styling solution from Material UI, intended for one-off customization. Our application has precisely one color theme picker, so it easily fits the definition of a one-off customization. You can read more about this system in the <a href="https://mui.com/system/getting-started/usage" target="_blank" rel="noreferrer noopener">Material UI documentation</a>.</p>



<p class="wp-block-paragraph">The purpose served by our use of the <code>sx</code> prop here is to apply some flexbox CSS properties to our <code>FormControl</code> to make its child elements (the <code>FormLabel</code> and <code>Select</code> components) appear side by side instead of displaying the form label above the select component.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;FormControl
  sx={{
    display: "flex",
    flexDirection: "row",
    alignItems: "center",
    gap: 1,
  }}
>/
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;FormControl</span></span>
<span class="line"><span style="color: #D4D4D4">  sx={{</span></span>
<span class="line"><span style="color: #D4D4D4">    display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    flexDirection: </span><span style="color: #CE9178">&quot;row&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    alignItems: </span><span style="color: #CE9178">&quot;center&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    gap: </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  }}</span></span>
<span class="line"><span style="color: #D4D4D4">&gt;/</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">Under the hood, a CSS selector is generated and applied to the rendered DOM element, and a &lt;style&gt; tag containing the corresponding CSS rule(s) is inserted into the DOM (see the screenshot below). So the result is similar to what we would get with a style sheet, except that it is generated at runtime after the initial HTML document is served to the client, which introduces some overhead for applying style rules.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" width="1377" height="158" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-3.png" alt="Image displaying a CSS style block generated by the Material-UI (MUI) library, specifically targeting a form label component." class="wp-image-44854" style="width:850px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-3.png 1377w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-3-300x34.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-3-1024x117.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-3-768x88.png 768w" sizes="(max-width: 1377px) 100vw, 1377px" /></figure>



<p class="wp-block-paragraph">In short, the trade-off is simpler code that&#8217;s easier to understand in exchange for a performance drop: up to 3x slower render time for 1,000 components, according to the documentation, which means you have to be rendering a lot of items to get a noticeable performance difference.</p>



<p class="wp-block-paragraph">Here&#8217;s what our color scheme picker looks like when rendered on the page:</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="274" height="227" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-4.png" alt="Image displaying a drop-down menu for selecting a user interface Theme with &quot;Light&quot; selected." class="wp-image-44855" style="width:280px"/></figure>



<p class="wp-block-paragraph">And in dark mode:</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="286" height="223" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-5.png" alt="Image displaying a drop-down menu for selecting a user interface Theme with &quot;Dark&quot; selected." class="wp-image-44856" style="width:280px"/></figure>



<p class="wp-block-paragraph">Not bad!</p>



<h2 class="wp-block-heading" id="h-client-components-and-building-a-nav-bar" style="padding-top:var(--wp--preset--spacing--30)">Client Components and Building a Nav Bar</h2>



<p class="wp-block-paragraph">Material UI has a <code>&lt;Link&gt;</code> component that makes it easy to render a professional-looking link on a web page. Since we&#8217;re using Next.js, we also want to use the <code>&lt;Link&gt;</code> component, which handles both server- and client-side routing. We&#8217;ll do this by providing the Next.js <code>&lt;Link&gt;</code> to the Material UI <code>&lt;Link&gt;</code> &#8220;component&#8221; prop, which essentially tells Material UI, &#8220;use this as the underlying component that you then attach all your fancy styles to.&#8221;</p>



<p class="wp-block-paragraph">However, with Next.js 16, this can result in an error: &#8220;Functions cannot be passed directly to Client Component&#8221;. As outlined in the Material UI documentation for Next.js integration, we can work around this by creating a client-side wrapper component for the Next.js <code>&lt;Link&gt;</code> component.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// src\app\_components\link.tsx
'use client';
import Link, { LinkProps } from 'next/link';

export default Link;
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// src\app\_components\link.tsx</span></span>
<span class="line"><span style="color: #CE9178">&apos;use client&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import Link, { LinkProps } from </span><span style="color: #CE9178">&apos;next/link&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">export default Link;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">This will come in handy when building our top nav bar for this application, which we can get started on:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// src\app\_components\topNavBar.tsx 
import NextLink from "./link";
// (rest of import section omitted for brevity)

function TopNavBar() {
  return (
    &lt;AppBar position="static">
      &lt;Container
        maxWidth="xl"
        sx={{ display: "flex", justifyContent: "space-between" }}
      >
        &lt;Box
          sx={{
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
          }}
          component="nav"
        >
          &lt;Link component={NextLink} href="/" color="inherit" underline="none">
            &lt;Typography
              variant="body1"
              component="span"
              sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
            >
              &lt;Icon>home&lt;/Icon>
              Home
            &lt;/Typography>
          &lt;/Link>
        &lt;/Box>
        &lt;ColorThemePicker />
      &lt;/Container>
    &lt;/AppBar>
  );
}

export default TopNavBar;
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// src\app\_components\topNavBar.tsx </span></span>
<span class="line"><span style="color: #D4D4D4">import NextLink from </span><span style="color: #CE9178">&quot;./link&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #6A9955">// (rest of import section omitted for brevity)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">function </span><span style="color: #DCDCAA">TopNavBar</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;AppBar position=</span><span style="color: #CE9178">&quot;static&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;Container</span></span>
<span class="line"><span style="color: #D4D4D4">        maxWidth=</span><span style="color: #CE9178">&quot;xl&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        sx={{ display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">, justifyContent: </span><span style="color: #CE9178">&quot;space-between&quot;</span><span style="color: #D4D4D4"> }}</span></span>
<span class="line"><span style="color: #D4D4D4">      &gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;Box</span></span>
<span class="line"><span style="color: #D4D4D4">          sx={{</span></span>
<span class="line"><span style="color: #D4D4D4">            display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            justifyContent: </span><span style="color: #CE9178">&quot;space-between&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">            alignItems: </span><span style="color: #CE9178">&quot;center&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">          }}</span></span>
<span class="line"><span style="color: #D4D4D4">          component=</span><span style="color: #CE9178">&quot;nav&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        &gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          &lt;Link component={NextLink} href=</span><span style="color: #CE9178">&quot;/&quot;</span><span style="color: #D4D4D4"> color=</span><span style="color: #CE9178">&quot;inherit&quot;</span><span style="color: #D4D4D4"> underline=</span><span style="color: #CE9178">&quot;none&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;Typography</span></span>
<span class="line"><span style="color: #D4D4D4">              variant=</span><span style="color: #CE9178">&quot;body1&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">              component=</span><span style="color: #CE9178">&quot;span&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">              sx={{ display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">, alignItems: </span><span style="color: #CE9178">&quot;center&quot;</span><span style="color: #D4D4D4">, gap: </span><span style="color: #B5CEA8">0.5</span><span style="color: #D4D4D4"> }}</span></span>
<span class="line"><span style="color: #D4D4D4">            &gt;</span></span>
<span class="line"><span style="color: #D4D4D4">              &lt;Icon&gt;home&lt;/Icon&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">              Home</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/Typography&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          &lt;/Link&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/Box&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;ColorThemePicker /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/Container&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/AppBar&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">export default TopNavBar;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">For our top nav bar, we&#8217;re using the <a href="https://mui.com/material-ui/react-app-bar" target="_blank" rel="noreferrer noopener">AppBar</a> component from Material UI. This component looks great at the top of the screen and is an ideal place for top-level links, action buttons, menus, a login/logout button, etc.</p>



<p class="wp-block-paragraph">We&#8217;re putting a couple of elements into our top nav bar to start out:</p>



<ol class="wp-block-list">
<li>A list of links (currently just a link to the home page)</li>



<li>The color theme picker (this will eventually be replaced with a &#8220;hamburger button&#8221; that opens a modal dialog with settings/options)</li>
</ol>



<p class="wp-block-paragraph">I want these two components to be evenly spaced across the app bar, which sounds like a job for flexbox. To make that happen, I need to wrap them in a component, and <a href="https://mui.com/material-ui/react-container" target="_blank" rel="noreferrer noopener">Material UI&#8217;s Container</a> component fits the bill. I&#8217;m using Container because it&#8217;s a responsive component—we can use the <code>maxWidth</code> prop to define how <em>wide</em> it&#8217;s allowed to get based on the screen size. I want this nav bar to resize responsively all the way up to large desktop screens, so I&#8217;m providing &#8220;xl&#8221; as the value here &#8211; the width of the container will automatically adjust to smaller screen sizes.</p>



<p class="wp-block-paragraph">We can use the <code>sx</code> prop here to provide custom styling rules for this component—in this case, that would be using the &#8220;display&#8221; and &#8220;justifyContent&#8221; CSS properties to declare that the children of this component should be spaced evenly across the width of this container.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;Container
  maxWidth="xl"
  sx={{ display: "flex", justifyContent: "space-between" }}
>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;Container</span></span>
<span class="line"><span style="color: #D4D4D4">  maxWidth=</span><span style="color: #CE9178">&quot;xl&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">  sx={{ display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">, justifyContent: </span><span style="color: #CE9178">&quot;space-between&quot;</span><span style="color: #D4D4D4"> }}</span></span>
<span class="line"><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">To follow HTML best practices, we want to wrap our navigation links in a semantic <code>&lt;nav&gt;</code> element. But we also want to make sure that the links themselves are evenly spaced, so we need to apply some CSS rules to this <code>&lt;nav&gt;</code> element. The best way to accomplish this with Material UI is to use the generic <a href="https://mui.com/material-ui/react-box/" target="_blank" rel="noreferrer noopener">Box</a> component.</p>



<p class="wp-block-paragraph">Whenever you need to wrap other components with a containing element and apply style rules to it, <code>&lt;Box&gt;</code> is the go-to component. And we can tell Material UI which HTML element to render in the DOM by using the component prop, so we&#8217;ll pass the value &#8220;nav&#8221; to that prop to ensure it renders as a proper nav element. And again, we&#8217;re using the sx prop to define some basic flexbox properties and ensure that our link elements inside this box are spaced evenly and positioned nicely.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;Box
  sx={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
  }}
  component="nav"
>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;Box</span></span>
<span class="line"><span style="color: #D4D4D4">  sx={{</span></span>
<span class="line"><span style="color: #D4D4D4">    display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    justifyContent: </span><span style="color: #CE9178">&quot;space-between&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    alignItems: </span><span style="color: #CE9178">&quot;center&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  }}</span></span>
<span class="line"><span style="color: #D4D4D4">  component=</span><span style="color: #CE9178">&quot;nav&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">To start with, we&#8217;re going to only have one link in our nav bar: &#8220;Home&#8221;. The simplest version of this would just be to use Material UI&#8217;s Link component, pass my wrapper for the Next.js Link to the &#8220;component&#8221; prop, and, of course, set href to &#8220;/&#8221;. Like the following:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;Link component={NextLink} href="/" color="inherit" underline="none">
  Home
&lt;/Link>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;Link component={NextLink} href=</span><span style="color: #CE9178">&quot;/&quot;</span><span style="color: #D4D4D4"> color=</span><span style="color: #CE9178">&quot;inherit&quot;</span><span style="color: #D4D4D4"> underline=</span><span style="color: #CE9178">&quot;none&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  Home</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/Link&gt;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">But I also want to add a nifty little house icon to this link. Because I want this link to be inviting: &#8220;Come on home, you&#8217;re tired, and we have a nice fire going. You can take off your socks and cozy up by the fireplace with some hot cocoa and a grilled cheese sandwich.&#8221;</p>



<p class="wp-block-paragraph">Material UI makes that easy with its <a href="https://mui.com/material-ui/icons/" target="_blank" rel="noreferrer noopener">Icon</a> component. You can choose SVG or Font for the icons. To get off to a quick and easy start, I recommend the Font option. Later in this post, I&#8217;ll show how to enable that, but here&#8217;s what it&#8217;s going to look like in this component:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;Link component={NextLink} href="/" color="inherit" underline="none">
  &lt;Icon>home&lt;/Icon>
  Home
&lt;/Link>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;Link component={NextLink} href=</span><span style="color: #CE9178">&quot;/&quot;</span><span style="color: #D4D4D4"> color=</span><span style="color: #CE9178">&quot;inherit&quot;</span><span style="color: #D4D4D4"> underline=</span><span style="color: #CE9178">&quot;none&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;Icon&gt;home&lt;/Icon&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  Home</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/Link&gt;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">This ends up looking like the following when rendered on the page:</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="188" height="74" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-6.png" alt="Screenshot of &quot;home&quot; link and &quot;home&quot; icon. The icon is positioned awkwardly off-center, making it look crooked." class="wp-image-44860" style="aspect-ratio:2.540772532188841;width:175px;height:auto"/></figure>



<p class="wp-block-paragraph">Oops! That icon is crooked! Or more accurately, it&#8217;s not vertically centered, so it looks awkward next to the text &#8220;Home&#8221;. We&#8217;ll fix that by applying some flexbox styling to the Link component:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;Link
  component={NextLink}
  href="/"
  color="inherit"
  underline="none"
  sx={{ display: "flex", alignItems: "center", gap: "0.5rem" }}
>
  &lt;Icon>home&lt;/Icon>
  Home
&lt;/Link>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;Link</span></span>
<span class="line"><span style="color: #D4D4D4">  component={NextLink}</span></span>
<span class="line"><span style="color: #D4D4D4">  href=</span><span style="color: #CE9178">&quot;/&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">  color=</span><span style="color: #CE9178">&quot;inherit&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">  underline=</span><span style="color: #CE9178">&quot;none&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">  sx={{ display: </span><span style="color: #CE9178">&quot;flex&quot;</span><span style="color: #D4D4D4">, alignItems: </span><span style="color: #CE9178">&quot;center&quot;</span><span style="color: #D4D4D4">, gap: </span><span style="color: #CE9178">&quot;0.5rem&quot;</span><span style="color: #D4D4D4"> }}</span></span>
<span class="line"><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;Icon&gt;home&lt;/Icon&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  Home</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/Link&gt;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">The result looks so much better!</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="131" height="58" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-7.png" alt="Screenshot of &quot;home&quot; link and &quot;home icon&quot;, vertically aligned to look neater." class="wp-image-44861" style="aspect-ratio:2.2589298319920936;object-fit:cover;width:175px"/></figure>



<p class="wp-block-paragraph">Finally, after the nav links themselves, we have the color theme picker. Altogether, our nav bar looks like the following screenshot:</p>



<figure class="wp-block-image aligncenter size-full"><img decoding="async" width="1230" height="232" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-8.png" alt="Screenshot of top nav bar." class="wp-image-44863" style="aspect-ratio:5.30214424951267" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-8.png 1230w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-8-300x57.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-8-1024x193.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-8-768x145.png 768w" sizes="(max-width: 1230px) 100vw, 1230px" /></figure>



<p class="wp-block-paragraph">Not bad, at least to start.</p>



<h2 class="wp-block-heading" id="h-updating-the-root-layout" style="padding-top:var(--wp--preset--spacing--30)">Updating the Root Layout</h2>



<p class="wp-block-paragraph">Now we&#8217;re ready to update our root layout (/src/app/layout.tsx) to use Material UI in our application.</p>



<h3 class="wp-block-heading" id="h-roboto-font-set-up" style="padding-top:var(--wp--preset--spacing--30)">Roboto Font Set Up</h3>



<p class="wp-block-paragraph">To enable using the Roboto font as the default font for our app, we need to use <a href="https://nextjs.org/docs/app/getting-started/fonts" target="_blank" rel="noreferrer noopener">Next.js font optimization</a>. The result of this step is that we end up self-hosting the Roboto font from Google:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>const roboto = Roboto({
  weight: &#91;"300", "400", "500", "700"&#93;,
  subsets: &#91;"latin"&#93;,
  display: "swap",
  variable: "--font-roboto",
});
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> roboto = </span><span style="color: #DCDCAA">Roboto</span><span style="color: #D4D4D4">({</span></span>
<span class="line"><span style="color: #D4D4D4">  weight: &#91;</span><span style="color: #CE9178">&quot;300&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;400&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;500&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;700&quot;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">  subsets: &#91;</span><span style="color: #CE9178">&quot;latin&quot;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">  display: </span><span style="color: #CE9178">&quot;swap&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  variable: </span><span style="color: #CE9178">&quot;--font-roboto&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">With that set up, we can give our HTML (root) element a new CSS class that will help give every typography element the Roboto font:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;html lang="en" className={roboto.variable} suppressHydrationWarning>

</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;html lang=</span><span style="color: #CE9178">&quot;en&quot;</span><span style="color: #D4D4D4"> className={</span><span style="color: #9CDCFE">roboto</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">variable</span><span style="color: #D4D4D4">} suppressHydrationWarning&gt;</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">Remember how we set up our theme with the following property?</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>typography: {
  fontFamily: "var(--font-roboto)",
},
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">typography: {</span></span>
<span class="line"><span style="color: #D4D4D4">  fontFamily: </span><span style="color: #CE9178">&quot;var(--font-roboto)&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">},</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">That &#8220;var(&#8211;font-roboto)&#8221; element is a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties" target="_blank" rel="noreferrer noopener">CSS variable</a>. When combined, these two things result in a CSS variable named &#8220;&#8211;font-roboto&#8221; that&#8217;s <em>scoped</em> to a CSS class that&#8217;s generated at runtime and inserted into the stylesheet. This CSS class is then assigned to the <code>&lt;html&gt;</code> element so that any element in our HTML document can use that CSS variable. And by utilizing the &#8220;fontFamily&#8221; property of the &#8220;typography&#8221; section of our theme, we ensure that every typography element (MUI or not) uses the value of that variable for its &#8220;font-family&#8221; CSS property.</p>



<p class="wp-block-paragraph">The following screenshots from Chrome dev tools demonstrate this in action:</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="816" height="67" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-9.png" alt="HTML tag showing the dynamically-generated CSS class." class="wp-image-44875" style="width:850px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-9.png 816w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-9-300x25.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-9-768x63.png 768w" sizes="(max-width: 816px) 100vw, 816px" /></figure>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="552" height="101" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-10.png" alt="Screenshot showing the CSS variable &quot;--font-roboto&quot;" class="wp-image-44877" style="width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-10.png 552w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-10-300x55.png 300w" sizes="(max-width: 552px) 100vw, 552px" /></figure>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="543" height="234" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-11.png" alt="Screenshot of a CSS rule for a label component, with the &quot;font-family&quot; property set to &quot;var(--font-roboto)&quot;" class="wp-image-44878" style="width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-11.png 543w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-11-300x129.png 300w" sizes="(max-width: 543px) 100vw, 543px" /></figure>



<h3 class="wp-block-heading" id="h-material-icons-set-up" style="padding-top:var(--wp--preset--spacing--40)">Material Icons Set Up</h3>



<p class="wp-block-paragraph">We need to import the Material Icons font so that we can use icons in our app. The easiest way to do this is to get it from the CDN. We can use the <code>&lt;head&gt;</code> element in our root layout.tsx file to insert a stylesheet link into the HTML document, ensuring that the user&#8217;s browser downloads that font.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;head>
  &lt;link
    rel="stylesheet"
    href="https://fonts.googleapis.com/icon?family=Material+Icons"
  />
&lt;/head>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;head&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;link</span></span>
<span class="line"><span style="color: #D4D4D4">    rel=</span><span style="color: #CE9178">&quot;stylesheet&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">    href=</span><span style="color: #CE9178">&quot;https://fonts.googleapis.com/icon?family=Material+Icons&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">  /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/head&gt;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">There&#8217;s also an NPM package you can install to self-host the font, but as I said earlier, it seems to crash the TypeScript server when using Next.js 16, so we&#8217;ll use the CDN until that gets sorted out.</p>



<h3 class="wp-block-heading" id="h-approutercacheprovider" style="padding-top:var(--wp--preset--spacing--30)">AppRouterCacheProvider</h3>



<p class="wp-block-paragraph">For optimal performance, it&#8217;s best to wrap your application with Material UI&#8217;s AppRouterCacheProvider component. According to the <a href="https://mui.com/material-ui/integrations/nextjs/#configuration" target="_blank" rel="noreferrer noopener">documentation</a>, this component &#8220;is responsible for collecting the CSS generated by MUI System on the server, as Next.js is streaming chunks of the .html page to the client.&#8221;</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;body>
  &lt;AppRouterCacheProvider>
    &lt;main>
      {children}
    &lt;/main>
  &lt;/AppRouterCacheProvider>
&lt;/body>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;body&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      {children}</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;/AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/body&gt;</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading" id="h-themeprovider" style="padding-top:var(--wp--preset--spacing--30)">ThemeProvider</h3>



<p class="wp-block-paragraph">For our application to use our custom theme, we need to wrap our application with the ThemeProvider component from Material UI, and provide our custom theme to the &#8220;theme&#8221; prop:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import theme from "../theme";
// ...
&lt;body>
  &lt;AppRouterCacheProvider>
    &lt;ThemeProvider theme={theme}>
      &lt;main>
        {children}
      &lt;/main>
    &lt;/ThemeProvider>
  &lt;/AppRouterCacheProvider>
&lt;/body>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">import theme from </span><span style="color: #CE9178">&quot;../theme&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #6A9955">// ...</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;body&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;ThemeProvider theme={theme}&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        {children}</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/ThemeProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;/AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/body&gt;</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading" id="h-cssbaseline" style="padding-top:var(--wp--preset--spacing--30)">CssBaseline</h3>



<p class="wp-block-paragraph">The <a href="https://mui.com/material-ui/react-css-baseline" target="_blank" rel="noreferrer noopener">CssBaseline</a> component allows us to clear out some undesirable CSS rules that browsers tend to have enabled by default. For example, without this component, there would be a default &#8220;margin&#8221; rule for <code>&lt;html&gt;</code> and <code>&lt;body&gt;</code>, resulting in a space around our actual page. So, our AppBar would not stretch all the way to the edges of the page, which is a jarring look. See the following screenshot for an example of what that looks like:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="426" height="242" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-12-1.png" alt="Screenshot of the margin that appears by default if CssBaseline isn't used." class="has-border-color has-custom-black-border-color wp-image-44882" style="border-width:1px;aspect-ratio:1.7553438063353077;object-fit:cover;width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-12-1.png 426w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-12-1-300x170.png 300w" sizes="(max-width: 426px) 100vw, 426px" /></figure>



<p class="wp-block-paragraph">So to avoid this, we simply have to add the CssBaseline component to our root layout:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;body>
  &lt;AppRouterCacheProvider>
    &lt;ThemeProvider theme={theme}>
      &lt;CssBaseline />
      &lt;main>
        {children}
      &lt;/main>
    &lt;/ThemeProvider>
  &lt;/AppRouterCacheProvider>
&lt;/body>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;body&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;ThemeProvider theme={theme}&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;CssBaseline /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        {children}</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/ThemeProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;/AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/body&gt;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">That makes the margin disappear, as we can see in this screenshot:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="387" height="224" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-13.png" alt="Screenshot of app without the browser default margin, thanks to CssBaseline." class="has-border-color has-custom-black-border-color wp-image-44883" style="border-width:1px;width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-13.png 387w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-13-300x174.png 300w" sizes="(max-width: 387px) 100vw, 387px" /></figure>



<h3 class="wp-block-heading" id="h-initcolorschemescript" style="padding-top:var(--wp--preset--spacing--30)">InitColorSchemeScript</h3>



<p class="wp-block-paragraph">The <a href="https://mui.com/material-ui/react-init-color-scheme-script/#introduction" target="_blank" rel="noreferrer noopener">InitColorSchemeScript</a> component, when rendered before any of our actual content, will cause a script to run that attaches an attribute (a CSS class in our case) to the <code>&lt;html&gt;</code> element, indicating whether to use light or dark mode, depending on user preference. This runs <em>before</em> React itself starts doing client-side rendering and hydration; the result is that we prevent a &#8220;flicker&#8221; that would otherwise occur because the color scheme is not the default that&#8217;s assumed during server-side render.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;body>
  &lt;AppRouterCacheProvider>
    &lt;ThemeProvider theme={theme}>
      &lt;CssBaseline />
      &lt;InitColorSchemeScript attribute="class" />
      &lt;main>
        {children}
      &lt;/main>
    &lt;/ThemeProvider>
  &lt;/AppRouterCacheProvider>
&lt;/body>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;body&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;ThemeProvider theme={theme}&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;CssBaseline /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;InitColorSchemeScript attribute=</span><span style="color: #CE9178">&quot;class&quot;</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        {children}</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/ThemeProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;/AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/body&gt;</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading" id="h-wrapping-app-contents-in-a-container-and-adding-the-nav-bar" style="padding-top:var(--wp--preset--spacing--30)">Wrapping App Contents in a Container and Adding the Nav Bar</h3>



<p class="wp-block-paragraph">To finish things up here, we&#8217;re going to wrap the child components (i.e., the contents of any given page) in a Container component so that they appear to be the same width as the content of our AppBar. Without this, the content of the app&#8217;s pages would stretch to the edge of the screen. See the following screenshot—it doesn&#8217;t look right.</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="470" height="212" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-14.png" alt="Screenshot of the web page's content stretching all the way to the edge of the browser window, making it wider than the nav bar's contents." class="has-border-color has-custom-black-border-color wp-image-44886" style="border-width:1px;width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-14.png 470w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-14-300x135.png 300w" sizes="(max-width: 470px) 100vw, 470px" /></figure>



<p class="wp-block-paragraph">By wrapping <code>{children}</code> in a container and giving it <code>maxWidth</code> of &#8220;xl&#8221;, matching the approach used for the AppBar content, we ensure the entire application aligns cleanly and maintains a comfortable margin between the screen edge and the app’s content.</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="543" height="220" src="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-15.png" alt="Screenshot of the web page's content having the same margin from the edge of the screen as the nav bar's contents." class="has-border-color has-custom-black-border-color wp-image-44889" style="border-width:1px;width:500px" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-15.png 543w, https://static.dmcinfo.com/wp-content/uploads/2026/05/adding-material-ui-next.js-image-15-300x122.png 300w" sizes="(max-width: 543px) 100vw, 543px" /></figure>



<p class="wp-block-paragraph">We&#8217;ll also render our custom TopNavBar component <em>before</em> the container, so that the nav bar always appears at the top of the page, with the current web page being displayed beneath it.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;body>
  &lt;AppRouterCacheProvider>
    &lt;ThemeProvider theme={theme}>
      &lt;CssBaseline />
      &lt;InitColorSchemeScript attribute="class" />
      &lt;main>
        &lt;TopNavBar />
        &lt;Container maxWidth="xl">{children}&lt;/Container>
      &lt;/main>
    &lt;/ThemeProvider>
  &lt;/AppRouterCacheProvider>
&lt;/body>
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;body&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;ThemeProvider theme={theme}&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;CssBaseline /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;InitColorSchemeScript attribute=</span><span style="color: #CE9178">&quot;class&quot;</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;TopNavBar /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;Container maxWidth=</span><span style="color: #CE9178">&quot;xl&quot;</span><span style="color: #D4D4D4">&gt;{children}&lt;/Container&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/ThemeProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;/AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/body&gt;</span></span>
<span class="line"></span></code></pre></div>



<h3 class="wp-block-heading" id="h-the-full-layout-tsx-file" style="padding-top:var(--wp--preset--spacing--30)">The Full layout.tsx File</h3>



<p class="wp-block-paragraph">Altogether, our layout.tsx file looks like this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// src\app\layout.tsx
import type { Metadata } from "next";
import { AppRouterCacheProvider } from "@mui/material-nextjs/v15-appRouter";
import { Roboto } from "next/font/google";
import { ThemeProvider } from "@mui/material/styles";
import theme from "../theme";
import { Container, CssBaseline, InitColorSchemeScript } from "@mui/material";
import TopNavBar from "./_components/topNavBar";

const roboto = Roboto({
  weight: &#91;"300", "400", "500", "700"&#93;,
  subsets: &#91;"latin"&#93;,
  display: "swap",
  variable: "--font-roboto",
});

export const metadata: Metadata = {
  title: "DBA Dashboard",
  description:
    "A dashboard UI for exploring the results from the SQL Server First Responder Kit stored procedures",
};

export default function RootLayout({
  children,
}: Readonly&lt;{
  children: React.ReactNode;
}>) {
  return (
    &lt;html lang="en" className={roboto.variable} suppressHydrationWarning>
      &lt;head>
        &lt;link
          rel="stylesheet"
          href="https://fonts.googleapis.com/icon?family=Material+Icons"
        />
      &lt;/head>
      &lt;body>
        &lt;AppRouterCacheProvider>
          &lt;ThemeProvider theme={theme}>
            &lt;CssBaseline />
            &lt;InitColorSchemeScript attribute="class" />
            &lt;main>
              &lt;TopNavBar />
              &lt;Container maxWidth="xl">{children}&lt;/Container>
            &lt;/main>
          &lt;/ThemeProvider>
        &lt;/AppRouterCacheProvider>
      &lt;/body>
    &lt;/html>
  );
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// src\app\layout.tsx</span></span>
<span class="line"><span style="color: #D4D4D4">import type { Metadata } from </span><span style="color: #CE9178">&quot;next&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import { AppRouterCacheProvider } from </span><span style="color: #CE9178">&quot;@mui/material-nextjs/v15-appRouter&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import { Roboto } from </span><span style="color: #CE9178">&quot;next/font/google&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import { ThemeProvider } from </span><span style="color: #CE9178">&quot;@mui/material/styles&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import theme from </span><span style="color: #CE9178">&quot;../theme&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import { Container, CssBaseline, InitColorSchemeScript } from </span><span style="color: #CE9178">&quot;@mui/material&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">import TopNavBar from </span><span style="color: #CE9178">&quot;./_components/topNavBar&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> roboto = </span><span style="color: #DCDCAA">Roboto</span><span style="color: #D4D4D4">({</span></span>
<span class="line"><span style="color: #D4D4D4">  weight: &#91;</span><span style="color: #CE9178">&quot;300&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;400&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;500&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&quot;700&quot;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">  subsets: &#91;</span><span style="color: #CE9178">&quot;latin&quot;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">  display: </span><span style="color: #CE9178">&quot;swap&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  variable: </span><span style="color: #CE9178">&quot;--font-roboto&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">export </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> metadata: Metadata = {</span></span>
<span class="line"><span style="color: #D4D4D4">  title: </span><span style="color: #CE9178">&quot;DBA Dashboard&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  description:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;A dashboard UI for exploring the results from the SQL Server First Responder Kit stored procedures&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">export default function </span><span style="color: #DCDCAA">RootLayout</span><span style="color: #D4D4D4">({</span></span>
<span class="line"><span style="color: #D4D4D4">  children,</span></span>
<span class="line"><span style="color: #D4D4D4">}: Readonly&lt;{</span></span>
<span class="line"><span style="color: #D4D4D4">  children: </span><span style="color: #9CDCFE">React</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ReactNode</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}&gt;) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;html lang=</span><span style="color: #CE9178">&quot;en&quot;</span><span style="color: #D4D4D4"> className={</span><span style="color: #9CDCFE">roboto</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">variable</span><span style="color: #D4D4D4">} suppressHydrationWarning&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;head&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;link</span></span>
<span class="line"><span style="color: #D4D4D4">          rel=</span><span style="color: #CE9178">&quot;stylesheet&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">          href=</span><span style="color: #CE9178">&quot;https://fonts.googleapis.com/icon?family=Material+Icons&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/head&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;body&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          &lt;ThemeProvider theme={theme}&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;CssBaseline /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;InitColorSchemeScript attribute=</span><span style="color: #CE9178">&quot;class&quot;</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">              &lt;TopNavBar /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">              &lt;Container maxWidth=</span><span style="color: #CE9178">&quot;xl&quot;</span><span style="color: #D4D4D4">&gt;{children}&lt;/Container&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            &lt;/main&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          &lt;/ThemeProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;/AppRouterCacheProvider&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/body&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/html&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">We can also adjust the page.tsx (home page) component to be a bit more minimal, since it no longer needs to render its own <code>&lt;main&gt;</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// src\app\page.tsx
export default function Home() {
  return (
    &lt;>
      &lt;header>
        &lt;h1>DBA Dashboard&lt;/h1>
      &lt;/header>
      &lt;p>
        Welcome to the DBA Dashboard! There&apos;s not much here yet, but stay
        tuned for updates as we build out the UI for exploring the results from
        the SQL Server First Responder Kit stored procedures.
      &lt;/p>
    &lt;/>
  );
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// src\app\page.tsx</span></span>
<span class="line"><span style="color: #D4D4D4">export default function </span><span style="color: #DCDCAA">Home</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;header&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;h1&gt;DBA Dashboard&lt;/h1&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/header&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;p&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        Welcome to the DBA Dashboard! There</span><span style="color: #CE9178">&apos;s not much here yet, but stay</span></span>
<span class="line"><span style="color: #CE9178">        tuned for updates as we build out the UI for exploring the results from</span></span>
<span class="line"><span style="color: #CE9178">        the SQL Server First Responder Kit stored procedures.</span></span>
<span class="line"><span style="color: #CE9178">      &lt;/p&gt;</span></span>
<span class="line"><span style="color: #CE9178">    &lt;/&gt;</span></span>
<span class="line"><span style="color: #CE9178">  );</span></span>
<span class="line"><span style="color: #CE9178">}</span></span>
<span class="line"></span></code></pre></div>



<h2 class="wp-block-heading" id="h-conclusion" style="padding-top:var(--wp--preset--spacing--40)">Conclusion</h2>



<p class="wp-block-paragraph" style="padding-bottom:var(--wp--preset--spacing--40)">And that&#8217;s it! We&#8217;ve successfully set up Material UI with our Next.js 16 application—going forward, we can easily make every UI element we add to the app look professional with minimal effort. In the next post, we&#8217;ll get authentication set up&nbsp;using a library called Better Auth, along with Microsoft&#8217;s Entra ID authentication service.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Have an upcoming project? DMC can help you take the next step.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Take your project to the next level with engineering solutions from DMC. Learn more about our <a href="https://static.dmcinfo.com/services/application-development/">Application Development</a> solutions or contact us to get started today!</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/44831/material-ui-nextjs-16-app-router/">Next.js 16 + Material UI Setup Guide for App Router + RSC: Dev Diary #2</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Starting a DBA Dashboard with Next.js 16 and React: Dev Diary #1</title>
		<link>https://static.dmcinfo.com/blog/41208/building-a-dba-dashboard-with-react-and-next-js-part-1/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Mon, 04 May 2026 14:00:00 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[Next.js]]></category>
		<category><![CDATA[React]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/?p=41208</guid>

					<description><![CDATA[<p>React has been DMC&#8217;s JavaScript framework of choice for over 10 years. In recent years, we&#8217;ve been using it alongside Next.js to build maintainable, scalable, full-stack web applications. In this blog series, I&#8217;m going to demonstrate how to use React and Next.js to build a DBA dashboard that offers a variety of tools to spot [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/41208/building-a-dba-dashboard-with-react-and-next-js-part-1/">Starting a DBA Dashboard with Next.js 16 and React: Dev Diary #1</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">React has been DMC&#8217;s JavaScript framework of choice for over 10 years. In recent years, we&#8217;ve been using it alongside Next.js to build maintainable, scalable, full-stack web applications. In this <a href="https://static.dmcinfo.com/blog/44831/material-ui-nextjs-16-app-router/">blog series</a>, I&#8217;m going to demonstrate how to use React and Next.js to build a DBA dashboard that offers a variety of tools to spot and troubleshoot performance or reliability issues with a SQL Server instance.</p>



<p class="wp-block-paragraph">I plan to use Better-Auth for authentication and Material UI as a UI component library, and of course, I&#8217;ll be using SQL Server (a.k.a. MSSQL). So, if you&#8217;d like to learn how to build a React app with Next.js, Better-Auth, Material UI, and MSSQL using Prisma ORM, I&#8217;m going to show you how to set it all up.</p>



<h2 class="wp-block-heading" id="h-scaffolding-a-next-js-app">Scaffolding <strong>a Next.js App</strong></h2>



<p class="wp-block-paragraph">The first step to building a Next.js app is to run the create-next-app CLI tool. First, make sure you&#8217;ve installed Node.js version 24 or later. Then, run <code>npx create-next-app@latest</code> the CLI tool and create the application. It presents us with the following options:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1054" height="955" src="https://static.dmcinfo.com/wp-content/uploads/2026/02/image.png" alt="" class="wp-image-41398" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/02/image.png 1054w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-300x272.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-1024x928.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-768x696.png 768w" sizes="(max-width: 1054px) 100vw, 1054px" /></figure>



<p class="wp-block-paragraph">I recommend the following options:</p>



<ol class="wp-block-list">
<li><strong>TypeScript</strong> &#8211; at DMC, we&#8217;ve used it for React applications since 2016 and have adopted it as a standard. TypeScript is a superset of JavaScript that adds strong typing so that, for example, you&#8217;ll get a compiler error if you try to assign an integer value to a string variable. Fully explaining TypeScript is outside of the scope of this article, but I strongly recommend using it for all React applications.</li>



<li><strong>ESLint</strong> &#8211; this is the gold standard linter for JavaScript/TypeScript, and you really can&#8217;t go wrong by choosing it.</li>



<li><strong>React Compiler</strong> &#8211; I have been very eager to try this out on a project ever since it was introduced to React. Among other things, this makes it so you don&#8217;t need to explicitly use the useMemo or useCallback hooks &#8211; the compiler will take care of those optimizations for you so that you can focus on building your app.</li>



<li><strong>src directory</strong> &#8211; this is a pretty standard setup for JavaScript/TypeScript applications.</li>



<li><strong>App Router</strong> &#8211; the alternative is Page Router, which is the legacy version of Next.js.</li>
</ol>



<p class="wp-block-paragraph">By default, the CLI tool gets us set up with a package-lock.json file, but I prefer to use Yarn as my package manager, so I like to delete that file and run <code>yarn install</code>. Then I keep the yarn.lock file in source control.</p>



<p class="wp-block-paragraph">The app scaffolded by the CLI tool can be run locally with the command <code>yarn dev</code>. Doing so shows me a placeholder home page that looks like the following:</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="1396" height="1138" src="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-1.png" alt="" class="wp-image-41410" style="width:554px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-1.png 1396w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-1-300x245.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-1-1024x835.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-1-768x626.png 768w" sizes="(max-width: 1396px) 100vw, 1396px" /></figure>



<p class="wp-block-paragraph">When I&#8217;m building a new app, the first thing I do is clear out all the scaffolded content, like that placeholder page and the accompanying CSS. Once I&#8217;ve finished that, layout.tsx file and page.tsx file scaffolded by the CLI looks like the following:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TSX</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// layout.tsx
import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "DBA Dashboard",
  description:
    "A dashboard UI for exploring the results from the SQL Server First Responder Kit stored procedures",
};

export default function RootLayout({
  children,
}: Readonly&lt;{
  children: React.ReactNode;
}>) {
  return (
    &lt;html lang="en">
      &lt;body>{children}&lt;/body>
    &lt;/html>
  );
}

// page.tsx
export default function Home() {
  return (
    &lt;div>
      &lt;main>
        &lt;header>
          &lt;h1>DBA Dashboard&lt;/h1>
        &lt;/header>
        &lt;p>
          Welcome to the DBA Dashboard! There's not much here yet, but stay
          tuned for updates as we build out the UI for exploring the results
          from the SQL Server First Responder Kit stored procedures.
        &lt;/p>
      &lt;/main>
    &lt;/div>
  );
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// layout.tsx</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">type</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">Metadata</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;next&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">metadata</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Metadata</span><span style="color: #D4D4D4"> = {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">title:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;DBA Dashboard&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">description:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;A dashboard UI for exploring the results from the SQL Server First Responder Kit stored procedures&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">RootLayout</span><span style="color: #D4D4D4">({</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">children</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">}: </span><span style="color: #4EC9B0">Readonly</span><span style="color: #D4D4D4">&lt;{</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">children</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">React</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">ReactNode</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}&gt;) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">html</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">lang</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;en&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">body</span><span style="color: #808080">&gt;</span><span style="color: #569CD6">{</span><span style="color: #9CDCFE">children</span><span style="color: #569CD6">}</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">body</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">html</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// page.tsx</span></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Home</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">div</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">main</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">header</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">h1</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">DBA Dashboard</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">h1</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">header</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">p</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          Welcome to the DBA Dashboard! There&apos;s not much here yet, but stay</span></span>
<span class="line"><span style="color: #D4D4D4">          tuned for updates as we build out the UI for exploring the results</span></span>
<span class="line"><span style="color: #D4D4D4">          from the SQL Server First Responder Kit stored procedures.</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">p</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">main</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">div</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">And here&#8217;s the resulting home page of our new Next.js app:</p>



<figure class="wp-block-image size-full is-resized has-custom-border"><img decoding="async" width="1164" height="1282" src="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-2.png" alt="" class="has-border-color has-custom-black-border-color wp-image-41411" style="border-width:1px;width:631px;height:auto" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-2.png 1164w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-2-272x300.png 272w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-2-930x1024.png 930w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-2-768x846.png 768w" sizes="(max-width: 1164px) 100vw, 1164px" /></figure>



<p class="wp-block-paragraph">You may notice a little circular &#8220;N&#8221; button at the lower left corner &#8211; that&#8217;s a widget that Next.js gives us when running locally in dev mode. (TODO: Give more details about this, probably a link to documentation. This feature is new to me &#8211; apparently it&#8217;s called a &#8220;devIndicator&#8221;).</p>



<h2 class="wp-block-heading" id="h-configuring-the-next-js-app">Configuring the Next.js App</h2>



<p class="wp-block-paragraph">At the start of a new project, I like to get off on the right foot with regard to linting. Our scaffolded app is set up for ESLint out of the box, so I can run <code>yarn lint</code> to check for violations, and there are quite a few!</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1416" height="628" src="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-3.png" alt="" class="wp-image-41412" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-3.png 1416w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-3-300x133.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-3-1024x454.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-3-768x341.png 768w" sizes="(max-width: 1416px) 100vw, 1416px" /></figure>



<p class="wp-block-paragraph">These violations are coming from files that were created by the CLI tool &#8211; I didn&#8217;t write this code. Because of that, I&#8217;d like to ignore these files when doing lint checks &#8211; to do that, I can add the &#8220;types&#8221; folder to the eslint.config.mjs file:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  // Override default ignores of eslint-config-next.
  globalIgnores(&#91;
    // Default ignores of eslint-config-next:
    ".next/**",
    "out/**",
    "build/**",
    "next-env.d.ts",
    // Custom ignores:
    "types/**",
  &#93;),
]);

export default eslintConfig;</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">defineConfig</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">globalIgnores</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;eslint/config&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">nextVitals</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;eslint-config-next/core-web-vitals&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">nextTs</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;eslint-config-next/typescript&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">eslintConfig</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">defineConfig</span><span style="color: #D4D4D4">([</span></span>
<span class="line"><span style="color: #D4D4D4">  ...</span><span style="color: #9CDCFE">nextVitals</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  ...</span><span style="color: #9CDCFE">nextTs</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// Override default ignores of eslint-config-next.</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">globalIgnores</span><span style="color: #D4D4D4">(&#91;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Default ignores of eslint-config-next:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;.next/**&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;out/**&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;build/**&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;next-env.d.ts&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// Custom ignores:</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;types/**&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  &#93;),</span></span>
<span class="line"><span style="color: #D4D4D4">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">eslintConfig</span><span style="color: #D4D4D4">;</span></span></code></pre></div>



<p class="wp-block-paragraph">Now, if I run <code>yarn lint</code> again, it only raises one violation:</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1212" height="394" src="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-4.png" alt="" class="wp-image-41413" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-4.png 1212w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-4-300x98.png 300w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-4-1024x333.png 1024w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-4-768x250.png 768w" sizes="(max-width: 1212px) 100vw, 1212px" /></figure>



<p class="wp-block-paragraph">Looks like I forgot to use an HTML entity for that apostrophe in my hard-coded text. So I&#8217;ll just fix that:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TSX</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>// page.tsx
export default function Home() {
  return (
    &lt;div>
      &lt;main>
        &lt;header>
          &lt;h1>DBA Dashboard&lt;/h1>
        &lt;/header>
        &lt;p>
          Welcome to the DBA Dashboard! There&apos;s not much here yet, but stay
          tuned for updates as we build out the UI for exploring the results
          from the SQL Server First Responder Kit stored procedures.
        &lt;/p>
      &lt;/main>
    &lt;/div>
  );
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">// page.tsx</span></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Home</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">div</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">main</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">header</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">h1</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">DBA Dashboard</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">h1</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">header</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">p</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          Welcome to the DBA Dashboard! There&apos;s not much here yet, but stay</span></span>
<span class="line"><span style="color: #D4D4D4">          tuned for updates as we build out the UI for exploring the results</span></span>
<span class="line"><span style="color: #D4D4D4">          from the SQL Server First Responder Kit stored procedures.</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">p</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">main</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">div</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">After fixing that, ESLint raises no violations, so now we&#8217;re officially starting with a squeaky-clean codebase!</p>



<figure class="wp-block-image size-full"><img decoding="async" width="412" height="181" src="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-5.png" alt="" class="wp-image-41414" srcset="https://static.dmcinfo.com/wp-content/uploads/2026/02/image-5.png 412w, https://static.dmcinfo.com/wp-content/uploads/2026/02/image-5-300x132.png 300w" sizes="(max-width: 412px) 100vw, 412px" /></figure>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--50)">In the next blog post, we&#8217;ll be adding a component library to our project: Material UI. A high-quality component library like Material UI makes it easy to build a professional-looking user interface for our dashboard.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step">Have an upcoming project? DMC can help you take the next step.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Take your project to the next level with engineering solutions from DMC. Learn more about our <a href="https://static.dmcinfo.com/services/application-development/" id="https://static.dmcinfo.com/services/application-development/">Application Development</a> solutions or contact us to get started today!</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/41208/building-a-dba-dashboard-with-react-and-next-js-part-1/">Starting a DBA Dashboard with Next.js 16 and React: Dev Diary #1</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Why DMC is Adopting Next.js for Web Application Development</title>
		<link>https://static.dmcinfo.com/blog/16026/why-dmc-is-adopting-next-js-for-web-application-development/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Thu, 22 Aug 2024 15:40:27 +0000</pubDate>
				<category><![CDATA[Web Application Development]]></category>
		<category><![CDATA[Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/16026/why-dmc-is-adopting-next-js-for-web-application-development/</guid>

					<description><![CDATA[<p>Introduction For the last seven&#160;years, DMC&#8217;s go-to stack for web application development has been a React SPA (single-page application) front-end with an ASP.NET Core REST API. Although that will still be a good fit for some projects,&#160;DMC has chosen to expand our toolset and adopt Next.js for developing new web applications. Why choosing the right [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/16026/why-dmc-is-adopting-next-js-for-web-application-development/">Why DMC is Adopting Next.js for Web Application Development</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<ul class="wp-block-list">
<li><a href="#introduction">Introduction</a></li>



<li><a href="#overview-of-nextjs">Overview of Next.js</a></li>



<li><a href="#performance-improvements">Performance Improvements</a></li>



<li><a href="#developer-experience">Developer Experience</a></li>



<li><a href="#case-studies-and-success-stories">Case Studies and Success Stories</a></li>



<li><a href="#future-outlook">Future Outlook</a></li>



<li><a href="#conclusion">Conclusion</a></li>
</ul>



<h2 id="h-introduction" class="wp-block-heading"><a name="introduction">Introduction</a></h2>



<p class="wp-block-paragraph">For the last seven&nbsp;years, DMC&#8217;s go-to stack for web application development has been a React SPA (single-page application) front-end with an ASP.NET Core REST API. Although that will still be a good fit for some projects,&nbsp;DMC has chosen to expand our toolset and adopt Next.js for developing new web applications.</p>



<h3 id="h-why-choosing-the-right-framework-is-important" class="wp-block-heading">Why choosing the right framework is important</h3>



<p class="wp-block-paragraph">When developing a new web application, your choice of framework or tech stack can have enormous and wide-ranging implications, and the benefits and consequences of this decision can impact your business years later. These implications include, but are not limited to:</p>



<ul class="wp-block-list">
<li><strong>Performance.</strong> Page load times and smoothness of transitions in the UI can both be affected by your framework choice.</li>



<li><strong>Maintenance.</strong> Choosing an out-dated framework, or even choosing a modern framework that&#8217;s a poor fit for your application, can result in future feature requests taking much longer to implement. I.e. a 1-hour development task could be an 8-hour task in extreme cases.</li>



<li><strong>Scalability.</strong> Some frameworks can limit your options for scaling as your userbase groups. For example a Blazor Server application may make it more difficult to do horizontal scaling. Also, some ORMs will have issues once your data reaches a certain scale.</li>



<li><strong>Talent.</strong> If you choose an unpopular framework, or continue holding onto an outdated one, it can be difficult to hire, train, and retain good developer talent. After all, not many developers want to work on a no-framework PHP app or a ASP.NET Web Forms app in 2024.</li>



<li><strong>Support.</strong> This is where popularity comes into play, because it tends to be easy to find information about how to solve a specific problem with a popular framework, whereas trying to find information on how to solve a specific problem with an <em>unpopular</em> framework can be nearly impossible.</li>
</ul>



<p class="wp-block-paragraph">So when developing a new web application, you should look for a framework that is:</p>



<ul class="wp-block-list">
<li>Performant, either in general or at least for your use case</li>



<li>Has a good developer experience. This helps make maintenance easier and helps attract and retain talent.</li>



<li>Scalable.</li>



<li>Popular, or at the very least not <em>unpopular</em>.</li>
</ul>



<p class="wp-block-paragraph">With those points in mind, DMC has chosen to add <a href="https://nextjs.org/" target="_blank">Next.js</a> to our web development toolbelt and officially adopt it for use on new projects. We have done several pilot projects with Next.js and we feel it&#8217;s a good, balanced approach that will serve a majority of projects well.</p>



<h2 id="h-overview-of-next-js" class="wp-block-heading"><a name="overview-of-nextjs">Overview of Next.js</a></h2>



<h3 id="h-what-is-next-js" class="wp-block-heading">What is Next.js?</h3>



<p class="wp-block-paragraph">Next.js is a full-stack React framework that has actually been around a long time (1.0 release was in 2016), but its popularity and their profile in the React community has exploded since they released their big App Router update last year (May 2023). This version of Next.js became the first React framework to implement <a href="https://react.dev/reference/rsc/server-components" target="_blank">React Server Components</a> and <a href="https://react.dev/reference/rsc/server-actions" target="_blank">Server Actions</a>, and as a result the official React documentation even started <a href="https://react.dev/learn/start-a-new-react-project#production-grade-react-frameworks" target="_blank">recommending Next.js for new projects</a>.</p>



<h3 id="h-key-features-of-next-js" class="wp-block-heading">Key Features of Next.js</h3>



<p class="wp-block-paragraph">Next.js offers features that make it stand out from other web development frameworks.</p>



<h4 id="h-routing" class="wp-block-heading">Routing</h4>



<p class="wp-block-paragraph">Most web development frameworks offer some sort of routing system, which enables you to connect a URL route (e.g. &#8220;/employees/123/edit&#8221;) to the piece of code responsible for responding to requests for that URL. Next.js offers a particularly effective routing system. It&#8217;s <em>file-based</em>, which means that requests to your web application will be routed a given React component based on the names of its folders and files.</p>



<p class="wp-block-paragraph">For example, in the file structure depicted in the below screenshot, a request to &#8220;/admin/addCompetency&#8221; will be handled by the file &#8220;app/admin/addCompetency/page.tsx&#8221;. This is intuitive <em>and</em> it enforces a disciplined approach to organizing your code files. If a new developer on the team is assigned a task to troubleshoot that &#8220;/admin/addCompetency&#8221; page, they know exactly where to start.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/routing-screenshot.png" alt="Example of folder-based routing in Next.js"/></figure>



<p class="wp-block-paragraph">But that&#8217;s only a small part of what Next.js&#8217; routing system offers us. In the above screenshot, you can see an &#8220;error.tsx&#8221; file. By including this file in this folder, we automatically wrap our page in an <a href="https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary" target="_blank">error boundary</a>. Any uncaught exceptions/errors on the &#8220;/admin/addCompetency&#8221; page will be handling by the component defined in &#8220;error.tsx&#8221; and a fallback UI will be displayed.</p>



<p class="wp-block-paragraph">Additionally, we can put a &#8220;loading.tsx&#8221; file in that folder, and it will cause a fallback UI (loading indicator) to be displayed while we wait for that portion of the page to finish loading. This feature allows us to implement <a href="https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming" target="_blank">loading UI and streaming</a> with almost no effort.</p>



<p class="wp-block-paragraph">Next.js&#8217; routing system also offers dynamic routes, parallel routes, intercepting routes, and other features that make it easy to compose a complex UI while keeping our codebase simple and well-organized. <a href="https://nextjs.org/docs/app/building-your-application/routing" target="_blank">Read more about it routing in Next.js.</a></p>



<h4 id="h-server-components" class="wp-block-heading">Server Components</h4>



<p class="wp-block-paragraph">I&#8217;ve been using React for web application development since 2016. And when I first started, I distinctly remember thinking, &#8220;Okay, but how do we get data from the server into our React components?&#8221; The official answer from React back then was <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2642-fe0f.png" alt="🤷‍♂️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">In 2016, the common way to load data into a React component (without third-party libraries for data fetching) was to do a fetch call from within your component&#8217;s &#8220;componentDidMount&#8221; lifecycle method. Honestly, it was kind of gross. With React 16.8 things got a little better with the useEffect hook. And of course, today, in a React SPA the best way to load data from the server into a component is with a third-party library like Tanstack Query (react-query).</p>



<p class="wp-block-paragraph">But React has never offered a first-class method of getting data from the server and giving it to our React components. Until now. React has introduced the concept of React Server Components (RSC), and Next.js is the React framework to make this feature available. With RSCs, we can write components that run on the server. The components can be defined as async functions, and therefore we can <em>await</em> asynchronous operations in them.</p>



<p class="wp-block-paragraph">For example, here&#8217;s a server component for a select list of &#8220;categories&#8221;. I&#8217;ve defined this as an async function, so it can only run on the server, but this allows us to get data directly from our database (which is what db_getCategories() does in this example). Because we await the result, we can guarantee that by the time we reach the next line of our code, we have the data.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>import db_getCategories from "@/database/queries/db_getCategories";
import linq from "linq";

export async function CategorySelect() {
  const categories = await db_getCategories();
  const orderedCategories = linq
    .from(categories)
    .orderBy((c) => c.Name)
    .toArray();
  return (
    &lt;select name="category_id">
      {orderedCategories.map((category) => (&lt;option key="{category.Id}" value="{category.Id}">
          {category.Name}&lt;/option>
      ))}
    &lt;/select>
  );
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">db_getCategories</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;@/database/queries/db_getCategories&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">linq</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;linq&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CategorySelect</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">categories</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">db_getCategories</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">orderedCategories</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">linq</span></span>
<span class="line"><span style="color: #D4D4D4">    .</span><span style="color: #DCDCAA">from</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">categories</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    .</span><span style="color: #DCDCAA">orderBy</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">c</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">c</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    .</span><span style="color: #DCDCAA">toArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">select</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;category_id&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      {</span><span style="color: #9CDCFE">orderedCategories</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">map</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">category</span><span style="color: #D4D4D4">) =&gt; (&lt;</span><span style="color: #4EC9B0">option</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">key</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{category.Id}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">value</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{category.Id}&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">          {category.Name}&lt;/</span><span style="color: #4EC9B0">option</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      ))}</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">select</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">This results in a very simple component: first we get the data, then we render the data as JSX. No state, no effects, no memoization &#8211; just simple, straightforward logic that&#8217;s easy for a developer to understand.</p>



<p class="wp-block-paragraph">But what sets React Server Components apart from other server-side rendering solutions is that they can be used to <em>stream</em> parts of the UI to the web browser. So as opposed to the good old days when we used to render the HTML for an entire web page on the server before sending it to the web browser, we can leverage server components along with React Suspense to stream our UI in chunks to the user complete with loading indicators:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/react-suspense-code-sample.png" alt="Code sample of wrapping a server component with React Suspense"/></figure>



<p class="wp-block-paragraph">Additionally, because the output of these server components is a minimal data format called the RSC Payload, rather than rendered HTML, server components do not have a significant impact on a web application&#8217;s I/O throughput and therefore should not have a negative impact on scalability in most cases.</p>



<p class="wp-block-paragraph">This is incredibly powerful. And to someone who&#8217;s been developing React applications for years, this feels like someone&#8217;s finally given me the keys to a car that I&#8217;ve just been hotwiring until now.</p>



<h4 id="h-server-actions" class="wp-block-heading">Server Actions</h4>



<p class="wp-block-paragraph">Server Actions are similar to Server Components in that they&#8217;re a React feature that Next.js has been the first framework to incorporate. Whereas React Server Components are designed to make loading/fetching data easier, Server Actions are designed for <em>mutating</em> data.</p>



<p class="wp-block-paragraph">Server Actions are defined similar to any other JavaScript function, except with a &#8220;use server&#8221; directive added in to signal that they&#8217;re Server Actions. Then Next.js takes these functions and essentially turns them into HTTP endpoints for us, and by calling these functions from a client component we automatically get an HTTP request sent to that endpoint.</p>



<p class="wp-block-paragraph">Here&#8217;s an example Server Action, that takes some simple parameters and makes a corresponding update in the database (db_updateCompetencyForEmployee()) based on those parameters and the authentication information from the request (await getUserSession()).</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>"use server";

export async function updateCompetencyForEmployee(
  competencyId: number,
  proficiencyId: number,
  learningPreference: boolean,
  notes: string | null
) {
  const userSession = await getUserSession();
  if (!userSession) {
    return { success: false, error: "User not logged in" };
  }
  const onprem_sid = userSession.user.onprem_sid;
  if (!onprem_sid) {
    return {
      success: false,
      error: "User session does not contain onprem_sid",
    };
  }
  const employee = await db_getEmployeeByOnpremSid(onprem_sid);
  if (isNaN(competencyId) || isNaN(proficiencyId) || !employee) {
    return {
      success: false,
      error: "Invalid competencyId, proficiencyId, or employeeId",
    };
  }
  await db_updateCompetencyForEmployee(
    employee.Id,
    competencyId,
    proficiencyId,
    learningPreference,
    notes
  );
  return { success: true };
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #CE9178">&quot;use server&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">updateCompetencyForEmployee</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">competencyId</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">number</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">proficiencyId</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">number</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">learningPreference</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">boolean</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">notes</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4"> | </span><span style="color: #4EC9B0">null</span></span>
<span class="line"><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">userSession</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getUserSession</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">success:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">error:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;User not logged in&quot;</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">onprem_sid</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">userSession</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">user</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">onprem_sid</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">onprem_sid</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">success:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">error:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;User session does not contain onprem_sid&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    };</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">employee</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">db_getEmployeeByOnpremSid</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">onprem_sid</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #DCDCAA">isNaN</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">competencyId</span><span style="color: #D4D4D4">) || </span><span style="color: #DCDCAA">isNaN</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">proficiencyId</span><span style="color: #D4D4D4">) || !</span><span style="color: #9CDCFE">employee</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">success:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">error:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;Invalid competencyId, proficiencyId, or employeeId&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    };</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">db_updateCompetencyForEmployee</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">employee</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Id</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">competencyId</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">proficiencyId</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">learningPreference</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">notes</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">success:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">true</span><span style="color: #D4D4D4"> };</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">Then from a client component, we can call this Server Action function directly, and under the hood React/Next.js will make an HTTP request to the server with those parameter values in the body of the request.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;label htmlfor="{`${fieldPrefix}-proficiency-${competencyId}`}">
  &lt;input onclick="{async" type="radio" /> {
      const response = await updateCompetencyForEmployee(
        competencyId,
        proficiencyId,
        props.learningPreference,
        props.notes
      );
      if (!response.success) {
        alert(`ERROR: ${response.error}`);
      } 
    }}
  />
  {proficiencyName}
&lt;/label></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">htmlfor</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{`${fieldPrefix}-proficiency-${competencyId}`}&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  &lt;</span><span style="color: #9CDCFE">input</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{async&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;radio&quot;</span><span style="color: #D4D4D4"> /&gt; {</span></span>
<span class="line"><span style="color: #D4D4D4">      const </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">updateCompetencyForEmployee</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">competencyId</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">proficiencyId</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">props</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">learningPreference</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">props</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">notes</span></span>
<span class="line"><span style="color: #D4D4D4">      );</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #DCDCAA">if</span><span style="color: #D4D4D4"> (!response.success) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">alert</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">`ERROR: </span><span style="color: #569CD6">${</span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">error</span><span style="color: #569CD6">}</span><span style="color: #CE9178">`</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">      } </span></span>
<span class="line"><span style="color: #D4D4D4">    }}</span></span>
<span class="line"><span style="color: #D4D4D4">  /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  {</span><span style="color: #9CDCFE">proficiencyName</span><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">One way to think of Server Actions is that they&#8217;ve made it far easier to cross the barrier between the client and the server.</p>



<p class="wp-block-paragraph">A really nifty way to use Server Actions is to provide them to the &#8220;action&#8221; attribute of a&nbsp; element like so:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>export function AddCategoryForm() {
  return (
    &lt;form action={addCompetencyCategory}>
      &lt;FormLabel htmlFor="add_category">
        Add Category
        &lt;input
          type="text"
          name="add_category"
          placeholder="New Category Name"
        />
      &lt;/FormLabel>
      &lt;FormSubmitButton pendingText="Saving..." type="submit">
        Add Category
      &lt;/FormSubmitButton>
      &lt;p>
        &lt;FormStatusMessageFromUrl />
      &lt;/p>
    &lt;/form>
  );
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">AddCategoryForm</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;</span><span style="color: #9CDCFE">form</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">action</span><span style="color: #D4D4D4">={</span><span style="color: #9CDCFE">addCompetencyCategory</span><span style="color: #D4D4D4">}&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;</span><span style="color: #9CDCFE">FormLabel</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">htmlFor</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;add_category&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">Add</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Category</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">input</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;text&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;add_category&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">          </span><span style="color: #9CDCFE">placeholder</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;New Category Name&quot;</span></span>
<span class="line"><span style="color: #D4D4D4">        /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/</span><span style="color: #9CDCFE">FormLabel</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;</span><span style="color: #9CDCFE">FormSubmitButton</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">pendingText</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Saving...&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;submit&quot;</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">Add</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Category</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/</span><span style="color: #9CDCFE">FormSubmitButton</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;</span><span style="color: #4EC9B0">p</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        &lt;</span><span style="color: #9CDCFE">FormStatusMessageFromUrl</span><span style="color: #D4D4D4"> /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">      &lt;/</span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    &lt;/</span><span style="color: #9CDCFE">form</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<pre class="wp-block-code"><code>
export function AddCategoryForm() {
  return (
    </code></pre>


<form action="{addCompetencyCategory}">Add Category <input name="add_category" type="text" placeholder="New Category Name" /> Add Category</form>


<p class="wp-block-paragraph"><formstatusmessagefromurl> </formstatusmessagefromurl></p>



<p class="wp-block-paragraph">); }</p>



<p class="wp-block-paragraph">With this simple form, we don&#8217;t need to manage state at all! This is very similar to plain HTML form handling, where pressing the submit button triggers the values of the form&#8217;s fields to be bundled up and sent to the provided URL as an HTTP request.</p>



<p class="wp-block-paragraph">Here&#8217;s the server action being used in the above example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>"use server";
// ...imports...
export async function addCompetencyCategory(formData: FormData) {
  // authorization
  if (!(await getUserIsAdmin())) {
    redirect(
      "/admin?error=" +
        encodeURIComponent("You must be an admin to add a category")
    );
  }
  // form data validation with ZOD
  const { data, success, error } = await schema.safeParseAsync(formData);
  if (!success) {
    const errorMessages = error.issues.map((i) => i.message).join("\n");
    // redirect with error message if failed validation
    redirect("/admin?error=" + encodeURIComponent(errorMessages));
  }
  const { add_category: name } = data;
  // save to database
  await db_addCompetencyCategory(name);
  revalidatePath("/admin");
  // redirect to "success" page
  redirect(
    "/admin?message=" + encodeURIComponent("Category added successfully")
  );
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #CE9178">&quot;use server&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #6A9955">// ...imports...</span></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">addCompetencyCategory</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">formData</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">FormData</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// authorization</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!(</span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">getUserIsAdmin</span><span style="color: #D4D4D4">())) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">redirect</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #CE9178">&quot;/admin?error=&quot;</span><span style="color: #D4D4D4"> +</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">encodeURIComponent</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;You must be an admin to add a category&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// form data validation with ZOD</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> { </span><span style="color: #4FC1FF">data</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">success</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">error</span><span style="color: #D4D4D4"> } = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">schema</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">safeParseAsync</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">formData</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">success</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">errorMessages</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">error</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">issues</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">map</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">join</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;</span><span style="color: #D7BA7D">\n</span><span style="color: #CE9178">&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// redirect with error message if failed validation</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">redirect</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/admin?error=&quot;</span><span style="color: #D4D4D4"> + </span><span style="color: #DCDCAA">encodeURIComponent</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">errorMessages</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">add_category</span><span style="color: #D4D4D4">: </span><span style="color: #4FC1FF">name</span><span style="color: #D4D4D4"> } = </span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// save to database</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">db_addCompetencyCategory</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">revalidatePath</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;/admin&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #6A9955">// redirect to &quot;success&quot; page</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">redirect</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&quot;/admin?message=&quot;</span><span style="color: #D4D4D4"> + </span><span style="color: #DCDCAA">encodeURIComponent</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Category added successfully&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">  );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">An interesting benefit of this approach is the form submission is fully functional <em>even if the user has JavaScript disabled</em>. We don&#8217;t need that for most projects, but for those few projects where we need to the application to still be functional with JavaScript disabled, this is an enormous benefit. If the user does have JavaScript enabled, though, Next.js automatically transforms the behavior of this simple form to act like a form in a modern SPA, because once the React component tree hydrates the client-side behavior gets enabled. This is called <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement" target="_blank">progressive enhancement</a>.</p>



<h4 id="h-api-routes" class="wp-block-heading">API routes</h4>



<p class="wp-block-paragraph">In addition to its full-stack capabilities, Next.js allows us to define more traditional endpoints via &#8220;route handlers&#8221;. These use the same kind of file-based routing as the rest of the framework, but route handlers allow us to return any kind of HTTP response we want in response to a request. For example, this GET route handler at &#8220;/api/projects/{projectId}/expenseReceipts/projectFiles&#8221; returns a file that can be downloaded by the browser:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>export async function GET(
  request: Request,
  { params }: { params: { projectId: string } }
) {
  if (!getUserIsLoggedIn()) {
    return new Response("Unauthorized", { status: 401 });
  }
  const acumaticaApiEndpoint = `${process.env.ACUMATICA_API_URL}/ExpenseReceipt/ProjectFiles?projectId=${params.projectId}`;
  const acumaticaResponse = await fetch(acumaticaApiEndpoint);
  if (acumaticaResponse.ok) {
    const bodyAsBuffer = await acumaticaResponse.arrayBuffer();
    return new Response(bodyAsBuffer, {
      headers: {
        "Content-Type": "application/octet-stream",
        "Content-Disposition": `attachment; filename="projectFiles.zip"`,
      },
    });
  }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">GET</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">request</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Request</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">  { </span><span style="color: #9CDCFE">params</span><span style="color: #D4D4D4"> }: { </span><span style="color: #9CDCFE">params</span><span style="color: #D4D4D4">: { </span><span style="color: #9CDCFE">projectId</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4"> } }</span></span>
<span class="line"><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #DCDCAA">getUserIsLoggedIn</span><span style="color: #D4D4D4">()) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Response</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Unauthorized&quot;</span><span style="color: #D4D4D4">, { </span><span style="color: #9CDCFE">status:</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">401</span><span style="color: #D4D4D4"> });</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">acumaticaApiEndpoint</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">`</span><span style="color: #569CD6">${</span><span style="color: #9CDCFE">process</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">env</span><span style="color: #D4D4D4">.</span><span style="color: #4FC1FF">ACUMATICA_API_URL</span><span style="color: #569CD6">}</span><span style="color: #CE9178">/ExpenseReceipt/ProjectFiles?projectId=</span><span style="color: #569CD6">${</span><span style="color: #9CDCFE">params</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">projectId</span><span style="color: #569CD6">}</span><span style="color: #CE9178">`</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">acumaticaResponse</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">fetch</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">acumaticaApiEndpoint</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">acumaticaResponse</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ok</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">bodyAsBuffer</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">acumaticaResponse</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">arrayBuffer</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Response</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">bodyAsBuffer</span><span style="color: #D4D4D4">, {</span></span>
<span class="line"><span style="color: #D4D4D4">      </span><span style="color: #9CDCFE">headers:</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&quot;Content-Type&quot;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;application/octet-stream&quot;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #CE9178">&quot;Content-Disposition&quot;</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">`attachment; filename=&quot;projectFiles.zip&quot;`</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">      },</span></span>
<span class="line"><span style="color: #D4D4D4">    });</span></span>
<span class="line"><span style="color: #D4D4D4">  }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">An advantage of this is that if we have a separate mobile or desktop application that needs to consume endpoints like these, we don&#8217;t need a separate REST API to serve those applications. It can make sense to have a separate REST API anyway, but it&#8217;s not strictly necessary.</p>



<h2 id="h-performance-improvements" class="wp-block-heading"><a name="performance-improvements">Performance Improvements</a></h2>



<h3 id="h-faster-load-times" class="wp-block-heading">Faster Load Times</h3>



<p class="wp-block-paragraph">Thanks to server-side rendering (SSR) with React Server Components, initial load times in a Next.js application can be much faster than the initial page load time of a React SPA. Whereas with an SPA we have to wait for the JavaScript bundle (which is usually hundreds of KB and can sometimes be multiple MB) to download before the user sees any UI, in a Next.js app the initial request for the web page gets fully rendered HTML as a response. Taking a current project as an example, loading the home page is only a 7.4 KB download versus 200 KB if I were developing it as an SPA. This tends to result in a much better score for <a href="https://web.dev/articles/fcp" target="_blank">First Contentful Paint (FCP)</a>, an important Core Web Vitals performance metric.</p>



<p class="wp-block-paragraph">And depending on how much content you&#8217;re able to render on the server for your application, you can even end up seeing the remarkable result of FCP and LCP (Largest Contentful Paint) being the same number! When this happens, it means the very instant that the user first sees content/UI for the web page, they see the entire UI.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/core-web-vitals-sample.png" alt="Core web vitals sample for a real Next.js application"/></figure>



<h3 id="h-caching" class="wp-block-heading">Caching</h3>



<p class="wp-block-paragraph">One of the reasons it&#8217;s easy to develop a highly-performant application with Next.js is because it offers robust caching options. Next.js with app router has four different caching mechanisms, all of which are enabled by default (Next.js 15 will make some of these opt-in).</p>



<ul class="wp-block-list">
<li><strong>Request Memoization</strong> &#8211; If you make multiple fetch requests to the same URL in multiple places in a React component tree, Next.js will only make the request once</li>



<li><strong>Data Cache</strong> &#8211; Same as Request Memoization, but is persistent across multiple requests to our Next.js application, e.g. multiple requests for the same web page</li>



<li><strong>Full Route Cache</strong> &#8211; If a given server component or route handler does not use headers or cookies to determine its output, then the HTML output and/or React Server Component Payload (or HTTP response in the case of route handlers) will be cached on the server as static content until something happens to &#8220;revalidate&#8221; it.</li>



<li><strong>Router Cache</strong> &#8211; This is the client-side caching mechanism in Next.js. It&#8217;s time-based, and by default, if a user has visited a given route in the last 30 seconds, navigating back to that route again within 30 seconds will serve up the same React component tree from the last visit and won&#8217;t make a request to the server at all.</li>
</ul>



<h3 id="h-automatic-code-splitting" class="wp-block-heading">Automatic Code Splitting</h3>



<p class="wp-block-paragraph">Code splitting involves splitting up your JavaScript bundle so that the user&#8217;s browser only downloads enough JavaScript to render the currently viewed web page. In other words, if there&#8217;s only one page in our app that utilizes the react-query package, we can use code splitting to ensure that the web browser doesn&#8217;t need to download react-query&#8217;s bundled JavaScript until they visit that specific page &#8211; visiting other pages will not trigger that chunk of JavaScript to be downloaded.</p>



<p class="wp-block-paragraph">This is something we&#8217;ve been able to do in React for a very long time now, but it&#8217;s fairly involved and is easy to get it wrong. So the fact that Next.js does this automatically for us, and does a good job with it, makes it that much easier to develop an app that delivers as fast and performant an experience as possible.</p>



<h3 id="h-improved-seo" class="wp-block-heading">Improved SEO</h3>



<p class="wp-block-paragraph">Although many of the web application projects we work are not concerned with SEO, some are. For those web applications where good SEO (Search Engine Optimization) is required or at least desired, Next.js helps us achieve better SEO through the following mechanisms:</p>



<ul class="wp-block-list">
<li>Search engine crawlers are better able to fully process the contents of a web page that was rendered on the server than those that are rendered on the client with JavaScript. Next.js enables us to render all our content on the server, and this in turn boosts SEO.</li>



<li>Performance is a factor in SEO. The faster a web page loads, the higher search engine ranking it will get. Next.js&#8217; many mechanisms for improving performance also result in better SEO.</li>



<li>Because most of your links will be rendered on the server with server components, search engine crawlers will have an easier time indexing all the pages on your web site or web application.</li>
</ul>



<p class="wp-block-paragraph">A takeaway here is that, if SEO is important for your web application or website, Next.js is one of your best framework options.</p>



<h2 id="h-developer-experience" class="wp-block-heading"><a name="developer-experience">Developer Experience</a></h2>



<p class="wp-block-paragraph">Developer experience is a broad term that covers workplace culture, management process, and workflow, but for the sake of this article we&#8217;re going to focus on one aspect of developer experience: tools.</p>



<p class="wp-block-paragraph">If you&#8217;re a contractor who builds houses, but you restrict your team from using power tools, your employees are not going to enjoy working for you but equally important is the fact that it will take you more hours and money to complete a project. Similarly, the tools a software development team uses can have an enormous impact on their productivity. A toolset that&#8217;s easy to work with and fits the project&#8217;s specific needs&nbsp;can result in us shipping more features for our client&#8217;s web application for less money, which means we deliver more value to our clients.</p>



<p class="wp-block-paragraph">So let&#8217;s look at some of the ways that Next.js improves the developer experience.</p>



<h3 id="h-simpler-code" class="wp-block-heading">Simpler code</h3>



<p class="wp-block-paragraph">Two of Next.js&#8217; big features &#8211; Server Actions and React Server Components &#8211; streamline the codebase of a React app in a very big way. I did a demo a while back where I developed the same form two ways: with a plain React component using hooks for state management, and with server components and server actions. The difference was bigger than I&#8217;d expected &#8211; the traditional React component was 125 lines of code whereas the Next.js version was only 29 lines of code. In the screenshot below, you can see the &#8220;plain React&#8221; version on the left and the Next.js version on the right.</p>



<p class="wp-block-paragraph">Comparison between a plain React component and the same component written in Next.js</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/react-form-comparison.png" alt="Comparison between a plain React component and the same component written in Next.js"/></figure>



<p class="wp-block-paragraph">It should be noted that neither version of this component utilizes any third party libraries &#8211; only built-in React features. The version on the right simply uses a handful of custom, reusable components, each only a few lines of very simple code. Both versions of the form component have the same behavior: a loading indicator for the select list of categories, temporarily disabling the submit button while the form submission is pending, and a display for error messages a &#8220;form saved successfully&#8221; status message.</p>



<p class="wp-block-paragraph">Not only does the Next.js version of this form component take much less effort to develop, but the code is also much more straightforward &#8211; it&#8217;s purely declarative and contains no imperative logic whatsoever. That almost completely eliminates the potential for buggy behavior (at least on the client-side), but it also makes it very easy to modify and maintain this component in the future. A developer can jump into this code file and understand everything about it with a quick glance.</p>



<p class="wp-block-paragraph">This has the potential to greatly reduce the amount of effort, and therefore money, required to build a web application. The key word there is potential &#8211; an app that requires a large amount of very intricate client-side interactivity may not see as much benefit in terms of the simplicity of the code base and the effort required to develop the app.</p>



<h3 id="h-enhanced-developer-tools" class="wp-block-heading">Enhanced Developer Tools</h3>



<p class="wp-block-paragraph">Hot reloading has become a staple of front end development. When I save a change to a file in my application, I want to see that change reflected in my web browser without needing to restart the application or even reload my browser. Next.js provides a particularly smooth hot reloading experience. If I save a change to a server component, hot reload will cause that server component to automatically be requested and re-rendered again, updating the UI instantaneously for all intents and purposes. The same is true with client-side components. This full-stack hot reload makes testing changes during development a breeze.</p>



<p class="wp-block-paragraph">Also, I&#8217;ve found that generative AI tools like GitHub Copilot and ChatGPT are particularly effective at generating code for server components and server actions. Similarly, the server side of a Next.js app will use Node.js APIs and libraries, which generative AI also has an easy time with. So tools like GitHub Copilot can enhance a Next.js developer&#8217;s productivity even further, which again, helps us deliver more value to our clients when working on a web application.</p>



<h3 id="h-rich-ecosystem-and-community-support" class="wp-block-heading">Rich Ecosystem and Community Support</h3>



<p class="wp-block-paragraph">The old adage about popularity contests being bad <em>does not</em> apply to software development. The more popular a programming language or framework is:</p>



<ul class="wp-block-list">
<li>The easier it is to recruit new developers that are already familiar with it.</li>



<li>The easier it is to find libraries or packages for that framework/library.</li>



<li>The easier it is to find answers to problems or questions via a Google search or a StackOverflow question.</li>
</ul>



<p class="wp-block-paragraph">Next.js with the app router is picking up in terms of popularity, but it also benefits from the fact that Node.js and React are both incredibly popular. In the <a href="https://survey.stackoverflow.co/2023/#section-most-popular-technologies-web-frameworks-and-technologies">2023 StackOverflow Developer Survey</a>, the two most popular web technologies <em>by a mile</em> were Node.js and React:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/stackoverflow-survey-results.png" alt="StackOverflow developer survey results showing both Node.js and React as the most popular technologies, with more than 40% of respondents using them"/></figure>



<p class="wp-block-paragraph">The takeaway here is that it is very easy to find support, recruits, and libraries when working with Next.js, more than with any other full-stack web framework. One caveat is that there are a lot of React component libraries/packages out there that have not been updated to work in React Server Components, and may never receive such an update. However, any existing third-party React component will work just fine if used from a client component. Some components will need to be imported in a specific way, but as an extreme example I was able to use a very client-heavy React component from an NPM package that hasn&#8217;t been updated since 2019 and it worked just fine. There is some misinformation out there around this point. The most you&#8217;ll have to do to make this work is to do something like the following when important a component:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">TypeScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>const ColorThemePicker = dynamic(() => import("./colorThemePicker"), {
  ssr: false,
});</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">ColorThemePicker</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">dynamic</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">import</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;./colorThemePicker&quot;</span><span style="color: #D4D4D4">), {</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #9CDCFE">ssr:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">});</span></span></code></pre></div>



<h2 id="h-case-studies-and-success-stories" class="wp-block-heading"><a name="case-studies-and-success-stories">Case Studies and Success Stories</a></h2>



<h3 id="h-industry-examples" class="wp-block-heading">Industry Examples</h3>



<p class="wp-block-paragraph">Working with Next.js, we&#8217;re in good company. The following is a small sample of businesses and organizations that have found success with Next.js.</p>



<ul class="wp-block-list">
<li><a href="https://vercel.com/blog/frameio-never-drop-the-illusion" target="_blank">Frame.io (an Adobe company)</a></li>



<li><a href="https://vercel.com/blog/washington-post-next.js-vercel-engineering-at-the-speed-of-breaking-news?nxtPslug=washington-post-next.js-vercel-engineering-at-the-speed-of-breaking-news&amp;utm_source=next-site&amp;utm_medium=wapo_callout&amp;utm_campaign=showcase-new" target="_blank">Washington Post</a></li>



<li><a href="https://vercel.com/blog/runway-enables-next-generation-content-creation-with-ai-and-vercel?utm_source=next-site&amp;utm_medium=runway_callout&amp;utm_campaign=showcase-new" target="_blank">runway</a></li>



<li><a href="https://vercel.com/blog/a-better-developer-experience-makes-building-cruise-critic-more-efficient" target="_blank">Cruise Critic (a TripAdvisor subsidiary)</a></li>



<li><a href="https://nextjs.org/showcase" target="_blank">Others</a> including Nike, ChatGPT, Spotify, Netflix, and Hulu.</li>
</ul>



<h3 id="h-internal-benefits" class="wp-block-heading">Internal Benefits</h3>



<p class="wp-block-paragraph">DMC has been piloting Next.js for a year now, it&#8217;s helped ensure success on several projects:</p>



<ul class="wp-block-list">
<li>DMC has already leveraged Next.js (with App Router) to help a client with a rewrite of their order management web portal. Next.js&#8217; streamlined developer experience and robust features allowed us to complete this project well under budget and deliver a web application that our client was happy with.</li>



<li>We wrote a new internal application (budget tracking app) using Next.js, again with the App Router.</li>



<li>We rewrote several internal web applications using Next.js, and the result for each application was a more performant application with a smoother UX and a simpler codebase.</li>
</ul>



<h2 id="h-future-outlook" class="wp-block-heading"><a name="future-outlook">Future Outlook</a></h2>



<h3 id="h-upcoming-features-and-roadmap" class="wp-block-heading">Upcoming Features and Roadmap</h3>



<p class="wp-block-paragraph">Next.js represents the cutting edge of web application development technology. Moving forward, they plan to improve integration for React 19 features, as well as ship support for the React Compiler, further improving the productivity of our web development team and allow us to achieve more functionality with less code. Next.js is also continually improving its caching features and is implementing <a href="https://nextjs.org/blog/next-14#partial-prerendering-preview" target="_blank">partial prerendering</a>, so this framework aims to become ever more performant as time goes on.</p>



<p class="wp-block-paragraph">DMC looks forward to leveraging Next.js and the App Router to deliver more value to our clients by efficiently building better web applications.</p>



<h3 id="h-ongoing-learning-and-adaptation" class="wp-block-heading">Ongoing Learning and Adaptation</h3>



<p class="wp-block-paragraph">As with any other technology, Next.js has a learning curve. Next.js is an opinionated framework, and our senior application developers have invested time in learning how to work <em>with</em> those opinions rather than against them. Our next steps are to disseminate that knowledge throughout our team so that we can apply best practices to the web application projects we take on for our clients.</p>



<p class="wp-block-paragraph">Aside from internally developed training content, the best learning resources for the Next.js App Router are:</p>



<ul class="wp-block-list">
<li><a href="https://nextjs.org/learn" target="_blank">The official Next.js tutorial</a></li>



<li><a href="https://nextjs.org/docs" target="_blank">The Next.js documentation</a></li>
</ul>



<h2 id="h-conclusion" class="wp-block-heading"><a name="conclusion">Conclusion</a></h2>



<h3 id="h-summary-of-key-points" class="wp-block-heading">Summary of Key Points</h3>



<p class="wp-block-paragraph">To recap, Next.js is an excellent tool for us to utilize on web application projects. Aside from objective technical advantages like performance and SEO, Next.js&#8217; core features allow us to develop a given web application with simpler code and&nbsp;less code, and will often produce a codebase that is easier to maintain over time. The end result of all of this is that our clients end up getting more bang for their buck on those projects that are a good fit for Next.js.</p>



<p class="wp-block-paragraph">To learn more about the benefits of using Next.js for web development, check out the <a href="https://nextjs.org/" target="_blank">Next.js website</a>, and to learn how to use Next.js check out <a href="https://nextjs.org/docs" target="_blank">the official documentation</a>.</p>



<p class="wp-block-paragraph">If you&#8217;re looking to develop a new application or rewrite an existing web application, DMC&#8217;s expert web developers can help you get up and running fast with a high-quality web application that serves your business needs.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Why Next.js Is Earning a Place in DMC’s Development Toolkit</strong>.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Explore our <a href="https://static.dmcinfo.com/services/application-development/" data-type="page" data-id="434">Application Development</a> expertise in using Next.js to create faster, scalable, and maintainable <a href="https://static.dmcinfo.com/services/application-development/web-application-development/" data-type="page" data-id="692">web applications</a> with a modern developer experience.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/16026/why-dmc-is-adopting-next-js-for-web-application-development/">Why DMC is Adopting Next.js for Web Application Development</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>React Smart Components: Lessons Learned While Upgrading an Old Redux Application</title>
		<link>https://static.dmcinfo.com/blog/16056/react-smart-components-lessons-learned-while-upgrading-an-old-redux-application/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Thu, 15 Aug 2024 11:38:25 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[Web Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/16056/react-smart-components-lessons-learned-while-upgrading-an-old-redux-application/</guid>

					<description><![CDATA[<p>Recently, we decided to modernize a legacy web application. This application was written back in 2018, before hooks were introduced to React. We used Redux for state management. As the years went on, and more features were added to React, Redux started to feel like an unnecessary complexity for this application. So, the first question [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/16056/react-smart-components-lessons-learned-while-upgrading-an-old-redux-application/">React Smart Components: Lessons Learned While Upgrading an Old Redux Application</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Recently, we decided to modernize a legacy <a href="https://static.dmcinfo.com/services/application-development/web-application-development">web application</a>. This application was written back in 2018, before hooks were introduced to React. We used Redux for state management. As the years went on, and more features were added to React, Redux started to feel like an unnecessary complexity for this application.</p>



<p class="wp-block-paragraph">So, the first question to answer was this: should we update this application to use the latest version of Redux (preferably with the <a href="https://redux-toolkit.js.org/" target="_blank">Redux Toolkit</a>), or should we completely replace our Redux store—along with all the accompanying actions, reducers, and sagas—with simpler, lightweight state management in the form of hooks and libraries like <a href="https://tanstack.com/query/latest/docs/framework/react/overview" target="_blank">React Query</a>?</p>



<p class="wp-block-paragraph">One of my biggest complaints about the way we used Redux in this application is that it requires writing a lot of boilerplate. If&nbsp;we were to stick with Redux, we would want to switch to Redux Toolkit, which allows you to accomplish the same functionality with a fraction of the boilerplate. However, this would involve rewriting all our Redux code. I did a proof of concept with a small portion of our Redux store, and it was clear that updating our Redux codebase would be akin to rewriting all our state management. So, keeping Redux in this application would not take less effort than just replacing Redux entirely for state management.</p>



<p class="wp-block-paragraph">We explored the path of switching from Redux to hooks for our state management, and it turned out to be surprisingly straightforward. In this blog post, we&#8217;ll explore why that is and what implications this has for structuring a React codebase. In the process, we&#8217;ll discuss whether an old concept—smart components vs. dumb components—still has a place in modern React development.</p>



<h2 id="h-sample-of-our-redux" class="wp-block-heading">Sample of Our Redux</h2>



<p class="wp-block-paragraph">To demonstrate what &#8220;flavor&#8221; of Redux our application used, let&#8217;s look at a component named &#8220;NewEntryForm&#8221;:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
interface FormProps {
    isVisible: boolean;
    selectedEmployeeName: string;
    selectedDate: Date | null;
    employees: Employee[];
    selectEmployee: (name: string) => void;
    selectDate: (date: Date | null) => void;
    closeForm: () => void;
    save: () => void;
}

export function NewEntryForm({
    closeForm,
    isVisible,
    save,
    selectedDate,
    selectedEmployeeName,
    selectDate,
    selectEmployee,
    employees
}: FormProps) {
    return (
        &lt;panel islightdismiss="{true}" isopen="{isVisible}" ondismiss="{closeForm}" type="{PanelType.smallFixedFar}">
            &lt;dropdown idropdownoption="" label="Employee" onchanged="{(item:" selectedkey="{selectedEmployeeName}"> selectEmployee(item.key as string)}
                options={employees.map(e => ({ key: e.name, text: e.name } as IDropdownOption))}
            />
            &lt;datepicker firstdayofweek="{DayOfWeek.Sunday}" value="{selectedDate"> selectDate(date || null)}
            />&lt;button onclick="{closeForm}">Cancel&lt;/button>&lt;button onclick="{save}">Save&lt;/button>
        &lt;/datepicker>&lt;/dropdown>&lt;/panel>
    );
}

export default connect(
    (state: FullStore) => ({
        isVisible: newEntryFormIsVisibleSelector(state),
        selectedEmployeeName: newEntryEmployeeNameSelector(state),
        selectedDate: newEntryDateSelector(state),
        employees: employeeOptionsForNewEntryFormSelector(state)
    }),
    (dispatch: Dispatch&lt;appaction>) =>
        bindActionCreators(
            {
                selectEmployee: selectEmployeeForNewEntry,
                selectDate: selectDateForNewEntry,
                closeForm: hideNewEntryForm,
                save: saveNewTimeEntry
            },
            dispatch
        )
)(NewEntryForm);
&lt;/appaction></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #569CD6">interface</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">FormProps</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">isVisible</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">boolean</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Date</span><span style="color: #D4D4D4"> | </span><span style="color: #4EC9B0">null</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">employees</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Employee</span><span style="color: #D4D4D4">[];</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">selectEmployee</span><span style="color: #D4D4D4">: (</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">selectDate</span><span style="color: #D4D4D4">: (</span><span style="color: #9CDCFE">date</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Date</span><span style="color: #D4D4D4"> | </span><span style="color: #4EC9B0">null</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">closeForm</span><span style="color: #D4D4D4">: () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">save</span><span style="color: #D4D4D4">: () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">NewEntryForm</span><span style="color: #D4D4D4">({</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">closeForm</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">isVisible</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">save</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">selectDate</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">selectEmployee</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">employees</span></span>
<span class="line"><span style="color: #D4D4D4">}: </span><span style="color: #4EC9B0">FormProps</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">panel</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">islightdismiss</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{true}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">isopen</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{isVisible}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ondismiss</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{closeForm}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{PanelType.smallFixedFar}&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">dropdown</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">idropdownoption</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Employee&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onchanged</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{(item:&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">selectedkey</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{selectedEmployeeName}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4"> selectEmployee(item.key as string)}</span></span>
<span class="line"><span style="color: #D4D4D4">                options=</span><span style="color: #569CD6">{</span><span style="color: #9CDCFE">employees</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">map</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> ({ </span><span style="color: #9CDCFE">key:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">text:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IDropdownOption</span><span style="color: #D4D4D4">))</span><span style="color: #569CD6">}</span></span>
<span class="line"><span style="color: #D4D4D4">            /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">datepicker</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">firstdayofweek</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{DayOfWeek.Sunday}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{selectedDate&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4"> selectDate(date || null)}</span></span>
<span class="line"><span style="color: #D4D4D4">            /&gt;</span><span style="color: #808080">&lt;</span><span style="color: #569CD6">button</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{closeForm}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">Cancel</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">button</span><span style="color: #808080">&gt;&lt;</span><span style="color: #569CD6">button</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{save}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">Save</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">button</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">datepicker</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">dropdown</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">panel</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">connect</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">    (</span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">FullStore</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> ({</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">isVisible:</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">newEntryFormIsVisibleSelector</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">selectedEmployeeName:</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">newEntryEmployeeNameSelector</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">selectedDate:</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">newEntryDateSelector</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">employees:</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">employeeOptionsForNewEntryFormSelector</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">state</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">    }),</span></span>
<span class="line"><span style="color: #D4D4D4">    (</span><span style="color: #9CDCFE">dispatch</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Dispatch</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">appaction</span><span style="color: #D4D4D4">&gt;) </span><span style="color: #569CD6">=&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">bindActionCreators</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">selectEmployee:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">selectEmployeeForNewEntry</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">selectDate:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">selectDateForNewEntry</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">closeForm:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">hideNewEntryForm</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">save:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">saveNewTimeEntry</span></span>
<span class="line"><span style="color: #D4D4D4">            },</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">dispatch</span></span>
<span class="line"><span style="color: #D4D4D4">        )</span></span>
<span class="line"><span style="color: #D4D4D4">)(</span><span style="color: #9CDCFE">NewEntryForm</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">appaction</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">Although this code is now seven&nbsp;years old and pre-dates hooks, it should look familiar to anyone who&#8217;s used React with TypeScript. We&#8217;re declaring the type of props, and then we&#8217;re declaring a <em>pure</em> functional component. I call this component pure because it will always render the same output for a given set of props.</p>



<p class="wp-block-paragraph">The part that&#8217;s unique to old-school Redux is the bit involving <code>connect()</code> at the bottom. This is a higher-order component provided by the React-Redux library, and it does exactly what it sounds like—it &#8220;connects&#8221; a React component to the Redux store. It gets various action handlers and pieces of state from the Redux store and provides those things as <em>props</em> to our React component. Here&#8217;s where I get to invoke a term that was popular in the early days of React.</p>



<p class="wp-block-paragraph">In this example, the function &#8220;NewEntryForm&#8221; is what is known as a <em>presentational component</em>. It does nothing more than take props and render JSX based on those props. It doesn&#8217;t manage any of its own state. The component created by the <code>connect</code> function, on the other hand, is what&#8217;s known as a <em>container component</em>. Container and presentational components are also known as <em>smart </em>and <em>dumb</em> components, or <em>stateful </em>and <em>pure</em> components.</p>



<p class="wp-block-paragraph">When I want to use this component, I can choose to use the smart component by doing a default import:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
import NewEntryForm from './newEntryForm'
Or I can choose to use the "dumb" component by doing a regular import:

import {NewEntryForm} from './newEntryForm'
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">NewEntryForm</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;./newEntryForm&apos;</span></span>
<span class="line"><span style="color: #9CDCFE">Or</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">I</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">can</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">choose</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">to</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">use</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">the</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;dumb&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">component</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">by</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">doing</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">a</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">regular</span><span style="color: #D4D4D4"> </span><span style="color: #C8C8C8">import</span><span style="color: #D4D4D4">:</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">import</span><span style="color: #D4D4D4"> {</span><span style="color: #9CDCFE">NewEntryForm</span><span style="color: #D4D4D4">} </span><span style="color: #C586C0">from</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;./newEntryForm&apos;</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">One of the advantages of this approach is that it makes the component more reusable. If I want to reuse this same component in two places, but it needs to get its state from somewhere different in both places, I can simply have two different <code>connect()</code> HOCs that get the props from different parts of the Redux store and use them both to render the NewEntryForm component. In other words, two smart components rendering the same <em>dumb</em> component.</p>



<h2 id="h-replacing-redux-with-hooks" class="wp-block-heading">Replacing Redux with Hooks</h2>



<p class="wp-block-paragraph">With few exceptions, every component in the application followed this pattern. We were storing most of our application&#8217;s state in the Redux store, and so all our smart components were just using <code>connect()</code> to grab state and event handlers from the Redux store and passing them to our dumb components as props.</p>



<p class="wp-block-paragraph">This is an extremist approach to Redux development, but it did come with a benefit: we can replace the state management for the application with minimal changes to our component files themselves.</p>



<p class="wp-block-paragraph">Take another look at that NewEntryForm component file. Everything up until the <code>connect()</code> part can stay the same, and I can just replace the <code>connect()</code> HOC with the following:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
interface NewEntryFormContainerProps {
    closeForm: () => void;
    save: (employeeName: string, date: Date) => void;
}

export default function NewEntryFormContainer({ closeForm, save }: NewEntryFormContainerProps) {
    const employeesQuery = useAllEmployeesQuery();
    const employees = useMemo(
        () =>
            employeesQuery.data
                ? linq
                      .from(employeesQuery.data)
                      .where((e) => e.isActive)
                      .orderBy((e) => e.name)
                      .toArray()
                : [],
        &#91;employeesQuery.data&#93;
    );
    const &#91;selectedEmployeeName, setSelectedEmployeeName&#93; = useState&lt;string>();
    const &#91;selectedDate, selectDate&#93; = useState&lt;date>(null);
    const saveCallback = useCallback(() => {
        if (!selectedEmployeeName || !selectedDate) {
            return;
        }
        save(selectedEmployeeName, selectedDate);
    }, &#91;selectedEmployeeName, selectedDate, save&#93;);

    return (
        &lt;newentryform isvisible="{true}" selectedemployeename="{selectedEmployeeName">
    );
}
&lt;/newentryform>&lt;/date>&lt;/string></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #569CD6">interface</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">NewEntryFormContainerProps</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">closeForm</span><span style="color: #D4D4D4">: () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">save</span><span style="color: #D4D4D4">: (</span><span style="color: #9CDCFE">employeeName</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">date</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Date</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">NewEntryFormContainer</span><span style="color: #D4D4D4">({ </span><span style="color: #9CDCFE">closeForm</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">save</span><span style="color: #D4D4D4"> }: </span><span style="color: #4EC9B0">NewEntryFormContainerProps</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">employeesQuery</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useAllEmployeesQuery</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">employees</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useMemo</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        () </span><span style="color: #569CD6">=&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">employeesQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span></span>
<span class="line"><span style="color: #D4D4D4">                ? </span><span style="color: #9CDCFE">linq</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">from</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">employeesQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">where</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">isActive</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">orderBy</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">toArray</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">                : [],</span></span>
<span class="line"><span style="color: #D4D4D4">        &#91;</span><span style="color: #9CDCFE">employeesQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">setSelectedEmployeeName</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">selectedDate</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">selectDate</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">date</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">saveCallback</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useCallback</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4"> || !</span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">save</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }, &#91;</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">save</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">newentryform</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">isvisible</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{true}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">selectedemployeename</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{selectedEmployeeName&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #808080">&lt;/</span><span style="color: #569CD6">newentryform</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">date</span><span style="color: #D4D4D4">&gt;&lt;/</span><span style="color: #9CDCFE">string</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">This new component still has two props (closeForm and save) because in the new version of the app, we decided to keep that state at a higher level of our component tree, but we&#8217;re getting everything else from some local hooks instead of from Redux. We&#8217;re now getting &#8220;selectedEmployeeName&#8221; and &#8220;setSelectedEmployeeName&#8221; from <code>useState()</code> instead of from a Redux store. We&#8217;re getting our array of employees from <code>useQuery()</code>, and we&#8217;re doing some filtering and sorting within a <code>useMemo()</code> to get the final value that we pass to the &#8220;employees&#8221; prop of the presentational component.</p>



<p class="wp-block-paragraph">To summarize, this component has a very specific job: manage state and provide that state as props to a presentational component.</p>



<p class="wp-block-paragraph">If we want, we can also choose to put this &#8220;smart&#8221; or &#8220;container&#8221; component in a separate file. We could name the file with the presentational component &#8220;newEntryForm.presentation.tsx&#8221; and we could name this new file &#8220;newEntryForm.container.tsx&#8221;.</p>



<p class="wp-block-paragraph">This container vs. presentation paradigm isn&#8217;t specific to migrating from Redux. Migrating from Redux to any other state management solution (Zustand, MobX, perhaps even React Server Components in a framework like Next.js) would be simpler than it would have been if our state and presentation was intermingled.</p>



<h2 id="h-does-this-mean-container-components-are-making-a-comeback" class="wp-block-heading">Does This Mean Container Components are Making a Comeback?</h2>



<h3 id="h-benefits-of-separation-of-concerns" class="wp-block-heading">Benefits of Separation of Concerns</h3>



<p class="wp-block-paragraph">With that in mind, some benefits of having separate container components are:</p>



<ul class="wp-block-list">
<li><strong>Code reuse.</strong> Presentational components are more flexible and easier to reuse, because they maintain no state of their own. In the example above, I&#8217;m getting an array of all active employees for the &#8220;employees&#8221; value. If in a different part of the app I want to reuse this same form but only allow the user to select from their direct reports instead of&nbsp;<em>all</em>&nbsp;employees, I can reuse the &#8220;NewEntryForm&#8221; component as is by&nbsp;wrapping it in a container that gets the &#8220;employees&#8221; array from a query of the user&#8217;s direct reports. Same presentational component, different container.<br>
 &nbsp;</li>



<li><strong>Easier refactoring of state.</strong> In the example above, if I want to switch from using hooks for state management and instead use Zustand for state management, I don&#8217;t need to change anything about my &#8220;NewEntryForm&#8221; component. I only have to modify or rewrite the container. Even for a more drastic change, like migrating the application to Next.js with app router and React Server Components (RSCs), I can migrate my &#8220;NewEntryForm&#8221; component unchanged and only have to replace my container component with a server component. That server component would probably involve using a database request to get the array of employees. We might also replace the &#8220;save&#8221; function with a server action. Finally, we would likely still have a client component for the container, which is where we would set up our &#8220;useState&#8221; and other hooks. In the end, the NewEntryForm component doesn&#8217;t need to be changed at all.</li>
</ul>



<p class="wp-block-paragraph">The separation of concerns principle has other benefits, but some of those benefits may not apply to the presentational vs. container components paradigm. One of the benefits of separation of concerns is maintainability—you can make changes to one layer without changing the other. In the example above, we could switch from, e.g., a Dropdown for the employee select to a ComboBox, and we only need to modify the NewEntryForm (presentational) component to do that. We don&#8217;t need to touch the container component.</p>



<p class="wp-block-paragraph">In a real application, this may not help us as much as we&#8217;d like. If we need to add a new feature to this form, e.g., a new &#8220;Comments&#8221; field, we&#8217;ll need to modify both the presentation component and the container—add the new field to the presentation component&#8217;s JSX and add the new piece of state to the container. There are a lot of feature requests or bug fixes that will require modifying both components, which is why many of the benefits of separation of concerns won&#8217;t necessarily apply to container vs. presentational components.</p>



<h2 id="h-functional-components-already-have-separation-of-concerns-built-in" class="wp-block-heading">Functional Components Already Have Separation of Concerns Built in</h2>



<p class="wp-block-paragraph">One reason why container components aren&#8217;t talked about as much as they used to be is because of the nature of functional components and the ease with which they can be refactored. Let&#8217;s write a very simple React component to demonstrate this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
function CustomersList() {
    const customersQuery = useQuery({
        queryKey: &#91;'customers'&#93;,
        queryFn: async () => {
            const response = await fetch('http://someUrl/')
            return response.json()
        }
    })
    const customers = useMemo(() => customersQuery.data || [], &#91;customersQuery&#93;)

    return ({customers.map(c => {c.name})}) 
}
        </textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CustomersList</span><span style="color: #D4D4D4">() {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">customersQuery</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useQuery</span><span style="color: #D4D4D4">({</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">queryKey:</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #CE9178">&apos;customers&apos;</span><span style="color: #D4D4D4">&#93;,</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">queryFn</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">response</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">fetch</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;http://someUrl&/#39;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">json</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    })</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">customers</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useMemo</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">customersQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4"> || [], &#91;</span><span style="color: #9CDCFE">customersQuery</span><span style="color: #D4D4D4">&#93;)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> ({customers.map(</span><span style="color: #9CDCFE">c</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span><span style="color: #9CDCFE">c</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">})}) </span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">        </span></span></code></pre></div>



<p class="wp-block-paragraph">The above component uses <code>react-query</code>&#8216;s &#8220;useQuery&#8221; hook to fetch data from some URL, and then it renders an unordered list based on the data that comes back.</p>



<p class="wp-block-paragraph">At a glance, we can tell this is not a presentational component, because it manages its own state. It&#8217;s also not a container component,&nbsp;because it renders JSX instead of just passing that state as props to other custom components. So, is it just a big nothing?</p>



<p class="wp-block-paragraph">Another way to look at it is that we have two distinct sections of code here: we have a &#8220;state&#8221; section where we&#8217;re doing our <code>useQuery()</code> and <code>useMemo()</code> stuff, and we have a &#8220;presentation&#8221; section where we&#8217;re rendering our UI with JSX. That structure makes it very easy to refactor and restructure this code. If, a couple of months later in development, we want to reuse the presentation part of this component, we can just highlight the <code>return</code> statement of this component and cut/paste it into a new component. There are even some Visual Studio Code extensions that allow you to refactor a React component like this with the click of a button.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
// here we have our refactored presentation component

function CustomersList (props: {customers: Customer[]}) {
    const customers = props.customers
    return ({customers.map(c =>{c.name})}) 
} // and then we have our original state in a container 
    
function CustomersFromEndpoint () { 
    const customersQuery = useQuery({ queryKey: &#91;'customers'&#93;, queryFn: async () => { const response = await fetch('http://someUrl/')   return response.json() } }) 
    const customers = useMemo( () => customersQuery.data || [], &#91;customersQuery&#93; ) 
    return  
} // and now we can re-use our presentation component with another

// container with different state, this time getting the customers list // from some hook that interacts with IndexedDB 

function CustomersFromIndexedDB () { const customers = useIndexedDBCustomersTable() return  }</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #6A9955">// here we have our refactored presentation component</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CustomersList</span><span style="color: #D4D4D4"> (</span><span style="color: #9CDCFE">props</span><span style="color: #D4D4D4">: {</span><span style="color: #9CDCFE">customers</span><span style="color: #D4D4D4">: </span><span style="color: #4EC9B0">Customer</span><span style="color: #D4D4D4">[]}) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">customers</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">props</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">customers</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> ({customers.map(</span><span style="color: #9CDCFE">c</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4">{</span><span style="color: #9CDCFE">c</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">})}) </span></span>
<span class="line"><span style="color: #D4D4D4">} </span><span style="color: #6A9955">// and then we have our original state in a container </span></span>
<span class="line"><span style="color: #D4D4D4">    </span></span>
<span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CustomersFromEndpoint</span><span style="color: #D4D4D4"> () { </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">customersQuery</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useQuery</span><span style="color: #D4D4D4">({ </span><span style="color: #9CDCFE">queryKey:</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #CE9178">&apos;customers&apos;</span><span style="color: #D4D4D4">&#93;, </span><span style="color: #DCDCAA">queryFn</span><span style="color: #9CDCFE">:</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">async</span><span style="color: #D4D4D4"> () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> { </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">response</span><span style="color: #D4D4D4"> = </span><span style="color: #C586C0">await</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">fetch</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;http://someUrl&/#39;</span><span style="color: #D4D4D4">)   </span><span style="color: #9CDCFE">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">response</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">json</span><span style="color: #D4D4D4">() } }) </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">customers</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useMemo</span><span style="color: #D4D4D4">( () </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">customersQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4"> || [], &#91;</span><span style="color: #9CDCFE">customersQuery</span><span style="color: #D4D4D4">&#93; ) </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">  </span></span>
<span class="line"><span style="color: #D4D4D4">} </span><span style="color: #6A9955">// and now we can re-use our presentation component with another</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">// container with different state, this time getting the customers list // from some hook that interacts with IndexedDB </span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">CustomersFromIndexedDB</span><span style="color: #D4D4D4"> () { </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">customers</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useIndexedDBCustomersTable</span><span style="color: #D4D4D4">() </span><span style="color: #9CDCFE">return</span><span style="color: #D4D4D4">  }</span></span></code></pre></div>



<p class="wp-block-paragraph">It could be argued that we no longer need to think ahead and separate our components into presentational and container components because we can separate them whenever we want on a moment&#8217;s notice.</p>



<h2 id="h-it-s-a-spectrum-not-a-binary-choice" class="wp-block-heading">It&#8217;s a Spectrum, not a Binary Choice</h2>



<p class="wp-block-paragraph">Like so many things in life, the statefulness of a React component is not a binary affair; it is a spectrum. Let&#8217;s revisit our NewEntryForm example, but in a new way:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
export default function NewEntryFormContainer({ closeForm, save }: NewEntryFormContainerProps) {
    const employeesQuery = useAllEmployeesQuery();
    const employees = useMemo(
        () =>
            employeesQuery.data
                ? linq
                      .from(employeesQuery.data)
                      .where((e) => e.isActive)
                      .orderBy((e) => e.name)
                      .toArray()
                : [],
        &#91;employeesQuery.data&#93;
    );
    const &#91;selectedEmployeeName, setSelectedEmployeeName&#93; = useState&lt;string>();
    const &#91;selectedDate, selectDate&#93; = useState&lt;date>(null);
    const saveCallback = useCallback(() => {
        if (!selectedEmployeeName || !selectedDate) {
            return;
        }
        save(selectedEmployeeName, selectedDate);
    }, &#91;selectedEmployeeName, selectedDate, save&#93;);

    return (
        &lt;panel islightdismiss="{true}" isopen="{true}" ondismiss="{closeForm}" type="{PanelType.smallFixedFar}">
            &lt;dropdown idropdownoption="" label="Employee" onchanged="{(item:" selectedkey="{selectedEmployeeName}"> setSelectedEmployeeName(item.key as string)}
                options={employees.map((e) => ({ key: e.name, text: e.name } as IDropdownOption))}
            />
            &lt;datepicker firstdayofweek="{DayOfWeek.Sunday}" value="{selectedDate"> selectDate(date || null)}
            />&lt;button onclick="{closeForm}">Cancel&lt;/button>&lt;button onclick="{saveCallback}">Save&lt;/button>
        &lt;/datepicker>&lt;/dropdown>&lt;/panel>
    );
}
&lt;/date>&lt;/string></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">NewEntryFormContainer</span><span style="color: #D4D4D4">({ </span><span style="color: #9CDCFE">closeForm</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">save</span><span style="color: #D4D4D4"> }: </span><span style="color: #4EC9B0">NewEntryFormContainerProps</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">employeesQuery</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useAllEmployeesQuery</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">employees</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useMemo</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">        () </span><span style="color: #569CD6">=&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">employeesQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span></span>
<span class="line"><span style="color: #D4D4D4">                ? </span><span style="color: #9CDCFE">linq</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">from</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">employeesQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">where</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">isActive</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">orderBy</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                      .</span><span style="color: #DCDCAA">toArray</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">                : [],</span></span>
<span class="line"><span style="color: #D4D4D4">        &#91;</span><span style="color: #9CDCFE">employeesQuery</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">setSelectedEmployeeName</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">selectedDate</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">selectDate</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">date</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">saveCallback</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useCallback</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4"> || !</span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">save</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }, &#91;</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">save</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">panel</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">islightdismiss</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{true}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">isopen</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{true}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ondismiss</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{closeForm}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{PanelType.smallFixedFar}&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">dropdown</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">idropdownoption</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Employee&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onchanged</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{(item:&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">selectedkey</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{selectedEmployeeName}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4"> setSelectedEmployeeName(item.key as string)}</span></span>
<span class="line"><span style="color: #D4D4D4">                options=</span><span style="color: #569CD6">{</span><span style="color: #9CDCFE">employees</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">map</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> ({ </span><span style="color: #9CDCFE">key:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">text:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IDropdownOption</span><span style="color: #D4D4D4">))</span><span style="color: #569CD6">}</span></span>
<span class="line"><span style="color: #D4D4D4">            /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">datepicker</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">firstdayofweek</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{DayOfWeek.Sunday}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{selectedDate&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4"> selectDate(date || null)}</span></span>
<span class="line"><span style="color: #D4D4D4">            /&gt;</span><span style="color: #808080">&lt;</span><span style="color: #569CD6">button</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{closeForm}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">Cancel</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">button</span><span style="color: #808080">&gt;&lt;</span><span style="color: #569CD6">button</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{saveCallback}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">Save</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">button</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">datepicker</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">dropdown</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">panel</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">date</span><span style="color: #D4D4D4">&gt;&lt;/</span><span style="color: #9CDCFE">string</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">This version of the NewEntryForm component is neither purely a container nor purely presentational. It maintains its own state (quite a bit of it, at that), and it returns a very well-defined UI (dropdown, a datepicker, and a couple of buttons). But you should note that this component does not manage <em>all</em> of its own state. It&#8217;s still receiving the &#8220;closeForm&#8221; and &#8220;save&#8221; functions as props rather than defining those functions itself.</p>



<p class="wp-block-paragraph">If we wanted to push this component further toward the &#8220;container&#8221; end of the spectrum, we might decide to define that &#8220;save&#8221; function right here within this component. Instead, let&#8217;s push this component a bit more toward the presentation side of the spectrum to enhance reusability:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
export default function NewEntryFormContainer({ employees, closeForm, save }: NewEntryFormContainerProps) {
    const &#91;selectedEmployeeName, setSelectedEmployeeName&#93; = useState&lt;string>();
    const &#91;selectedDate, selectDate&#93; = useState&lt;date>(null);
    const saveCallback = useCallback(() => {
        if (!selectedEmployeeName || !selectedDate) {
            return;
        }
        save(selectedEmployeeName, selectedDate);
    }, &#91;selectedEmployeeName, selectedDate, save&#93;);

    return (
        &lt;panel islightdismiss="{true}" isopen="{true}" ondismiss="{closeForm}" type="{PanelType.smallFixedFar}">
            &lt;dropdown idropdownoption="" label="Employee" onchanged="{(item:" selectedkey="{selectedEmployeeName}"> setSelectedEmployeeName(item.key as string)}
                options={employees.map((e) => ({ key: e.name, text: e.name } as IDropdownOption))}
            />
            &lt;datepicker firstdayofweek="{DayOfWeek.Sunday}" value="{selectedDate"> selectDate(date || null)}
            />&lt;button onclick="{closeForm}">Cancel&lt;/button>&lt;button onclick="{saveCallback}">Save&lt;/button>
        &lt;/datepicker>&lt;/dropdown>&lt;/panel>
    );
}
&lt;/date>&lt;/string></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #C586C0">export</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">default</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">NewEntryFormContainer</span><span style="color: #D4D4D4">({ </span><span style="color: #9CDCFE">employees</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">closeForm</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">save</span><span style="color: #D4D4D4"> }: </span><span style="color: #4EC9B0">NewEntryFormContainerProps</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">setSelectedEmployeeName</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">string</span><span style="color: #D4D4D4">&gt;();</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #4FC1FF">selectedDate</span><span style="color: #D4D4D4">, </span><span style="color: #4FC1FF">selectDate</span><span style="color: #D4D4D4">&#93; = </span><span style="color: #DCDCAA">useState</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">date</span><span style="color: #D4D4D4">&gt;(</span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4FC1FF">saveCallback</span><span style="color: #D4D4D4"> = </span><span style="color: #DCDCAA">useCallback</span><span style="color: #D4D4D4">(() </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">if</span><span style="color: #D4D4D4"> (!</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4"> || !</span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #DCDCAA">save</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">    }, &#91;</span><span style="color: #9CDCFE">selectedEmployeeName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">selectedDate</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">save</span><span style="color: #D4D4D4">&#93;);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">panel</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">islightdismiss</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{true}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">isopen</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{true}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ondismiss</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{closeForm}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{PanelType.smallFixedFar}&quot;</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">dropdown</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">idropdownoption</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">label</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;Employee&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onchanged</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{(item:&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">selectedkey</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{selectedEmployeeName}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4"> setSelectedEmployeeName(item.key as string)}</span></span>
<span class="line"><span style="color: #D4D4D4">                options=</span><span style="color: #569CD6">{</span><span style="color: #9CDCFE">employees</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">map</span><span style="color: #D4D4D4">((</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">) </span><span style="color: #569CD6">=&gt;</span><span style="color: #D4D4D4"> ({ </span><span style="color: #9CDCFE">key:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">text:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4"> } </span><span style="color: #C586C0">as</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IDropdownOption</span><span style="color: #D4D4D4">))</span><span style="color: #569CD6">}</span></span>
<span class="line"><span style="color: #D4D4D4">            /&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #808080">&lt;</span><span style="color: #569CD6">datepicker</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">firstdayofweek</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{DayOfWeek.Sunday}&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">value</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{selectedDate&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4"> selectDate(date || null)}</span></span>
<span class="line"><span style="color: #D4D4D4">            /&gt;</span><span style="color: #808080">&lt;</span><span style="color: #569CD6">button</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{closeForm}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">Cancel</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">button</span><span style="color: #808080">&gt;&lt;</span><span style="color: #569CD6">button</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onclick</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;{saveCallback}&quot;</span><span style="color: #808080">&gt;</span><span style="color: #D4D4D4">Save</span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">button</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #808080">&lt;/</span><span style="color: #569CD6">datepicker</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">dropdown</span><span style="color: #808080">&gt;&lt;/</span><span style="color: #569CD6">panel</span><span style="color: #808080">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">    );</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">date</span><span style="color: #D4D4D4">&gt;&lt;/</span><span style="color: #9CDCFE">string</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">We are now receiving the &#8220;employees&#8221; array as props to this component, instead of managing that state directly in this component via <code>useQuery()</code>. That enhances our reusability, because now we can display this component in multiple places in our application using a different data source for the employee options each time.</p>



<p class="wp-block-paragraph">But we still have a bunch of state defined in this component. This doesn&#8217;t hurt reusability, because these are the kinds of states that <em>should </em>be tightly coupled to the presentation. After all, what good is DatePicker without a corresponding piece of state to keep track of its value? Ditto for the dropdown. An exception there would be if you&#8217;re using server actions along with uncontrolled inputs, but if you&#8217;re doing that, you&#8217;re likely doing the same in each place where you&#8217;re using this component.</p>



<p class="wp-block-paragraph">The other reason this helps with reusability is that now the <em>interface</em> of this component (AKA the props) is smaller, so we don&#8217;t have to define as much state in the parent component to use this component. That results in less repeated code.</p>



<h2 id="h-come-on-dude-just-tell-me-what-to-do" class="wp-block-heading">Come on, Dude, Just Tell Me What to Do</h2>



<p class="wp-block-paragraph">There is no &#8220;always do this&#8221; or &#8220;never do that&#8221; kind of rule to take away from this article. Here are my takeaways:</p>



<ol class="wp-block-list">
<li>Having a strict <strong>separation of presentation components and container components </strong>will result in writing more boilerplate, but it has the benefit of giving you a crystal-clear scope of what needs to be rewritten if you ever decide to change your state management strategy, e.g., from Redux to hooks or hooks to Zustand.<br> &nbsp;</li>



<li>The nature of <strong>functional components and hooks lends itself to easy refactoring</strong>. If you generally follow sound component design principles, any decisions you make regarding the statefulness of a component are not permanent, and you can always change your mind.<br> &nbsp;</li>



<li><strong>Statefulness is a spectrum</strong>. It is not a dichotomy of containers and presentational components. A component can maintain some of its own state without hurting its reusability, and doing so can even increase its reusability. It is perfectly fine to get some things from props while also maintaining some state via hooks.</li>
</ol>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Modernize React State Management Beyond Redux</strong>.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Explore our <a href="https://static.dmcinfo.com/services/application-development/" data-type="page" data-id="434">Application Development</a> expertise in upgrading legacy React applications with hooks, React Query, and reusable component architectures.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/16056/react-smart-components-lessons-learned-while-upgrading-an-old-redux-application/">React Smart Components: Lessons Learned While Upgrading an Old Redux Application</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SQL Server Performance Troubleshooting</title>
		<link>https://static.dmcinfo.com/blog/16708/sql-server-performance-troubleshooting/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Thu, 18 Jan 2024 11:53:11 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/16708/sql-server-performance-troubleshooting/</guid>

					<description><![CDATA[<p>Introduction You have a SQL Server database, and one or more of your queries are running slow. You need to figure out why. You&#8217;re not going to find the solution to your specific problem in this blog post, but you will find the first troubleshooting step that may literally save you from spending entire days [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/16708/sql-server-performance-troubleshooting/">SQL Server Performance Troubleshooting</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 id="h-introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">You have a SQL Server database, and one or more of your queries are running slow. You need to figure out why. You&#8217;re not going to find the solution to your specific problem in this blog post, but you will find the first troubleshooting step that may literally save you from spending entire days going down the wrong path to diagnose and solve the problem.</p>



<p class="wp-block-paragraph">After reading this, you will know the three categories that a slow query can fall into, and how to figure out which of those categories fit&nbsp;your problem query. From there, you&#8217;ll be able to narrow down your research and other troubleshooting steps to find a solution to your problem faster.</p>



<h2 id="h-category-1-the-query-runs-slowly" class="wp-block-heading">Category 1: The query runs slowly</h2>



<p class="wp-block-paragraph">In my experience so far, most slow queries fall into this category. Queries in this category take a long time to complete because the steps that SQL Server take&nbsp;to execute it (the execution plan) are resource-intensive and/or time-intensive. For example, your query may be doing a Clustered Index Scan on a table with tens of millions of rows, and that can take a long time.</p>



<p class="wp-block-paragraph">The individual causes of this kind of slow query can include, but are not limited to:</p>



<ol class="wp-block-list">
<li>Missing indexes</li>



<li>Poorly written SQL</li>



<li>Excessive indexes (in the case of inserts, updates, deletes)</li>



<li>Parameter sniffing</li>
</ol>



<p class="wp-block-paragraph">The actual solution to your problem will be vastly different depending on what the exact cause is, but, if you can determine that your query actually runs slowly, you&#8217;ve already narrowed down your research into the cause and the solution.</p>



<h2 id="h-category-2-the-query-compiles-slowly" class="wp-block-heading">Category 2: The query compiles slowly</h2>



<p class="wp-block-paragraph">When you run a query, SQL Server can&#8217;t execute it until it has an execution plan. An execution plan is the set of steps that SQL Server will take to actually serve your query —&nbsp;the actual, physical and logical steps. This process is called compiling an execution plan. For example, let&#8217;s say you run the following query:</p>


<pre class="”brush:sql”"> SELECT * FROM dbo.Users WHERE CreatedDate &gt;= &apos;2023-01-01&apos; </pre>


<p class="wp-block-paragraph">To actually execute that query, SQL Server may compile an execution plan that looks something like this:</p>



<ol class="wp-block-list">
<li>Non-clustered index scan on the `IX_Users_CreatedDate`</li>



<li>Key lookup with the clustered index</li>



<li>Nested loops to join the results of steps 1 and 2</li>



<li>Parallelism (Gather streams)</li>



<li>SELECT</li>
</ol>



<p class="wp-block-paragraph">For most queries, it takes very little time to compile the execution plan, e.g. 30 milliseconds (ms). The query above may only take 1ms to compile; however, more complex SQL queries may take much longer to compile. The worst I&#8217;ve seen in production was 30 seconds, which is abysmal! In fact, when a query has such a long compile time, you may even see a &#8220;compilation time out,&#8221; which means that SQL Server stopped trying to come up with the most optimal plan and just said, &#8220;Forget it, we&#8217;ll roll with what we have so far.&#8221; With such a query, it may take very little time to actually run, but that up-front cost of compiling the query may be costing you big time:&nbsp;especially if your applications are using the `OPTION(RECOMPILE)` query hint so that the execution plan gets compiled every time.</p>



<p class="wp-block-paragraph">If it turns out your problem query is returning slowly because of a long compile time, this is actually good news because this can be incredibly easy to solve. It&#8217;s likely that you&#8217;re one search engine search away from a solution that will take minutes to implement.</p>



<p class="wp-block-paragraph">NOTE: A long compile time may not have much impact on your system because SQL Server only needs to compile an execution plan if it doesn&#8217;t already have one cached in the <strong>plan cache</strong>. The result is that, for a particularly complex query, it will take 20 seconds to compile the execution plan the first time that query runs, but then SQL Server doesn&#8217;t need to spend any time compiling a plan for subsequent executions of that same query; however, an execution plan can get flushed from the plan cache for a number of reasons, e.g. the statistics on one of the tables involved in the query got updated. If events like that happen particularly often, then that will turn long compile times into a real problem for end users.</p>



<p class="wp-block-paragraph">In my time troubleshooting SQL Server performance issues, compile time is pretty rarely the cause of a slow query, but when compile time <em>is</em> the problem, you can easily spend hours or days spinning your wheels if you don&#8217;t realize the query is just taking a long time to compile.</p>



<h2 id="h-category-3-the-query-is-waiting-for-something" class="wp-block-heading">Category 3: The query is waiting for something</h2>



<p class="wp-block-paragraph">Your production server probably has a lot going on. The query you&#8217;re running is not the only thing SQL Server has to worry about. There may be some other query that needs to update the same data you need to read, or other queries are hogging all the CPU or RAM on the system, etc. These things, along with a host of other events, will cause your query to have to wait for&nbsp;a lock to be released or some RAM to free up before it can even get started.</p>



<p class="wp-block-paragraph">In other words, your slow query is not actually slow —&nbsp;it&#8217;s simply having to wait a long time before it&#8217;s allowed to start running&nbsp;— but the effect on your applications and your end users is the same. The big difference is that your troubleshooting efforts will probably have to be focused on other queries in order to fix this query. So, if your query is waiting on some lock to be released, you may need to tune a different query so it takes out fewer locks or holds those locks for less time.</p>



<p class="wp-block-paragraph">If there are many different queries across your system that are running slowly or timing out, chances are that your problem falls into this category.</p>



<p class="wp-block-paragraph"><strong>How do I determine which category my query falls into?</strong></p>



<p class="wp-block-paragraph">The best way to find out which category a specific slow query falls into is to reproduce the issue by running the query manually in SQL Server Management Studio and capturing the actual execution plan. The execution plan will have very specific information that will help you categorize your slow query. I&#8217;ll provide instructions for how to do this, then show some examples.</p>



<p class="wp-block-paragraph"><strong>How to get the actual execution plan</strong></p>



<ol class="wp-block-list">
<li>Open SQL Server Management Studio and open a query window for the database in question.</li>



<li>Press `CTRL + M` to enable capturing the actual execution plan for your query. There&#8217;s also a button directly above the query window that you can click —&nbsp;it&#8217;s the button that says &#8220;Include Actual Execution Plan&#8221; in a tooltip when you hover over it.</li>



<li>Run your query.</li>



<li>When it&#8217;s done, there will be an &#8220;Execution Plan&#8221; tab next to the &#8220;Results&#8221; and &#8220;Messages&#8221; tabs below the query window. Click the &#8220;Execution Plan&#8221; tab to view the execution plan.</li>



<li>Anywhere in the execution plan tab, right-click and select &#8220;Show Execution Plan XML&#8221;. The XML view is the best way to get at the specific information we&#8217;re looking for.</li>
</ol>



<h5 id="h-analyzing-the-execution-plan" class="wp-block-heading">Analyzing the execution plan</h5>



<p class="wp-block-paragraph"><strong>Checking for waits</strong></p>



<p class="wp-block-paragraph">Once you have your execution plan XML open, you can examine it to see what the query is spending time doing. First, do a `CTRL + F` to do a search for &#8220;WaitStats&#8221;. When you do that search, you may see something like this:</p>


<pre class="”brush:xml”"> &lt;WaitStats&gt; &nbsp; &nbsp; &lt;Wait WaitType=&quot;LCK_M_S&quot; WaitTimeMs=&quot;38265&quot; WaitCount=&quot;1&quot; /&gt; &lt;/WaitStats&gt; </pre>


<p class="wp-block-paragraph">This section of the XML shows us a list of &#8220;waits&#8221; that happened while trying to run the query. In this case, we see we had an &#8220;LCK_M_S&#8221; type of wait, and the query waited for a whopping 38 seconds! This query did indeed take 38 seconds to run in SSMS, so I&#8217;ve found the cause of my problem. Now, if I do a search engine search for &#8220;SQL Server LCK_M_S&#8221;, we find that this means that my query needed to take a &#8220;shared lock&#8221; on a resource (in this case a table) and it had to wait 38 seconds to get that lock. Now I have a <em>very</em> specific direction to go in with my research and troubleshooting.&nbsp;</p>



<p class="wp-block-paragraph">There are a lot of different wait types in SQL Server, and you may end up going in very different directions with your troubleshooting efforts depending on which type of wait is making your query slow. In the case of a lock wait like this, I might use a tool like sp_whoisactive to find out what other transaction/query is blocking my query. On the other hand, if your query is spending a lot of time on a &#8220;PAGEIOLATCH_EX&#8221; wait, you may end up using the &#8220;Top Resource Consuming Queries&#8221; report in Query Store to find out which query (or queries) are doing the most physical reads.</p>



<p class="wp-block-paragraph"><strong>Checking for compile time</strong></p>



<p class="wp-block-paragraph">If the total &#8220;WaitTimeMs&#8221; in the WaitStats section isn&#8217;t significant, or if there is no &#8220;WaitStats&#8221; section at all, next you should find out how long the query takes to compile. To do this, in your execution plan XML, do a search for the term &#8220;CompileTime&#8221;. You should find something like this:</p>


<pre class="”brush:xml”"> &lt;QueryPlan DegreeOfParallelism=&quot;1&quot; CachedPlanSize=&quot;1056&quot; CompileTime=&quot;5943&quot; CompileCPU=&quot;5943&quot; CompileMemory=&quot;33896&quot;&gt; </pre>


<p class="wp-block-paragraph">For a simple query you should see a &#8220;CompileTime&#8221; of 0ms, or perhaps as much as 10ms, but here we see 5943 milliseconds, which is just shy of 6 seconds! What this means is that it took SQL Server 6 seconds just to put together an execution plan for this query. That&#8217;s before the query even gets to run. You may see this for very complicated queries, e.g., queries that have multiple nested sub-queries or an exceptionally large number of joins. In one case, I saw a query with a compile time of over 30 seconds in production. Our application-side timeout was 30 seconds, so this particular query was timing out and causing errors for end users.</p>



<p class="wp-block-paragraph">There&#8217;s a caveat here: SQL Server should only actually spend that time the first time it runs the query. Subsequent executions should just re-use that same execution plan, skipping all that compile time. So something you&#8217;ll see if compile time is your problem is that the query is slow the first time you run it, but then the second, third, etc. time you run it is much faster.</p>



<p class="wp-block-paragraph">Solving this problem can be incredibly low-effort (the last time I ran into it, it took 15 minutes to develop a fix), so, the faster you can identify this as the cause of your problem, the faster you&#8217;ll have a resolution.</p>



<p class="wp-block-paragraph"><strong>Checking for run time</strong></p>



<p class="wp-block-paragraph">If you&#8217;ve already eliminated compile time and wait time as the cause of your slow query, that leaves us with the third possibility —&nbsp;it&#8217;s just a slow query. Let&#8217;s take the following execution plan as an example:</p>


<pre class="”brush:xml”"> &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-16&quot;?&gt; &lt;ShowPlanXML xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot; Version=&quot;1.564&quot; Build=&quot;16.0.1000.6&quot; xmlns=&quot;http://schemas.microsoft.com/sqlserver/2004/07/showplan&quot;&gt; &nbsp; &lt;BatchSequence&gt; &nbsp; &nbsp; &lt;Batch&gt; &nbsp; &nbsp; &nbsp; &lt;Statements&gt; &nbsp; &nbsp; &nbsp; &nbsp; &lt;StmtSimple StatementCompId=&quot;1&quot; StatementEstRows=&quot;1285280&quot; StatementId=&quot;1&quot; StatementOptmLevel=&quot;FULL&quot; CardinalityEstimationModelVersion=&quot;70&quot; StatementSubTreeCost=&quot;80.0303&quot; StatementText=&quot;SELECT *&amp;#xD;&amp;#xA;FROM Object1&quot; StatementType=&quot;SELECT&quot; QueryHash=&quot;0x3352E00A1F942050&quot; QueryPlanHash=&quot;0xE26E3CB998E16684&quot; RetrievedFromCache=&quot;true&quot; StatementSqlHandle=&quot;0x0900CE27F0841D17EE29F830B0383DD372BF0000000000000000000000000000000000000000000000000000&quot; DatabaseContextSettingsId=&quot;7&quot; ParentObjectId=&quot;0&quot; StatementParameterizationType=&quot;0&quot; SecurityPolicyApplied=&quot;false&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;StatementSetOptions ANSI_NULLS=&quot;true&quot; ANSI_PADDING=&quot;true&quot; ANSI_WARNINGS=&quot;true&quot; ARITHABORT=&quot;true&quot; CONCAT_NULL_YIELDS_NULL=&quot;true&quot; NUMERIC_ROUNDABORT=&quot;false&quot; QUOTED_IDENTIFIER=&quot;true&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;QueryPlan DegreeOfParallelism=&quot;0&quot; NonParallelPlanReason=&quot;TSQLUserDefinedFunctionsNotParallelizable&quot; MemoryGrant=&quot;67552&quot; CachedPlanSize=&quot;112&quot; CompileTime=&quot;5&quot; CompileCPU=&quot;5&quot; CompileMemory=&quot;920&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;MissingIndexes&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;MissingIndexGroup Impact=&quot;82.7209&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;MissingIndex Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnGroup Usage=&quot;EQUALITY&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column3&quot; ColumnId=&quot;7&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ColumnGroup&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnGroup Usage=&quot;INCLUDE&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column2&quot; ColumnId=&quot;6&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column4&quot; ColumnId=&quot;9&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column5&quot; ColumnId=&quot;11&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column6&quot; ColumnId=&quot;12&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column13&quot; ColumnId=&quot;14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column7&quot; ColumnId=&quot;15&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column8&quot; ColumnId=&quot;16&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column9&quot; ColumnId=&quot;19&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Column Name=&quot;Column10&quot; ColumnId=&quot;22&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ColumnGroup&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/MissingIndex&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/MissingIndexGroup&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/MissingIndexes&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Warnings&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;MemoryGrantWarning GrantWarningKind=&quot;Excessive Grant&quot; RequestedMemory=&quot;67552&quot; GrantedMemory=&quot;67552&quot; MaxUsedMemory=&quot;2112&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Warnings&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;MemoryGrantInfo SerialRequiredMemory=&quot;2048&quot; SerialDesiredMemory=&quot;67552&quot; RequiredMemory=&quot;2048&quot; DesiredMemory=&quot;67552&quot; RequestedMemory=&quot;67552&quot; GrantWaitTime=&quot;0&quot; GrantedMemory=&quot;67552&quot; MaxUsedMemory=&quot;2112&quot; MaxQueryMemory=&quot;3966864&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant=&quot;389427&quot; EstimatedPagesCached=&quot;194713&quot; EstimatedAvailableDegreeOfParallelism=&quot;4&quot; MaxCompileMemory=&quot;11859840&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;WaitStats&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Wait WaitType=&quot;SOS_SCHEDULER_YIELD&quot; WaitTimeMs=&quot;16&quot; WaitCount=&quot;4469&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Wait WaitType=&quot;ASYNC_NETWORK_IO&quot; WaitTimeMs=&quot;12&quot; WaitCount=&quot;2&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/WaitStats&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;QueryTimeStats CpuTime=&quot;17901&quot; ElapsedTime=&quot;17919&quot; UdfCpuTime=&quot;12925&quot; UdfElapsedTime=&quot;12919&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RelOp AvgRowSize=&quot;2584&quot; EstimateCPU=&quot;0.128528&quot; EstimateIO=&quot;0&quot; EstimateRebinds=&quot;0&quot; EstimateRewinds=&quot;0&quot; EstimatedExecutionMode=&quot;Row&quot; EstimateRows=&quot;1285280&quot; LogicalOp=&quot;Compute Scalar&quot; NodeId=&quot;0&quot; Parallel=&quot;false&quot; PhysicalOp=&quot;Compute Scalar&quot; EstimatedTotalSubtreeCost=&quot;80.0303&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column1&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column2&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column3&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column4&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column5&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column6&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column7&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column8&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column9&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column10&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column11&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column12&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Column=&quot;Expr1005&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeCountersPerThread Thread=&quot;0&quot; ActualRows=&quot;1758196&quot; Batches=&quot;0&quot; ActualEndOfScans=&quot;1&quot; ActualExecutions=&quot;1&quot; ActualExecutionMode=&quot;Row&quot; ActualElapsedms=&quot;15298&quot; ActualCPUms=&quot;15293&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ComputeScalar&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Column=&quot;Expr1005&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator ScalarString=&quot;ScalarString1&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;UserDefinedFunction FunctionName=&quot;Function1&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column13&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/UserDefinedFunction&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RelOp AvgRowSize=&quot;2607&quot; EstimateCPU=&quot;10.9251&quot; EstimateIO=&quot;0&quot; EstimateRebinds=&quot;0&quot; EstimateRewinds=&quot;0&quot; EstimatedExecutionMode=&quot;Row&quot; EstimateRows=&quot;1285280&quot; LogicalOp=&quot;Right Outer Join&quot; NodeId=&quot;1&quot; Parallel=&quot;false&quot; PhysicalOp=&quot;Hash Match&quot; EstimatedTotalSubtreeCost=&quot;79.9018&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column1&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column2&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column3&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column4&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column5&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column6&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column13&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column7&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column8&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column9&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column10&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column11&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column12&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;MemoryFractions Input=&quot;0.923425&quot; Output=&quot;0.923425&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeCountersPerThread Thread=&quot;0&quot; ActualRows=&quot;1758196&quot; Batches=&quot;0&quot; ActualEndOfScans=&quot;1&quot; ActualExecutions=&quot;1&quot; ActualExecutionMode=&quot;Row&quot; ActualElapsedms=&quot;1730&quot; ActualCPUms=&quot;1730&quot; ActualScans=&quot;0&quot; ActualLogicalReads=&quot;0&quot; ActualPhysicalReads=&quot;0&quot; ActualReadAheads=&quot;0&quot; ActualLobLogicalReads=&quot;0&quot; ActualLobPhysicalReads=&quot;0&quot; ActualLobReadAheads=&quot;0&quot; InputMemoryGrant=&quot;61512&quot; OutputMemoryGrant=&quot;61512&quot; UsedMemoryGrant=&quot;1472&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Hash&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValues /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;HashKeysBuild&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/HashKeysBuild&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;HashKeysProbe&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column4&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/HashKeysProbe&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ProbeResidual&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator ScalarString=&quot;ScalarString2&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Compare CompareOp=&quot;EQ&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column4&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Compare&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ProbeResidual&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RelOp AvgRowSize=&quot;135&quot; EstimateCPU=&quot;0.0129687&quot; EstimateIO=&quot;1.87942&quot; EstimateRebinds=&quot;0&quot; EstimateRewinds=&quot;0&quot; EstimatedExecutionMode=&quot;Row&quot; EstimateRows=&quot;11647&quot; EstimatedRowsRead=&quot;11647&quot; LogicalOp=&quot;Clustered Index Scan&quot; NodeId=&quot;2&quot; Parallel=&quot;false&quot; PhysicalOp=&quot;Clustered Index Scan&quot; EstimatedTotalSubtreeCost=&quot;1.89239&quot; TableCardinality=&quot;11647&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column12&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeCountersPerThread Thread=&quot;0&quot; ActualRows=&quot;11647&quot; ActualRowsRead=&quot;11647&quot; Batches=&quot;0&quot; ActualEndOfScans=&quot;1&quot; ActualExecutions=&quot;1&quot; ActualExecutionMode=&quot;Row&quot; ActualElapsedms=&quot;6&quot; ActualCPUms=&quot;6&quot; ActualScans=&quot;1&quot; ActualLogicalReads=&quot;2552&quot; ActualPhysicalReads=&quot;0&quot; ActualReadAheads=&quot;0&quot; ActualLobLogicalReads=&quot;0&quot; ActualLobPhysicalReads=&quot;0&quot; ActualLobReadAheads=&quot;0&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;IndexScan Ordered=&quot;false&quot; ForcedIndex=&quot;false&quot; ForceScan=&quot;false&quot; NoExpandHint=&quot;false&quot; Storage=&quot;RowStore&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Alias=&quot;Object5&quot; Column=&quot;Column12&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Object Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object4&quot; Index=&quot;Index1&quot; Alias=&quot;Object5&quot; IndexKind=&quot;Clustered&quot; Storage=&quot;RowStore&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/IndexScan&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RelOp&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RelOp AvgRowSize=&quot;2501&quot; EstimateCPU=&quot;12.9357&quot; EstimateIO=&quot;0&quot; EstimateRebinds=&quot;0&quot; EstimateRewinds=&quot;0&quot; EstimatedExecutionMode=&quot;Row&quot; EstimateRows=&quot;1285280&quot; LogicalOp=&quot;Inner Join&quot; NodeId=&quot;3&quot; Parallel=&quot;false&quot; PhysicalOp=&quot;Hash Match&quot; EstimatedTotalSubtreeCost=&quot;67.0843&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column1&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column2&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column3&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column4&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column5&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column6&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column13&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column7&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column8&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column9&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column10&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column11&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;MemoryFractions Input=&quot;0.0765755&quot; Output=&quot;0.0765755&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeCountersPerThread Thread=&quot;0&quot; ActualRows=&quot;1758196&quot; Batches=&quot;0&quot; ActualEndOfScans=&quot;1&quot; ActualExecutions=&quot;1&quot; ActualExecutionMode=&quot;Row&quot; ActualElapsedms=&quot;1090&quot; ActualCPUms=&quot;1090&quot; ActualScans=&quot;0&quot; ActualLogicalReads=&quot;0&quot; ActualPhysicalReads=&quot;0&quot; ActualReadAheads=&quot;0&quot; ActualLobLogicalReads=&quot;0&quot; ActualLobPhysicalReads=&quot;0&quot; ActualLobReadAheads=&quot;0&quot; InputMemoryGrant=&quot;6032&quot; OutputMemoryGrant=&quot;6032&quot; UsedMemoryGrant=&quot;640&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Hash&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValues /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;HashKeysBuild&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/HashKeysBuild&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;HashKeysProbe&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column3&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/HashKeysProbe&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ProbeResidual&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator ScalarString=&quot;ScalarString3&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Compare CompareOp=&quot;EQ&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column3&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Identifier&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Compare&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ScalarOperator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ProbeResidual&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RelOp AvgRowSize=&quot;51&quot; EstimateCPU=&quot;0.0007422&quot; EstimateIO=&quot;0.0409028&quot; EstimateRebinds=&quot;0&quot; EstimateRewinds=&quot;0&quot; EstimatedExecutionMode=&quot;Row&quot; EstimateRows=&quot;532&quot; EstimatedRowsRead=&quot;532&quot; LogicalOp=&quot;Clustered Index Scan&quot; NodeId=&quot;4&quot; Parallel=&quot;false&quot; PhysicalOp=&quot;Clustered Index Scan&quot; EstimatedTotalSubtreeCost=&quot;0.041645&quot; TableCardinality=&quot;532&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column11&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeCountersPerThread Thread=&quot;0&quot; ActualRows=&quot;532&quot; ActualRowsRead=&quot;532&quot; Batches=&quot;0&quot; ActualEndOfScans=&quot;1&quot; ActualExecutions=&quot;1&quot; ActualExecutionMode=&quot;Row&quot; ActualElapsedms=&quot;0&quot; ActualCPUms=&quot;0&quot; ActualScans=&quot;1&quot; ActualLogicalReads=&quot;54&quot; ActualPhysicalReads=&quot;0&quot; ActualReadAheads=&quot;0&quot; ActualLobLogicalReads=&quot;0&quot; ActualLobPhysicalReads=&quot;0&quot; ActualLobReadAheads=&quot;0&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;IndexScan Ordered=&quot;false&quot; ForcedIndex=&quot;false&quot; ForceScan=&quot;false&quot; NoExpandHint=&quot;false&quot; Storage=&quot;RowStore&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column14&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Column=&quot;Column11&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Object Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object3&quot; Index=&quot;Index2&quot; IndexKind=&quot;Clustered&quot; Storage=&quot;RowStore&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/IndexScan&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RelOp&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RelOp AvgRowSize=&quot;2479&quot; EstimateCPU=&quot;1.9342&quot; EstimateIO=&quot;52.1728&quot; EstimateRebinds=&quot;0&quot; EstimateRewinds=&quot;0&quot; EstimatedExecutionMode=&quot;Row&quot; EstimateRows=&quot;1758220&quot; EstimatedRowsRead=&quot;1758220&quot; LogicalOp=&quot;Clustered Index Scan&quot; NodeId=&quot;5&quot; Parallel=&quot;false&quot; PhysicalOp=&quot;Clustered Index Scan&quot; EstimatedTotalSubtreeCost=&quot;54.107&quot; TableCardinality=&quot;1758220&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column1&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column2&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column3&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column4&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column5&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column6&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column13&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column7&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column8&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column9&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column10&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/OutputList&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;RunTimeCountersPerThread Thread=&quot;0&quot; ActualRows=&quot;1758223&quot; ActualRowsRead=&quot;1758223&quot; Batches=&quot;0&quot; ActualEndOfScans=&quot;1&quot; ActualExecutions=&quot;1&quot; ActualExecutionMode=&quot;Row&quot; ActualElapsedms=&quot;481&quot; ActualCPUms=&quot;481&quot; ActualScans=&quot;1&quot; ActualLogicalReads=&quot;70819&quot; ActualPhysicalReads=&quot;0&quot; ActualReadAheads=&quot;0&quot; ActualLobLogicalReads=&quot;0&quot; ActualLobPhysicalReads=&quot;0&quot; ActualLobReadAheads=&quot;0&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RunTimeInformation&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;IndexScan Ordered=&quot;false&quot; ForcedIndex=&quot;false&quot; ForceScan=&quot;false&quot; NoExpandHint=&quot;false&quot; Storage=&quot;RowStore&quot;&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column1&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column2&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column3&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column4&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column5&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column6&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column13&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column7&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column8&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column9&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;ColumnReference Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Column=&quot;Column10&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValue&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/DefinedValues&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;Object Database=&quot;Database1&quot; Schema=&quot;Schema1&quot; Table=&quot;Object2&quot; Index=&quot;Index3&quot; IndexKind=&quot;Clustered&quot; Storage=&quot;RowStore&quot; /&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/IndexScan&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RelOp&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Hash&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RelOp&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/Hash&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RelOp&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/ComputeScalar&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/RelOp&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/QueryPlan&gt; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/StmtSimple&gt; &nbsp; &nbsp; &nbsp; &lt;/Statements&gt; &nbsp; &nbsp; &lt;/Batch&gt; &nbsp; &lt;/BatchSequence&gt; &lt;/ShowPlanXML&gt; </pre>


<p class="wp-block-paragraph">In this execution plan, we can see from the &#8220;WaitStats&#8221; section that our query only spent a total of about 28ms waiting. It only took 5 milliseconds to compile, but, if you search for &#8220;ElapsedTime&#8221;, you see that it took 17919 milliseconds, or just shy of 18 seconds, to run. So, this is just a slow query.</p>



<p class="wp-block-paragraph">At this point we need to analyze the execution plan further to see where all that time is being spent&nbsp;and go from there to solve it. Maybe we need to add a new non-clustered index somewhere, or maybe we can add a new where clause to reduce the number of results on one side of an expensive join. There are many different causes of this problem and many different solutions, and that&#8217;s outside the scope of this article.</p>



<h2 id="h-the-takeaway" class="wp-block-heading">The takeaway</h2>



<p class="wp-block-paragraph">When you run into a database performance issue with SQL Server, it can feel overwhelming to try and troubleshoot it, but the first step is always going to be to gather information. If you start with the steps outlined in this article, you can quickly narrow down the problem and get to an answer faster. Remember, the first step in your troubleshooting journey is finding out what your query is spending so much time doing: compiling, waiting, or running.</p>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Diagnose Slow SQL Server Queries More Efficiently</strong>.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Explore our <a href="https://static.dmcinfo.com/services/application-development/" data-type="page" data-id="434">Application Development</a> expertise in SQL Server performance troubleshooting, query execution plans, and database optimization.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/16708/sql-server-performance-troubleshooting/">SQL Server Performance Troubleshooting</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Linq.js &#8211; JavaScript Library Spotlight</title>
		<link>https://static.dmcinfo.com/blog/24096/linq-js-javascript-library-spotlight/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Tue, 11 Jul 2017 14:56:17 +0000</pubDate>
				<category><![CDATA[Web Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/24096/linq-js-javascript-library-spotlight/</guid>

					<description><![CDATA[<p>Working with and manipulating arrays has always been one of my least favorite aspects of JavaScript&#160;development. Ever since I became a professional .NET developer and C# became my language of choice, my distaste for working with arrays in JavaScript only grew stronger due to the lack of access to one of my favorite parts of [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/24096/linq-js-javascript-library-spotlight/">Linq.js &#8211; JavaScript Library Spotlight</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Working with and manipulating arrays has always been one of my least favorite aspects of <a href="https://www.javascript.com/" target="_blank" rel="noreferrer noopener">JavaScript</a>&nbsp;development. Ever since I became a professional <a href="https://www.microsoft.com/net/" target="_blank" rel="noreferrer noopener">.NET</a> developer and C# became my language of choice, my distaste for working with arrays in JavaScript only grew stronger due to the lack of access to one of my favorite parts of the .NET Framework: LINQ.</p>



<p class="wp-block-paragraph">For those unfamiliar with LINQ, it&#8217;s a library in the .NET Framework that allows a programmer to perform a large variety of complex operations on arrays, lists, and other collections with a minimal amount of code. This is done using a combination of methods such as GroupBy, OrderBy, and Join, and lambda expressions (anonymous functions).</p>



<p class="wp-block-paragraph">For example, the following code sample creates a list of <strong>time entry</strong> objects and then uses LINQ methods to convert it into a list of anonymous objects—each object has the name of a service item and the percentage of minutes spent on that service item.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C#</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>            var entries = new List&lt;timeentry>()
            {
                new TimeEntry("Eng Services", 15),
                new TimeEntry("Eng Services", 240),
                new TimeEntry("Eng Services", 180),
                new TimeEntry("N/C Eng Services", 15),
                new TimeEntry("N/C Eng Services", 30),
                new TimeEntry("N/C Eng Services", 80),
                new TimeEntry("N/C Eng Services", 120)
            };
            var totalMinutes = entries.Sum(e => e.DurationMinutes);
            var serviceItemsBreakdown = entries.GroupBy(e => e.ServiceItem)
                                                .Select(e => new { ServiceItem = e.Key, Percentage = (float)e.Sum(i => i.DurationMinutes) * 100 / (float)totalMinutes })
                                                .ToList();
&lt;/timeentry></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">entries</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">List</span><span style="color: #D4D4D4">&lt;</span><span style="color: #4EC9B0">timeentry</span><span style="color: #D4D4D4">&gt;()</span></span>
<span class="line"><span style="color: #D4D4D4">            {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeEntry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Eng Services&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">15</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeEntry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Eng Services&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">240</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeEntry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Eng Services&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">180</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeEntry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;N/C Eng Services&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">15</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeEntry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;N/C Eng Services&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">30</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeEntry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;N/C Eng Services&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">80</span><span style="color: #D4D4D4">),</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">TimeEntry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;N/C Eng Services&quot;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">120</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">            };</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">totalMinutes</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">entries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Sum</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DurationMinutes</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">serviceItemsBreakdown</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">entries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GroupBy</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ServiceItem</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                                                .</span><span style="color: #DCDCAA">Select</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> { </span><span style="color: #9CDCFE">ServiceItem</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Key</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">Percentage</span><span style="color: #D4D4D4"> = (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">)</span><span style="color: #9CDCFE">e</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Sum</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4"> =&gt; </span><span style="color: #9CDCFE">i</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">DurationMinutes</span><span style="color: #D4D4D4">) * </span><span style="color: #B5CEA8">100</span><span style="color: #D4D4D4"> / (</span><span style="color: #569CD6">float</span><span style="color: #D4D4D4">)</span><span style="color: #9CDCFE">totalMinutes</span><span style="color: #D4D4D4"> })</span></span>
<span class="line"><span style="color: #D4D4D4">                                                .</span><span style="color: #DCDCAA">ToList</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;/</span><span style="color: #9CDCFE">timeentry</span><span style="color: #D4D4D4">&gt;</span></span></code></pre></div>



<p class="wp-block-paragraph">The above code results in a list of two objects: <strong>{ ServiceItem = &#8220;Eng Services&#8221;, Percentage = 63.97059 }</strong> and <strong>{ ServiceItem = &#8220;N/C Eng Services&#8221;, Percentage = 36.02941 }</strong></p>



<p class="wp-block-paragraph">If we were to do this same operation using vanilla JavaScript methods, it would be a lot more complex, and would probably use more than one for-loop. More importantly, it involves reinventing the wheel or copying someone else&#8217;s algorithm, and that&#8217;s usually a waste of your employer&#8217;s money and/or your client&#8217;s money. And for a .NET developer, there is also inherent frustration researching or writing an algorithm for something that would take you 10 seconds to do using LINQ.</p>



<h2 id="h-solution-to-the-problem" class="wp-block-heading">Solution To The Problem</h2>



<p class="wp-block-paragraph">About two months ago, it dawned on me to check for any JavaScript libraries that provide LINQ-type methods, and it turns out that there are several. I decided to use Linq.js, which I found at the now-defunct linqjs.codeplex.com.</p>



<p class="wp-block-paragraph">I like most that this particular library implements the majority of LINQ&#8217;s methods. The signatures of the methods are even identical, so .NET developers can jump right into using it without much guidance.</p>



<p class="wp-block-paragraph">As with any JavaScript library, there are pros and cons to using it.</p>



<p class="wp-block-paragraph"><strong>Pros:</strong></p>



<ol class="wp-block-list">
<li>At 21kb when minified, it&#8217;s a pretty lightweight library.</li>



<li>The learning curve is almost non-existent for C#/.NET developers.</li>



<li>It&#8217;s incredibly performant. Even performing complex operations on large arrays is extremely fast.</li>



<li>It allows you to manipulate arrays and other types of collections with minimal code.</li>



<li>It seems very stable (I haven&#8217;t run into any bugs with it yet).</li>



<li>It has been <a href="https://www.npmjs.com/package/linq" type="link" id="https://www.npmjs.com/package/linq" target="_blank" rel="noreferrer noopener">packaged for NPM</a>.</li>
</ol>



<p class="wp-block-paragraph"><strong>Cons:</strong></p>



<ol class="wp-block-list">
<li>If you&#8217;re already using a lot of libraries, the 21kb for this library might not be worth it for some projects.</li>



<li>The last time it was updated by its creator was back in 2011, so it&#8217;s not actively being developed.</li>



<li>Non .NET developers may have a steeper learning curve.</li>
</ol>



<p class="wp-block-paragraph">I&#8217;d only recommend using this library for projects where you&#8217;re doing a lot of array manipulation. For example, if you&#8217;re using a library like Telerik&#8217;s Kendo UI to create a dashboard with charts and graphs, and you need to twist your data into various shapes in order to render different chart types, then Linq.js will save you a decent bit of time.</p>



<h2 id="h-demo-application" class="wp-block-heading">Demo Application</h2>



<p class="wp-block-paragraph">To demonstrate some of Linq.js&#8217;s more common functions, I&#8217;ve created a quick and dirty little application that you can <a href="https://github.com/DMCChristopherOlsen/LinqJSExample" type="link" id="https://github.com/DMCChristopherOlsen/LinqJSExample" target="_blank" rel="noreferrer noopener">download the code</a> for.</p>



<p class="wp-block-paragraph">When it comes down to it, Linq.js is all about data manipulation, so&nbsp;the first code I&#8217;ll show you is the file Data.js&nbsp;where I define a couple of classes (Person and Industry being the most important) and create an array of each. This is the data that we will use to demonstrate the functionality of Linq.js.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>var Person = function(firstName, lastName, occupation, ageInYears){
    this.FirstName = firstName;
    this.LastName = lastName != null ? lastName : '';
    this.Occupation = occupation;
    this.AgeInYears = ageInYears;

    this.GetTableRow = function(){
        return '' + this.FirstName + ''
            + '' + this.LastName + ''
            + '' + this.Occupation + ''
            + '' + this.AgeInYears + '';
    }.bind(this);
};

var Industry = function(name, occupations){
    this.Name = name;
    this.Occupations = occupations;
}

var Group = function(key, count){
    this.Key = key;
    this.Count = count;

    this.GetTableRow = function(){
        return '' + this.Key + ''
            + '' + this.Count + '';
    }.bind(this)
};

var people = [];
people.push(new Person('John', 'Doe', 'Doctor', 38));
people.push(new Person('Mary', 'Jane', 'Pharmacist', 29));
people.push(new Person('John', 'Downer', 'Sanitation Worker', 43));
people.push(new Person('Dee', 'Tective', 'Police Officer', 35));
people.push(new Person('Pikupin', 'Dropov', 'Russian Chaufer', 34));
people.push(new Person('Don', 'Lothario', 'Marriage Counselor', 40));
people.push(new Person('Doctor', null, 'Time traveler', 984));
people.push(new Person('Jane', 'Doe', 'Massage therapist', 29));
people.push(new Person('Christopher', 'Olsen', 'Application Developer', 34));
people.push(new Person('Kelly', 'Smith', 'Police Officer', 40));


var industries = [];
industries.push(new Industry('Healthcare', &#91;'Doctor', 'Pharmacist'&#93;));
industries.push(new Industry('Government', &#91;'Sanitation Worker'&#93;));
industries.push(new Industry('Law Enforcement', &#91;'Police Officer'&#93;));
industries.push(new Industry('Service', &#91;
    'Russian Chaufer',
    'Marriage Counselor',
    'Massage therapist'
&#93;));
industries.push(new Industry('Software', &#91;'Application Developer'&#93;));
industries.push(new Industry('Sci Fi', &#91;'Time traveler'&#93;));</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">firstName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">lastName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">ageInYears</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">FirstName</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">firstName</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">LastName</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">lastName</span><span style="color: #D4D4D4"> != </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4"> ? </span><span style="color: #9CDCFE">lastName</span><span style="color: #D4D4D4"> : </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Occupation</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AgeInYears</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">ageInYears</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetTableRow</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(){</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">FirstName</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&apos;&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">            + </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">LastName</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&apos;&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">            + </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Occupation</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&apos;&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">            + </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AgeInYears</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }.</span><span style="color: #DCDCAA">bind</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Industry</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">occupations</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Occupations</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">occupations</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Group</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">count</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Key</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Count</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">count</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetTableRow</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(){</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Key</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&apos;&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">            + </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4"> + </span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Count</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&apos;&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">    }.</span><span style="color: #DCDCAA">bind</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">this</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">};</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4"> = [];</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;John&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Doe&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Doctor&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">38</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Mary&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Jane&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Pharmacist&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">29</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;John&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Downer&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Sanitation Worker&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">43</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Dee&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Tective&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Police Officer&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">35</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Pikupin&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Dropov&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Russian Chaufer&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">34</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Don&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Lothario&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Marriage Counselor&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">40</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Doctor&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">null</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Time traveler&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">984</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Jane&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Doe&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Massage therapist&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">29</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Christopher&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Olsen&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Application Developer&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">34</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Person</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Kelly&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Smith&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Police Officer&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #B5CEA8">40</span><span style="color: #D4D4D4">));</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4"> = [];</span></span>
<span class="line"><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Industry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Healthcare&apos;</span><span style="color: #D4D4D4">, &#91;</span><span style="color: #CE9178">&apos;Doctor&apos;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&apos;Pharmacist&apos;</span><span style="color: #D4D4D4">&#93;));</span></span>
<span class="line"><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Industry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Government&apos;</span><span style="color: #D4D4D4">, &#91;</span><span style="color: #CE9178">&apos;Sanitation Worker&apos;</span><span style="color: #D4D4D4">&#93;));</span></span>
<span class="line"><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Industry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Law Enforcement&apos;</span><span style="color: #D4D4D4">, &#91;</span><span style="color: #CE9178">&apos;Police Officer&apos;</span><span style="color: #D4D4D4">&#93;));</span></span>
<span class="line"><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Industry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Service&apos;</span><span style="color: #D4D4D4">, &#91;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&apos;Russian Chaufer&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&apos;Marriage Counselor&apos;</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #CE9178">&apos;Massage therapist&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">&#93;));</span></span>
<span class="line"><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Industry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Software&apos;</span><span style="color: #D4D4D4">, &#91;</span><span style="color: #CE9178">&apos;Application Developer&apos;</span><span style="color: #D4D4D4">&#93;));</span></span>
<span class="line"><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">push</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Industry</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&apos;Sci Fi&apos;</span><span style="color: #D4D4D4">, &#91;</span><span style="color: #CE9178">&apos;Time traveler&apos;</span><span style="color: #D4D4D4">&#93;));</span></span></code></pre></div>



<p class="wp-block-paragraph">So, we have an array of <strong>People</strong>&nbsp;with their first names, last names, occupations, and ages defined. We also have an array of <strong>Industries</strong>&nbsp;that each contain&nbsp;a name and an array of occupations that fit into it.</p>



<h2 id="h-orderby" class="wp-block-heading">OrderBy</h2>



<p class="wp-block-paragraph">The data manipulation on these arrays is performed in the file Main.js. The first thing we want to do is take the <strong>People</strong> array and order (sort) it by LastName.</p>



<p class="wp-block-paragraph">The following code uses the Linq.js <strong>OrderBy</strong> method to do exactly that:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>    var sortedByLastName = Enumerable.From(people).OrderBy(function(person){
                                            return person.LastName;
                                        }).ToArray();</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">sortedByLastName</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">From</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">OrderBy</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">LastName</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                        }).</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p class="wp-block-paragraph">In order to use a function from Linq.js, we have to turn it into an <strong>Enumerable</strong>, and we do that by calling the static method <strong>From</strong> on the Enumerable class and passing our array as a parameter. From there, we can use method chaining (just like in C#&#8217;s version of LINQ) to perform any number of operations.</p>



<p class="wp-block-paragraph">In this example, we call <strong>OrderBy</strong>&nbsp;and pass it one <strong>Parameter</strong>: an anonymous function that takes one of the array&#8217;s elements as a parameter, and returns the property for the item by which we want to sort.</p>



<p class="wp-block-paragraph">This results in an Enumerable class instance, so the last thing we do is turn it back into a JavaScript array by calling the <strong>ToArray </strong>method.</p>



<p class="wp-block-paragraph">The first thought that enters your head here may be, &#8220;Hey, wait a minute, this just does the same thing as Array.prototype.sort!&#8221; Which is true. However, it has two advantages.</p>



<p class="wp-block-paragraph"><strong>Advantages</strong></p>



<ol class="wp-block-list">
<li>OrderBy is a stable sort regardless of browser implementation, whereas Array.prototype.sort can cause you problems on some browsers (namely Chrome). You can read more about it in this blog,&nbsp;<a href="https://static.dmcinfo.com/blog/25016/sorting-in-javascript-handling-google-chromes-unstable-sort/">Sorting in JavaScript: Handling Google Chromes Unstable Sort</a>.</li>



<li>The signature of the function is much simpler than that of Array.Prototype.Sort, so you accomplish the same thing with less code.</li>
</ol>



<p class="wp-block-paragraph">In any case, when we run this code by opening the web page, we get the following result:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Chart-1-resized.jpg" alt=""/></figure>



<p class="wp-block-paragraph">If you want to sort them in descending order, simply use the <strong>OrderByDescending</strong> method.</p>



<h2 id="h-where" class="wp-block-heading">Where</h2>



<p class="wp-block-paragraph">Next, I want to take only the Persons whose last name starts with &#8216;D&#8217;, and order them by AgeInYears, but in descending order. So, the oldest people will be first, followed by younger people.</p>



<p class="wp-block-paragraph">The following code accomplishes this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>var onlyDLastNameOrderBy = Enumerable.From(people).Where(function(person){
                                                return person.LastName&#91;0&#93; == 'D';
                                            }).OrderByDescending(function(person){
                                                return person.AgeInYears;
                                            }).ToArray();</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">onlyDLastNameOrderBy</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">From</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Where</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">LastName</span><span style="color: #D4D4D4">&#91;</span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">&#93; == </span><span style="color: #CE9178">&apos;D&apos;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                            }).</span><span style="color: #DCDCAA">OrderByDescending</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AgeInYears</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                            }).</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p class="wp-block-paragraph">Again, we start by using <strong>Enumerable.From(people)</strong> to convert our array to an Enumerable. Then, we use the <strong>Where </strong>function, passing it an anonymous function that returns the first letter of a person&#8217;s last name. This function effectively &#8220;filters&#8221; the array, resulting in an Enumerable that contains only the people with last names starting with &#8216;D&#8217;.</p>



<p class="wp-block-paragraph">Finally, we use the OrderByDescending method to order the Persons by AgeInYears, in descending order. The signature for this method is identical to that of OrderBy. Then we use ToArray to convert our results back into a JavaScript array.</p>



<p class="wp-block-paragraph">If we run this code, we get the following result:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Chart-2.jpg" alt=""/></figure>



<h2 id="h-groupby" class="wp-block-heading">GroupBy</h2>



<p class="wp-block-paragraph">Now, let&#8217;s go beyond just returning different sub-sets of Persons—let&#8217;s use LINQ to turn the array of Persons into something else entirely. Specifically, I want a list of ages, along with the number of Persons of that age.</p>



<p class="wp-block-paragraph">Here&#8217;s the code:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>    var groupedByAge = Enumerable.From(people).GroupBy(function(person){
                                                        return person.AgeInYears;
                                                    }, function(person){
                                                        return person;
                                                    }, function(key, group){
                                                        return new Group(key, group.Count());
                                                }).OrderBy(function(group){
                                                    return group.Key;
                                                }).ToArray();</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">groupedByAge</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">From</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">GroupBy</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">AgeInYears</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                    }, </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                    }, </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">group</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">new</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Group</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">key</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">group</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Count</span><span style="color: #D4D4D4">());</span></span>
<span class="line"><span style="color: #D4D4D4">                                                }).</span><span style="color: #DCDCAA">OrderBy</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">group</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">group</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Key</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                }).</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p class="wp-block-paragraph">Here, we use the GroupBy method. What this method does is group array elements by a given property, and then returns a new Enumerable of objects based on the grouping property and the sub-set of elements for each value of that property. We need to pass three anonymous functions into this method.</p>



<p class="wp-block-paragraph"><strong>Anonymous Functions</strong></p>



<ol class="wp-block-list">
<li>A function that returns the property we want to group by (AgeInYears in this case).</li>



<li>A function that returns the object we want to include in the <strong>Group</strong>. In this case, I&#8217;m returning the entire person.</li>



<li>A function that takes a <strong>Key</strong>&nbsp;(a value of your grouping property), and a <strong>Group</strong>&nbsp;(the collection of items that have that value for the grouping property), and returns a new object based on those two parameters. In this case, I&#8217;m returning a new <strong>Group</strong>, which has two properties: an age, and the number of people that are of that age.</li>
</ol>



<p class="wp-block-paragraph">It should be noted that I&#8217;m using another Linq.js method here: <strong>Count</strong>. This returns the number of items in the collection. I don&#8217;t use the JavaScript <strong>Length</strong> property because that the <strong>Group </strong>parameter is not a JavaScript array, it&#8217;s a LINQ Enumerable, so <strong>Length</strong>&nbsp;won&#8217;t work on it.</p>



<p class="wp-block-paragraph">Finally, I use <strong>OrderBy </strong>to order the groupings in ascending order, and we get the following result when we run the code:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Chart-3-resized.jpg" alt=""/></figure>



<p class="wp-block-paragraph">We&#8217;ll be using the GroupBy function again later to return percentages instead of raw counts.</p>



<h2 id="h-join" class="wp-block-heading">Join</h2>



<p class="wp-block-paragraph">Next, I want to get a collection of all the people&nbsp;with the <strong>Industry </strong>that they&#8217;re in. This is tricky because Industry is not a property on the <strong>Person </strong>object. <strong>Occupation </strong>is though, and each Industry has an array of occupations in it, so that&#8217;s the only connection we have between our two sets of data.</p>



<p class="wp-block-paragraph">We need to do a <strong>Join</strong>, though it could also be accomplished with some nested for-loops. For demonstration purposes, we&#8217;re going to use the LINQ approach and do a <strong>Join</strong>.</p>



<p class="wp-block-paragraph">First, though, we need to flatten our Industry array into something we can Join to the Person array.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>    var flattenedOccupations = Enumerable.From(industries).SelectMany(function(industry){
        return Enumerable.From(industry.Occupations).Select(function(occupation){
            return {
                Name: occupation,
                Industry: industry.Name
            };
        }).ToArray();
    }).ToArray();</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">flattenedOccupations</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">From</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">industries</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">SelectMany</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">industry</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">From</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">industry</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Occupations</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Select</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">Name:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                </span><span style="color: #9CDCFE">Industry:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">industry</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span></span>
<span class="line"><span style="color: #D4D4D4">            };</span></span>
<span class="line"><span style="color: #D4D4D4">        }).</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">    }).</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p class="wp-block-paragraph">Here, we use the <strong>SelectMany </strong>function on the <strong>Industries</strong>&nbsp;array. SelectMany takes an array of objects which have an array as one of their properties and returns a single array of all the items in those sub-arrays.</p>



<p class="wp-block-paragraph">In our example, we can use SelectMany to get all of the occupations in our array of Industries, but we take it a step further. Instead of just returning the occupations, we use the Select method (analogous to the Array.prototype.map function) to return an array of objects with the name of the occupation and the name of the industry. Our result is a &#8220;flattened&#8221; array of occupations and their corresponding industries.</p>



<p class="wp-block-paragraph">Now we can do the actual Join:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>    var peopleJoinedWithIndustries = Enumerable.From(people).Join(flattenedOccupations,
                                                                    function(person){
                                                                        return person.Occupation;
                                                                    },
                                                                    function(occupation){
                                                                        return occupation.Name;
                                                                    },
                                                                    function(person, occupation){
                                                                        return {
                                                                            FullName: person.FirstName + " " + person.LastName,
                                                                            Occupation: occupation.Industry
                                                                        };
                                                                    }).ToArray();</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">peopleJoinedWithIndustries</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">From</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">Join</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">flattenedOccupations</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                    </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Occupation</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                    },</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                    </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                    },</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                    </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                        </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                            </span><span style="color: #9CDCFE">FullName:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">FirstName</span><span style="color: #D4D4D4"> + </span><span style="color: #CE9178">&quot; &quot;</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">LastName</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                            </span><span style="color: #9CDCFE">Occupation:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">occupation</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Industry</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                        };</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                    }).</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();</span></span></code></pre></div>



<p class="wp-block-paragraph">The Join method takes four parameters.</p>



<p class="wp-block-paragraph"><strong>Parameters</strong></p>



<ol class="wp-block-list">
<li>The array that we want to join our array with (in this case, I want to join <strong>people</strong>&nbsp;with <strong>flattenedOccupations</strong>, so I pass in <strong>flattenedOccupations</strong>). This is also called the <strong>inner collection</strong>.</li>



<li>The <strong>outer selector</strong>. This is an anonymous function that returns the property of our<strong> outer collection</strong>&nbsp;(people) that we want to use for joining. In this case, I return the Occupation property, because that&#8217;s what I want to join on.</li>



<li>The <strong>inner selector</strong>. This is an anonymous function that returns the property of our <strong>inner collection</strong>&nbsp;(<strong>flattenedOccupations</strong>) that we want to use for joining. In this case, I return the Name property.</li>



<li>A <strong>result selector</strong>. This is an anonymous function that takes one item from each array and returns a new object based on those two objects. In this case, for each <strong>join result</strong>, I chose to return a new anonymous object with two properties: the person&#8217;s full name, and the name of the industry</li>
</ol>



<p class="wp-block-paragraph">If we run the code, we get the following result:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Chart-4-resized.jpg" alt=""/></figure>



<p class="wp-block-paragraph">In this particular example, we could have accomplished this more easily with a couple nested loops, but that&#8217;s only because we had to flatten our array of Industries before we could do a Join.</p>



<p class="wp-block-paragraph">In any case, now that we have an array matching people&#8217;s names with the industry they&#8217;re in, we can do something real fancy: get the percentage of people in each industry.</p>



<p class="wp-block-paragraph">Here&#8217;s the code:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">JavaScript</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>    var percentageByIndustry = Enumerable.From(peopleJoinedWithIndustries).GroupBy(function(person){
                                                                                return person.Industry;
                                                                            }, function(person){
                                                                                return person;
                                                                            }, function(industryName, group){
                                                                                return {
                                                                                    Industry: industryName,
                                                                                    Percentage: ((group.Count() / (people.length != 0 ? people.length : 1)) * 100) + '%'
                                                                                };
    }).ToArray();  </textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">percentageByIndustry</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">Enumerable</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">From</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">peopleJoinedWithIndustries</span><span style="color: #D4D4D4">).</span><span style="color: #DCDCAA">GroupBy</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Industry</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                            }, </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">person</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                            }, </span><span style="color: #569CD6">function</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">industryName</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">group</span><span style="color: #D4D4D4">){</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                                </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                                    </span><span style="color: #9CDCFE">Industry:</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">industryName</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                                    </span><span style="color: #9CDCFE">Percentage:</span><span style="color: #D4D4D4"> ((</span><span style="color: #9CDCFE">group</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Count</span><span style="color: #D4D4D4">() / (</span><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">length</span><span style="color: #D4D4D4"> != </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4"> ? </span><span style="color: #9CDCFE">people</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">length</span><span style="color: #D4D4D4"> : </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">)) * </span><span style="color: #B5CEA8">100</span><span style="color: #D4D4D4">) + </span><span style="color: #CE9178">&apos;%&apos;</span></span>
<span class="line"><span style="color: #D4D4D4">                                                                                };</span></span>
<span class="line"><span style="color: #D4D4D4">    }).</span><span style="color: #DCDCAA">ToArray</span><span style="color: #D4D4D4">();  </span></span></code></pre></div>



<p class="wp-block-paragraph">This is similar to the GroupBy we did earlier. We&#8217;re grouping the <strong>peopleJoinedWithIndustries </strong>array by the <strong>Industry </strong>property, and returning a new array of objects. For each object that we&#8217;re returning, we&#8217;re assigning a <strong>Percentage </strong>property with the <strong>Count </strong>of the group passed into the group selector, divided by the number of people in the original people array (handling for an empty array to avoid dividing by zero), and then multiplied by 100 to get a nice percentage. This gives us the following result:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Chart-5-resized.jpg" alt=""/></figure>



<p class="wp-block-paragraph">So, that&#8217;s it for the demonstration. I only covered a few of the LINQ functions, but Linq.js features 90 methods in total, including MaxBy, MinBy, Average, Distinct, and Contains.&nbsp;</p>



<p class="wp-block-paragraph">For a full list of functions, you can view the <a href="https://github.com/neuecc/linq.js/" type="link" id="https://github.com/neuecc/linq.js/" target="_blank" rel="noreferrer noopener">Linq.js reference sheet</a>.</p>



<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/services/application-development/web-application-development" target="_blank">Learn more about DMC&#8217;s Web Application Development services.</a></p>
<p>The post <a href="https://static.dmcinfo.com/blog/24096/linq-js-javascript-library-spotlight/">Linq.js &#8211; JavaScript Library Spotlight</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>The Revit API: Creating Your First Add-In</title>
		<link>https://static.dmcinfo.com/blog/24963/the-revit-api-creating-your-first-add-in/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Thu, 03 Nov 2016 08:17:01 +0000</pubDate>
				<category><![CDATA[Application Development]]></category>
		<category><![CDATA[MS Visual Studio/.NET]]></category>
		<category><![CDATA[Software Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/24963/the-revit-api-creating-your-first-add-in/</guid>

					<description><![CDATA[<p>Lately, I&#8217;ve gotten reacquainted with an old friend of mine: the Revit API. Revit is an incredible piece of architectural software from Autodesk, and one of its features is the ability to expand its functionality through the use of add-ins. Revit add-in development is now one of the many services that we&#8217;re able to offer [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/24963/the-revit-api-creating-your-first-add-in/">The Revit API: Creating Your First Add-In</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Lately, I&#8217;ve gotten reacquainted with an old friend of mine: the Revit API. Revit is an incredible piece of architectural software from Autodesk, and one of its features is the ability to expand its functionality through the use of add-ins. Revit add-in development is now one of the many services that we&#8217;re able to offer here at DMC, and I&#8217;d like to celebrate that fact by sharing an article about how to get started with Revit add-in development.</p>



<p class="wp-block-paragraph">First of all, this article assumes you know/have the following things:</p>



<ol class="wp-block-list">
<li>Intermediate C# and .NET development experience or knowledge</li>



<li>A license for Revit (preferably 2013 or later) so you can actually test your add-in</li>



<li>A license for Visual Studio (I&#8217;ll be using 2015 for this example). You can develop Revit macros using Revit’s built-in code editor, but developing add-ins (external commands and external applications) pretty much requires Visual Studio.</li>
</ol>



<p class="wp-block-paragraph">The first thing you&#8217;ll want to do is set up your development machine. I chose to use a virtual machine (VM) running Windows 8, for reasons that are beyond the scope of this article. Make sure you check the system requirements for the version of Revit that you&#8217;re developing for. Install Revit and your chosen version of Visual Studio, and you&#8217;re ready to get started.</p>



<p class="wp-block-paragraph">To create a new Revit add-in, open up Visual Studio and start a new Class Library project.</p>



<p class="wp-block-paragraph">Visual Studio is kind enough to provide us with a class, named “Class1.cs”.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/RevitAPITutorial_4.png" alt="The class that will hold our Revit external command"/></figure>



<p class="wp-block-paragraph">Either rename it, or delete it and replace it with another class (.cs) file, and make sure the new class is named something that makes sense. I’m naming mine “RevitTutorialExternalCommand”. Once that’s done, we’re going to make our new class implement the interface “IExternalCommand”.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/RevitAPITutorial_5.png" alt="Implementing the Revit IExternalCommand interface"/></figure>



<p class="wp-block-paragraph">As you probably know, that squiggly red underline means Visual Studio doesn&#8217;t recognize that reference. This is because IExternalCommand is an interface from the Revit API libraries. So to use it, we&#8217;ll have to add a reference in our project to the Revit API dlls. You can find these in the directory where you installed Revit. The files we&#8217;re looking for are RevitAPI.dll and RevitAPIUI.dll. These are the two libraries that you&#8217;ll use most often when developing add-ins for Revit.</p>



<p class="wp-block-paragraph">Once we&#8217;ve added references in our project to these files, we can add the line:</p>



<p class="wp-block-paragraph">using Autodesk.Revit.UI;</p>



<p class="wp-block-paragraph">to our class file.</p>



<p class="wp-block-paragraph">Now we still have the red squiggly underline; as you probably guessed, Visual Studio now recognizes the interface name, but it’s warning us that we’re not correctly implementing the interface. In order for our class to implement it, we have to add the Execute method to the class. This method has the following signature:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C#</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>public Result Execute(
    ExternalCommandData commandData,
    ref string message,
    ElementSet elements)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Result</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">Execute</span><span style="color: #D4D4D4">(</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4EC9B0">ExternalCommandData</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">commandData</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">ref</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">message</span><span style="color: #D4D4D4">,</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4EC9B0">ElementSet</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">elements</span><span style="color: #D4D4D4">)</span></span></code></pre></div>



<p class="wp-block-paragraph">The access modifier has to be public in order for Revit to be able to run our add-in. This method essentially serves as the entry point for your add-in – this is the equivalent of “public static void Main()”. There’s one more thing we should do before actually writing code – add an attribute that determines the transactional behavior of the external command. This attribute is in the namespace Autodesk.Revit.Attributes, so you’ll want to add that to your using statements. Revit transactions are outside the scope of this article, but the short version is that anything your add-in does to change the file that it’s working with has to be done within the scope of a transaction, so the TransactionMode enum that we supply to the Transaction attribute allows us to decide whether a transaction is automatically wrapped around our entire add-in, or whether we have to manually start and commit Transactions. It’s usually a good idea to use Manual, so that’s what I’m showing here.</p>



<p class="wp-block-paragraph">Finally, let’s write some code to make this add-in actually DO something. We’ll keep it simple for this first tutorial: we’ll just display a message box for the user.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/RevitAPITutorial_12.png" alt="Showing a TaskDialog in Revit API"/></figure>



<p class="wp-block-paragraph">In the above image, you can see I&#8217;ve used a class called TaskDialog&#8217;s &#8220;Show&#8221; method, and passed it two strings as its parameters. The first parameter is the &#8220;title&#8221; displayed at the top of the message box. The second parameter is the content text of the message box. At the end of our Execute method, we return an Autodesk.Revit.UI.Result, an enum with three possible values: &#8220;Cancelled&#8221;, &#8220;Succeeded&#8221;, and &#8220;Failed&#8221;. We&#8217;ll return Succeeded for now.</p>



<p class="wp-block-paragraph">Finally, we&#8217;ll build our solution, start Revit, and try it out.</p>



<p class="wp-block-paragraph">But wait, there&#8217;s a problem&#8230; When we have add-ins loaded, there&#8217;s supposed to be an &#8220;Add-ins&#8221; menu tab at the top, right next to &#8220;Modify&#8221;. You can see in the above picture that it&#8217;s not there. This is because we haven&#8217;t actually deployed our add-in so that it can be loaded by Revit. We need to do two things to make this happen:&nbsp;</p>



<ol class="wp-block-list">
<li>Write an add-in file (an XML file with a &#8220;.addin&#8221; extension) that Revit reads when it starts up in order to determine which add-ins are to be loaded</li>



<li>Build our project to produce a dll file, and place it somewhere accessible (we&#8217;ve already done this one)</li>
</ol>



<p class="wp-block-paragraph">Let&#8217;s go ahead and write that .addin file. You can do this using Notepad, Notepad++, or any other text editor. When creating the file, you need to put it in the add-ins folder for your targeted version of Revit, which can be found at C:\Users\[YOUR USERNAME]\AppData\Roaming\Autodesk\Revit\Addins\[YOUR REVIT VERSION YEAR]. So for me, it&#8217;s C:\Users\Christopher Olsen\AppData\Roaming\Autodesk\Revit\Addins\2014. The AppData folder is usually hidden by default, so you&#8217;ll have to make sure Windows Explorer is configured to show hidden files and folders.</p>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">As mentioned before, this .addin file is in XML format.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/RevitAPITutorial_14.png" alt="A sample Revit .addin manifest file"/></figure>



<p class="wp-block-paragraph">Our root element here is the RevitAddIns tag. Within this tag, you&#8217;ll place one or more Addin tags (yes, you can use one file to specify multiple add-ins for Revit to load). The &#8220;Type&#8221; attribute on the Add-in tag is for specifying what kind of add-in this is. The two most popular options are &#8220;Command&#8221; and &#8220;Application&#8221;. Since we&#8217;ve written an external command (as indicated by the fact that we used the IExternalCommand interface), we&#8217;ll use the &#8220;Command&#8221; value for this attribute.</p>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Nested inside the AddIn tag, we have several important elements. The Text tag is where we specify the display name for our add-in in the &#8220;External Commands&#8221; drop-down menu in Revit. The Assembly tag must contain the file path to the dll for our add-in &#8211; in this example, I&#8217;ve pointed it toward the dll file in my project&#8217;s obj\Debug folder in order to make testing and debugging easier. AddInId is essentially the ID of your add-in, and must be unique for a session of Revit &#8211; the best way to do this is to generate a new GUID in visual studio (you can do this by going to the Tools menu, and clicking on &#8220;Create GUID&#8221;. You don&#8217;t have to put this GUID in your code somewhere, but it&#8217;s common practice to place it in a comment right above your class definition.</p>



<p class="wp-block-paragraph">Next, we have the FullClassName tag, which must contain the namespace and class name for your external command. The VendorID tag is required, and is supposed to be unique for a given session of Revit &#8211; Autodesk recommends using the url of your company&#8217;s website. Finally, VendorDescription is a more verbose description of your company &#8211; if your external command throws an unhandled exception, Revit will display an error message on screen that ends with &#8220;For more info, contact this knucklehead that developed the add-in: [VendorDescription]&#8221;. I&#8217;ve paraphrased the actual text of the error message, for comical effect.</p>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">And that&#8217;s it. There are many more things we can specify in that .addin file, but these are the core ones you&#8217;ll really want to use. Other tags you can add to this file include the path to an image that will be displayed for your add-in in the external command drop-down menu. For a full run-down, check out the table on Autodesk&#8217;s <a href="https://www.revitapidocs.com/" target="_blank" rel="noreferrer noopener">knowledge base website</a>.</p>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">Now, if we open Revit, we see that the &#8220;Add-Ins&#8221; tab has been added, and clicking on the &#8220;External Tools&#8221; drop-down shows us our add-in. If we click on it, our external command&#8217;s &#8220;Execute&#8221; method runs, and we get our message box.</p>



<p class="wp-block-paragraph">Now that we’ve got the super basic stuff out of the way, we can turn our attention to doing something meaningful in a Revit add-in. In my next blog post, we’ll go over how we can have our user select elements, and how we can then perform operations on those elements.</p>



<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/services/application-development/pc-application-development" target="_blank">Learn more about how DMC can help you develop custom software.</a></p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/24963/the-revit-api-creating-your-first-add-in/">The Revit API: Creating Your First Add-In</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Sorting in JavaScript: Handling Google Chrome&#8217;s Unstable Sort</title>
		<link>https://static.dmcinfo.com/blog/25016/sorting-in-javascript-handling-google-chromes-unstable-sort/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Tue, 25 Oct 2016 14:59:20 +0000</pubDate>
				<category><![CDATA[Web Application Development]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/25016/sorting-in-javascript-handling-google-chromes-unstable-sort/</guid>

					<description><![CDATA[<p>(NOTE: There is sample code to go along with this article.) In web applications, a task that often needs to be performed&#160;is the sorting of arrays. If you&#8217;re anything like me, you often use the Array.prototype.sort method to accomplish this task. And who could blame you? It&#8217;s short, it&#8217;s easy, its implementation is performant, and [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/25016/sorting-in-javascript-handling-google-chromes-unstable-sort/">Sorting in JavaScript: Handling Google Chrome&#8217;s Unstable Sort</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">(NOTE: There is <a href="https://github.com/DMCChristopherOlsen/JavaScriptCustomSortingExample" rel="no follow" target="_blank">sample code</a> to go along with this article.)</p>



<p class="wp-block-paragraph">In web applications, a task that often needs to be performed&nbsp;is the sorting of arrays. If you&#8217;re anything like me, you often use the Array.prototype.sort method to accomplish this task. And who could blame you? It&#8217;s short, it&#8217;s easy, its implementation is performant, and it works exactly the way you want it to most of the time. As you can see in the code pictured below, all we have to do is call the &#8220;sort&#8221; method of our array, passing in a function that compares two elements and returns either 1, -1, or 0 depending on how the elements compare. Nice and easy.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/JSGoogleUnstableSort_1.png" alt="Example of Array.prototype.sort in JavaScript"/></figure>



<p class="wp-block-paragraph">But wait &#8211; what do I mean by &#8220;it works&#8230; most of the time&#8221;? Well, let me clarify: it always works, but it won&#8217;t always work the way you expect it to. For most of us, when we sort an array, list, or any other collection, we expect that two items that are considered &#8220;equal&#8221; should appear in the sorted collection in the same order that they were in the unsorted collection. For example, if we were sorting an array that contained the integers 6, 3, 3, and 1, we would expect the first 3 to appear before the second 3 in the sorted array. That&#8217;s called a &#8220;stable&#8221; sort.</p>



<p class="wp-block-paragraph">Now, the problem with the Array.prototype.sort method is that different browsers implement it differently. Internet Explorer and Edge always use a stable sort. Firefox uses a stable sort. Even poor, neglected Opera uses a stable sort. But there&#8217;s a rebel: Google Chrome. Chrome uses a stable sorting algorithm for arrays of length &lt; 10, but for arrays of more than ten items, it uses an unstable QuickSort algorithm. This can lead to unreliable behavior that you might not discover when testing if you don&#8217;t think to test your code on an array with more than ten items in Chrome.</p>



<p class="wp-block-paragraph">To be fair, it doesn&#8217;t matter most of the time whether or not we use a stable sort. If we&#8217;re just sorting a list of names, we don&#8217;t really care whether all the Smiths appear in the same order relative to each other after sorting. However, there are some cases where we DO care about the stability of our sort, such as the very simple web application depicted in the screenshot below.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/JSGoogleUnstableSort_2.png" alt="Screenshot of web application"/></figure>



<p class="wp-block-paragraph">This web application renders an array of &#8220;Person&#8221; objects, in alphabetical order by first name. To render the array in order, we use the Array.prototype.sort method, passing in a comparison function that only compares the first name, so other properties (such as last name or occupation) have absolutely zero impact on the order in which the items are displayed. Now, if we&#8217;re only displaying this data one time when first loading the page, it doesn&#8217;t matter. Where we run into a problem is if we try to re-sort the data and then render it again, because Chrome&#8217;s unstable sorting algorithm will cause the items to &#8220;jump around&#8221; when there are multiple items with the same &#8220;First Name&#8221; property.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/JSGoogleUnstableSort_3.png" alt="Screenshot of web application after changing name of one of the entries"/></figure>



<p class="wp-block-paragraph">In the above screenshot, I&#8217;ve used the text input field in the &#8220;First Name&#8221; column to change Alec Baldwin&#8217;s first name to &#8220;Steve&#8221;. In the OnChange event handler for that input, we&#8217;re re-sorting the array and re-rendering it. What we would EXPECT to see is that the people are displayed in the same order, except that Baldwin now appears at the bottom of the table because his new first name (Steve) should appear last alphabetically &#8211; all the other items in the table should appear in the same order relative to each other as before. But that&#8217;s not what happens: James Bond now appears between James Madison and James Caan, and James Buchanan now appears almost at the top of the list instead of almost at the bottom. Not only is it visually unsettling when items jump around like that unexpectedly, but it also hurts the user experience.</p>



<p class="wp-block-paragraph">Now, Google has no plans to change their implementation of Array.prototype.sort ever, because they consider it to be working as intended. And I don&#8217;t blame them &#8211; the W3 standard for that method does not specify whether the sort should be stable, so it&#8217;s left up to each browser developer whether or not to use a stable sorting algorithm. So if you want to ensure that your sorts are always stable, you have to implement your own method of sorting.</p>



<p class="wp-block-paragraph">The method I recommend for sorting in JavaScript is to create an object or class that will handle sorting for you; you shouldn&#8217;t override the Array.prototype.sort method for a number of different reasons, which won&#8217;t be covered here.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/JSGoogleUnstableSort_4.png" alt="Customer sorting functions in JavaScript"/></figure>



<p class="wp-block-paragraph">The above screenshot shows the file &#8220;Sorter.js&#8221; included in <a href="https://github.com/DMCChristopherOlsen/JavaScriptCustomSortingExample" target="_blank" rel="no follow">the sample project</a>. It declares an object in my project&#8217;s namespace called &#8220;Sorter&#8221;, which has several methods. The method that we actually use in our application is &#8220;sort&#8221;. This method takes two parameters: the array that we&#8217;re sorting, and the function that will be used to compare objects in the array (this function should be identical to the one we would use in Array.prototype.sort). Based on the number of items in the array, the sort method uses one of two sorting methods: if there are fewer than ten&nbsp;items, we use an insertion sort. If there are more than ten&nbsp;items in the array, we use a stable merge sort, which is still one of the faster sorting algorithms. The merge sort algorithm we&#8217;re using here was adapted from the one shown in&nbsp;this article.</p>



<p class="wp-block-paragraph">So now, back in our main application logic where we previously were calling the Array.prototype.sort method, we will now call the &#8220;sort&#8221; method of our Sorter object, like so:</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/JSGoogleUnstableSort_5.png" alt="Example code calling custom JavaScript sort method"/></figure>



<p class="wp-block-paragraph">This new method is only slightly less convenient than the prototype sort method, but it allows us to have greater control over how our arrays are sorted, and it greatly reduces the possibility of sorting-related bugs. If we edit items in the web application now, we can see that items with the same value for &#8220;First Name&#8221; will stay in the same order relative to each other, so items don&#8217;t &#8220;jump around&#8221; unexpectedly.</p>



<p class="wp-block-paragraph">A&nbsp;real-world application of this technique is a time entry web application, where users have a table of time entries with fields such as duration, person&#8217;s name, and category, and where users can choose to sort by any combination of those fields. Instead of writing a bunch of complicated code to determine when the table should re-sort, or ensuring that if the table is already sorted it doesn&#8217;t get re-sorted, we can simply use our custom sorting implementation to make sure that items in the table don&#8217;t get moved around unless they absolutely should.</p>



<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/services/application-development/web-application-development" target="_blank">Learn how DMC can help you with your web development needs.</a></p>
<p>The post <a href="https://static.dmcinfo.com/blog/25016/sorting-in-javascript-handling-google-chromes-unstable-sort/">Sorting in JavaScript: Handling Google Chrome&#8217;s Unstable Sort</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Querying the Active Directory</title>
		<link>https://static.dmcinfo.com/blog/25759/querying-the-active-directory/</link>
		
		<dc:creator><![CDATA[Christopher Olsen]]></dc:creator>
		<pubDate>Tue, 26 Apr 2016 13:26:52 +0000</pubDate>
				<category><![CDATA[Web Application Development]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MS Visual Studio/.NET]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/25759/querying-the-active-directory/</guid>

					<description><![CDATA[<p>While developing a .NET application, you may find that you need to access data that is stored in the Active Directory (AD). In these cases, if you’re not experienced with querying the AD, you may be tempted to create a copy of the data you need into a SQL database – however, this would amount [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/25759/querying-the-active-directory/">Querying the Active Directory</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">While developing a .NET application, you may find that you need to access data that is stored in the Active Directory (AD). In these cases, if you’re not experienced with querying the AD, you may be tempted to create a copy of the data you need into a SQL database – however, this would amount to unnecessary data redundancy.</p>



<p class="wp-block-paragraph">Fortunately, pulling data from the AD and using it in your application is much easier than you might think, and allows you to maintain data integrity by keeping your data in just one place. Also, SharePoint developers will be happy to learn that the AD can be queried from within a SharePoint add-in with the same code that I&#8217;ll be sharing here.</p>



<p class="wp-block-paragraph">First, let’s have a brief review of what the Active Directory (AD) is:</p>



<p class="wp-block-paragraph">The AD is, for the purpose being discussed here, basically a database, except that its storage is hierarchical in nature. The data it stores typically regards the different components in a computer network. For example, a very common use of the AD is to store various pieces of information about a user (email address, name, phone number, title/job description, etc.) It may also contain data about user groups, such as the marketing department, IT, etc.</p>



<h2 id="h-getting-started" class="wp-block-heading">Getting Started</h2>



<p class="wp-block-paragraph">The first thing you’ll need to do is figure out what your AD domain path is. <span style="line-height: 1.6;">Also, if you&#8217;re querying the AD from within a SharePoint add-in, you&#8217;ll need to make sure that the user under which the Add-in web service is running has the proper permissions to access the AD.</span></p>



<p class="wp-block-paragraph">And finally, it should be said at this point that you’ll need to use a few .NET assemblies that you may have never touched before:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C#</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>using System.DirectoryServices;

using AD = System.DirectoryServices.ActiveDirectory;
using AP = System.DirectoryServices.Protocols;
using AM = System.DirectoryServices.AccountManagement;</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">DirectoryServices</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AD</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">DirectoryServices</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">ActiveDirectory</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AP</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">DirectoryServices</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Protocols</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #C586C0">using</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">AM</span><span style="color: #D4D4D4"> = </span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">DirectoryServices</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">AccountManagement</span><span style="color: #D4D4D4">;</span></span></code></pre></div>



<h2 id="h-example-getting-user-info" class="wp-block-heading">Example: Getting User Info</h2>



<p class="wp-block-paragraph">Something I often find myself having to do is get a list of all users in the AD, along with some piece of data associated with them. In this example, we&#8217;ll be building a Dictionary that associates each user with the name of their manager.</p>



<p class="wp-block-paragraph">First, let&#8217;s show you the code, then we&#8217;ll explain it.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/code1.png" alt="Active Directory querying code"/></figure>



<p class="wp-block-paragraph">After we’ve declared an empty dictionary, and retrieved our current path, we go ahead and create an instance of the DirectoryEntry class, passing our domain path as a parameter to its constructor. It should be noted that most of these DirectoryServices classes implement the IDisposable interface, meaning that we need to make sure we dispose of them after we’re done using them. To make this easier, of course, we utilize “using” statements here, so that our unmanaged resources automatically get released.</p>



<p class="wp-block-paragraph">Next, we pass our DirectoryEntry as a parameter to the DirectorySearcher class constructor. The DirectorySearcher class’ main purpose is to allow us to query the AD.</p>



<p class="wp-block-paragraph">Now that we have our DirectorySearcher instance, the wise thing to do is to use a filter to narrow our search results – if we skip this step, we sacrifice performance. So we assign to the DirectorySearcher’s Filter property a string that represents an LDAP query. In this case, our query “(&amp;(objectCategory=User)(objectClass=person))” returns only objects that are in the User category and that implement the “person” class.</p>



<p class="wp-block-paragraph">Now, for a given AD object, there can be dozens or even hundreds of properties. So to save on performance, we only want our query to return those properties which we’ll actually be using. To do this, we add property names as strings to the PropertiesToLoad property of our DirectorySearcher. Finally, we execute the query by calling the FindAll method of the DirectorySearcher. This returns a SearchResultCollection.</p>



<p class="wp-block-paragraph">Iterating through this collection gives us access to SearchResult class instances. To avoid a null reference exception, I like to ensure that each SearchResult actually has the properties that I’m looking for. After that, getting our values is a simple matter of accessing the Properties property of the SearchResult with a string indexer. In this case, both are properties are simply strings, so I only want to return element zero, but if the property I was querying were an array, I would probably be iterating through it or doing some other operation.</p>



<p class="wp-block-paragraph">So the final step here is to add the value of our user’s display name and the value of the “manager” property as a KeyValuePair in my Dictionary, and move on to the next SearchResult. Once we’ve iterated through the entire collection, we can simply return our Dictionary.</p>



<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/services/digital-workplace-solutions/microsoft-consulting-services">Learn more about our Microsoft Consulting Services</a></p>
<p>The post <a href="https://static.dmcinfo.com/blog/25759/querying-the-active-directory/">Querying the Active Directory</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
