<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Naveed Ausaf - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Naveed Ausaf - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 12 Jun 2026 10:51:09 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/nausaf/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Set Semantic Version for .NET Core Apps and Libraries ]]>
                </title>
                <description>
                    <![CDATA[ Semantic Versioning (or SemVer for short) is a software versioning scheme that stipulates three-part version numbers of the form <major>.<minor>.<patch>, such as 1.0.2, with an optional prerelease suffix of the form -<prerelease>, as in 1.0.2-beta. S... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/set-semantic-versioning-for-net/</link>
                <guid isPermaLink="false">672df563eedb451674f63814</guid>
                
                    <category>
                        <![CDATA[ .NET ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Aspnetcore ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naveed Ausaf ]]>
                </dc:creator>
                <pubDate>Fri, 08 Nov 2024 11:26:27 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731065367635/f8ce5091-d526-4d09-8282-2ffe63cead40.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://semver.org/">Semantic Versioning</a> (or SemVer for short) is a software versioning scheme that stipulates three-part version numbers of the form <code>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code>, such as <code>1.0.2</code>, with an optional prerelease suffix of the form <code>-&lt;prerelease&gt;</code>, as in <code>1.0.2-beta</code>.</p>
<p>SemVer is perhaps the the most widely used versioning scheme today. For example, both <a target="_blank" href="https://learn.microsoft.com/en-us/nuget/concepts/package-versioning?tabs=semver20sort#pre-release-versions">Nuget</a> and <a target="_blank" href="https://docs.npmjs.com/about-semantic-versioning">npm</a> recommend and support it, and VS Code <a target="_blank" href="https://github.com/microsoft/vscode/releases">uses it</a> as well.</p>
<p>In most GitHub repos that use the GitHub Releases feature to publish releases, you would see a SemVer version number in the latest release badge on the home page, as can be seen in the screenshot below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730988665455/34706cc9-7cf3-401c-9407-2f15933fef49.png" alt="Latest release badge in Next.js GitHub repository showing the three-part Semantic Versioning version number 15.0.3" class="image--center mx-auto" width="411" height="206" loading="lazy"></p>
<p>I frequently need to set a SemVer version number when building ASP.NET Core API projects, and then read or report this at runtime.</p>
<p>For example, if I build a minimal API with its version set to <code>1.0.2-beta</code>, this would be reported by a <code>/version</code> endpoint exposed by the API, as shown in the screenshot below from <a target="_blank" href="https://hoppscotch.io/">Hoppscotch</a> (this is a <a target="_blank" href="https://www.postman.com/">Postman</a>-like tool with the convenience that it runs the browser):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730746046707/eb8968ef-41c7-4919-a0ed-7aeb25e0a03d.png" alt="Screenshot of Hoppscotch shows that a was call made to the /version endpoint of an API running locally (on localhost). The result was a JSON document containing a &quot;version&quot; property whose value is the API's SemVer version number of &quot;1.0.2-beta&quot;." class="image--center mx-auto" width="623" height="488" loading="lazy"></p>
<p>Checking that the version reported from deployed services, such as web apps and APIs, is correct is a crucial part of my CD pipeline and is one of the smoke tests I use to determine if a deployment succeeded.</p>
<p>One slight complication when setting a SemVer version number on .NET assemblies is that .NET originally used four part version numbers like <code>1.0.3.212</code> and assemblies still have these (assembly is the .NET term for <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/standard/assembly/">units of code compiled to .NET bytecode</a>, the most typical of these being dll’s and exe’s).</p>
<p>The other is that .NET has not one but many, slightly different, version numbers that are present in the same assembly.</p>
<p>In this article, I’ll show you how to sidestep these quirks and stamp a SemVer version number on a .NET assembly during build, then read it back at runtime.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-structure-of-a-semver-version-number">Structure of a SemVer Version Number</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-many-version-numbers-of-a-net-assembly">The Many Version Numbers of a .NET Assembly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-a-semver-version-number">How to Set a SemVer Version Number</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-an-assemblys-semver-version-at-runtime">How to Read an Assembly’s SemVer Version at Runtime</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-structure-of-a-semver-version-number">Structure of a SemVer Version Number</h2>
<p>Consider a SemVer version number like <code>1.0.2</code> or <code>1.0.2-beta</code>. It has the form <code>&lt;major&gt;</code>.<code>&lt;minor&gt;</code>.<code>&lt;patch&gt;</code>-<code>&lt;prerelease&gt;</code></p>
<p>This is what the various components mean:</p>
<p>The <code>&lt;major&gt;</code> component of the version number would be incremented only if the new release would break an existing (most recent) release.</p>
<p>In case of a UI app, clients may be taken to mean <em>human clients</em>. So if the new release would break users’ existing assets such as workflow definitions, this would call for incrementing the major version number. In this event, if the previous release was <code>1.0.2</code>, the new release should be <code>2.0.0</code> (all lower components of the version number would reset).</p>
<p>In case of a library, such as a library package on Nuget or NPM, the clients would be other code. So if the new release would break existing client code, i.e. it would not be backwards compatible with its own previous version, then again it is the <code>&lt;major&gt;</code> component would be incremented.</p>
<p><code>&lt;minor&gt;</code> is incremented if new features have been added but the new version is still backwards compatible. So from <code>1.0.2</code> you would go to <code>1.1.0</code>.</p>
<p><code>&lt;patch&gt;</code> is incremented when a new release needs to be made even though there is no breaking change and no new functionality has been added. This could happen, for example, if there was a bugfix that had to be released.</p>
<p><code>-&lt;prerelease&gt;</code> suffix is optional. It is typically suffixed to a three part version number when software needs to be made available during prerelease testing phases such as alpha and beta. For example, before generally releasing version <code>1.0.2</code> of your software, you can make it available to your beta testers as <code>1.0.2-beta</code>.</p>
<p>The <code>&lt;prerelease&gt;</code> component can pretty much be any string of your choosing and the only requirement is that it is either an <em>alphanumeric identifier</em> such as <code>beta</code> or <code>12</code> or <code>alpha2</code> (no characters other than numbers or letters of the alphabet) or multiple alphanumeric identifiers separated by a dot(<code>.</code>) e.g. <code>development.version</code>.</p>
<h2 id="heading-the-many-version-numbers-of-a-net-assembly">The Many Version Numbers of a .NET Assembly</h2>
<p>As Andrew Lock’s <a target="_blank" href="https://andrewlock.net/version-vs-versionsuffix-vs-packageversion-what-do-they-all-mean/#how-to-set-the-version-number-when-you-build-your-app-library">article on .NET versioning</a> explains, a .NET assembly has not one but several different version numbers:</p>
<ul>
<li><p><strong>AssemblyVersion:</strong> This is a four part version number, for example, <code>1.0.2.0</code>. It is used by the runtime when loading linked assemblies.</p>
</li>
<li><p><strong>FileVersion:</strong> This is the version number reported for a <strong>.dll</strong> file in Windows File Explorer when you right click the assembly and select Properties.</p>
</li>
<li><p><strong>InformationalVersion:</strong> Yet another version number and, like FileVersion, can be seen in Properties dialog if you right-click the assembly in Windows and select Properties. This can contain strings and not only integers and dots that AssemblyVersion and FileVersion are constrained to.</p>
</li>
<li><p><strong>PackageVersion:</strong> If the project is a Nuget package, this would be the version number of the package that the assembly is part of.</p>
</li>
</ul>
<p>All of these version numbers are emitted into the assembly during compilation as metadata. You can see them if you inspect the assembly with <a target="_blank" href="https://www.jetbrains.com/decompiler/">JetBrains dotPeek</a> (free) or <a target="_blank" href="https://www.red-gate.com/products/reflector/">Red gate Reflector</a> (not free) or similar.</p>
<p>FileVersion and InformationalVersion can also be seen in the Details tab of the Properties dialog that appears when you right-click the assembly file in Windows File Explorer and select Properties:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730755185100/d444a84b-5148-47e6-ab75-951d9f0f73ac.png" alt="Properties Dialog for a compiled .NET DLL in Windows File Explorer. It shows the DLL's &quot;File Version&quot; attribute with value &quot;1.0.2.0&quot; and its &quot;Product version&quot; attribute with value &quot;1.0.2-beta&quot;" class="image--center mx-auto" width="480" height="621" loading="lazy"></p>
<p>In the screenshot above, “Product version” is the caption for InformationalVersion whereas “File version” is the caption for FIleVersion.</p>
<p>Of the four types of version numbers described above, only the first three apply to any assembly (i.e. whether or not the assembly is part of a Nuget package).</p>
<p>Of those three, AssemblyVersion alsways adds a <code>0</code> in the fourth place if you try to set a SemVer version which only has three numbers (plus an optional <em>prerelease</em> suffix). For example if you try to set a SemVer version of <code>1.0.2-beta</code> during build and then read the AssemblyVersion value at run time in the assembly, it would be <code>1.0.2.0</code>.</p>
<p>FileVersion does the same, as shown in the screenshot above.</p>
<p>InformationalVersion is the only version number which would get set exactly to the server version you set during build, as the screenshot above shows.</p>
<p>Therefore, InformationalVersion is what we need to set with a SemVer version during build, and read back at run time.</p>
<h2 id="heading-how-to-set-a-semver-version-number">How to Set a SemVer Version Number</h2>
<p>There are two things you need to do to set a SemVer version number on an assembly during build.</p>
<p><strong>First,</strong> in a <code>&lt;PropertyGroup&gt;</code> element in the project’s <code>csproj</code> file, add element <code>&lt;IncludeSourceRevisionInInformationalVersion&gt;false&lt;/IncludeSourceRevisionInInformationalVersion&gt;</code>:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">PropertyGroup</span>&gt;</span>
 ...
 <span class="hljs-tag">&lt;<span class="hljs-name">IncludeSourceRevisionInInformationalVersion</span>&gt;</span>false<span class="hljs-tag">&lt;/<span class="hljs-name">IncludeSourceRevisionInInformationalVersion</span>&gt;</span> 
<span class="hljs-tag">&lt;/<span class="hljs-name">PropertyGroup</span>&gt;</span>
</code></pre>
<p>As described in <a target="_blank" href="https://developercommunity.visualstudio.com/t/Build-adds-string-to-assembly-Informatio/10515014?sort=newest">this issue</a>, this ensures that InformationalVersion is set exactly to the SemVer version number we specified and does not get a <code>+&lt;hash code&gt;</code> tacked on at the end.</p>
<p><strong>Second,</strong> pass the version number as value of <code>Version</code> property passed to <code>dotnet build</code> command e.g.:</p>
<pre><code class="lang-yaml"><span class="hljs-string">dotnet</span> <span class="hljs-string">build</span> <span class="hljs-string">--configuration</span> <span class="hljs-string">Release</span> <span class="hljs-string">-p</span> <span class="hljs-string">Version=1.0.2-beta</span>
</code></pre>
<p>This would set InformationalVersion in the compiled assembly (.exe or .dll file) to <code>1.0.2-beta</code>.</p>
<p>Incidentally, it would also set AssemblyVersion and FileVersion (an extra <code>0</code> would be added to the end of <code>1.0.2</code>) but we are not interested in those.</p>
<p>Note that instead pf passing <code>Version</code> argument on the command line, you can set MS Build property <code>&lt;Version&gt;1.0.2-beta&lt;/Version&gt;</code> in a <code>&lt;PropertyGroup&gt;</code> element in the csproj file. However passing a value of <code>Version</code> parameter to <code>dotnet build</code> is simpler because the csproj file does not need to be modified everytime the version number is incremented. This is helpful in CD pipelines. If you do set <code>&lt;Version&gt;</code> in csproj file, this will be overridden by whatever you provide on the <code>dotnet build</code> command line as the value of the <code>Version</code> property.</p>
<h2 id="heading-how-to-read-an-assemblys-semver-version-at-runtime">How to Read an Assembly’s SemVer Version at Runtime</h2>
<p>Code that reads InfromationalVersion at run time is as follows:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">string</span>? version = Assembly.GetEntryAssembly()?.
  GetCustomAttribute&lt;AssemblyInformationalVersionAttribute&gt;()?.
  InformationalVersion;
</code></pre>
<p>In my minimal APIs, to add a <code>/version</code> endpoint as I showed in the Introduction section above, I place the above snippet in <code>Program.cs</code>, then add the following snippet immediately after. Note that the whole thing should appear <strong>before</strong> <code>builder.Build()</code> <strong>is called</strong>:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">//this object of an anonymous type will </span>
<span class="hljs-comment">//be serialised as JSON in response body</span>
<span class="hljs-comment">//when returned by a handler</span>
<span class="hljs-keyword">var</span> objVersion = <span class="hljs-keyword">new</span> { Version = version ?? <span class="hljs-string">""</span> };

<span class="hljs-comment">//OTHER CODE</span>
<span class="hljs-comment">//var app = builder.Build()</span>
</code></pre>
<p>After <code>builder.Build()</code> is called, I create the handler for the <code>/version</code> endpoint:</p>
<pre><code class="lang-csharp">app.MapGet(<span class="hljs-string">"/version"</span>, () =&gt; objVersion);
</code></pre>
<p>Now when I run the API project and call the <code>/version</code> endpoint, I get the version number back in a JSON object in HTTP response body:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.2-beta"</span>
}
</code></pre>
<p>This is what the Hoppscotch screenshot in the Introduction showed.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article showed you how to set a SemVer version number in your .NET assemblies.</p>
<p>It also showed you how to read the version number at runtime.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up ESLint, Prettier, StyleLint, and lint-staged in Next.js ]]>
                </title>
                <description>
                    <![CDATA[ A linter is a tool that scans code for potential issues. This is invaluable with a programming language like JavaScript which is so loosely typed. Even for TypeScript, which is a strongly typed language whose compiler does a great job of detecting er... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-eslint-prettier-stylelint-and-lint-staged-in-nextjs/</link>
                <guid isPermaLink="false">66e88a49bd6e8dcd7dc5a8a2</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ eslint ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Prettier ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Stylelint ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Lint-Staged ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naveed Ausaf ]]>
                </dc:creator>
                <pubDate>Mon, 16 Sep 2024 19:43:05 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726515328917/f3ecbc80-6d06-45ee-b307-89ed1a7bc854.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A <strong>linter</strong> is a tool that scans code for potential issues. This is invaluable with a programming language like JavaScript which is so loosely typed.</p>
<p>Even for TypeScript, which is a strongly typed language whose compiler does a great job of detecting errors at compile time, linters such as ESLint have plugins that catch problems which are not caught by the compiler.</p>
<p>When you generate a new app using the Next.js CLI (<code>npx create-next-app</code>), ESLint is configured by default. But there are several problems with the linting setup generated by <code>create-next-app</code>:</p>
<ul>
<li><p>If you choose SCSS for styling, you should use <a target="_blank" href="https://stylelint.io/">Stylelint</a> in the build process to lint CSS or SCSS stylesheets. But it’s not set up automatically.</p>
</li>
<li><p>If instead you opt for Tailwind for styling, you should set up the Tailwind plugin for ESLint. But again, this isn’t done in the generated ESLint configuration.</p>
</li>
<li><p>If you choose TypeScript, then in Next.js v14 and below, TypeScript-specific ESLint rules are not configured, contrary to <a target="_blank" href="https://nextjs.org/docs/pages/building-your-application/configuring/eslint#typescript">what the documentation states</a>. While a Next.js v15 app has these set up, I would still tweak the setup further with the more powerful linting rules provided by the <a target="_blank" href="https://typescript-eslint.io/">typescript-eslint project</a>. These include the <a target="_blank" href="https://typescript-eslint.io/rules/no-floating-promises">no-floating-promises rule</a> which points out if you have forgotten to <code>await</code> a method that returns a <code>Promise</code>. These and other rules from typescript-eslint are going to save you lots and lots of times as you write TypeScript code.</p>
</li>
<li><p>And finally, Prettier is not set up. Prettier is a code formatting tool. It can prevent inconsistently formatted code from getting into the code repository, which would make comparisons between different versions of the same file difficult. Also, nicely formatted code is easier to work with. So this is a pretty big omission.</p>
</li>
</ul>
<p>In this tutorial, I'll show you how I set up linting and formatting in my Next.js projects in a way that addresses the issues above. I’ll also teach you how to install and configure some related VS Code extensions for coding assistance.</p>
<p>To follow along, you can either use a Next.js project you already have, or generate a new app by running <code>npx create-next-app</code> on the terminal.</p>
<p>If you’re scaffolding a new app, your choices are up to you (defaults are fine) but make sure to choose YES in response to the question about whether you’d like to use ESLint:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725930860799/fb38b3b2-5592-4eb4-b8d0-153aeb2d749d.png" alt="Terminal window in which Next.js scaffolder, create-next-app, is showing code generation options to the user" class="image--center mx-auto" width="688" height="383" loading="lazy"></p>
<p>If you are following along with an existing app rather than a new one, upgrade it by running the <a target="_blank" href="https://nextjs.org/docs/pages/building-your-application/upgrading/version-14">following command</a> in app root:</p>
<pre><code class="lang-bash">npm i next@latest react@latest react-dom@latest eslint-config-next@latest
npm i --save-dev eslint
</code></pre>
<p>This will avoid versioning conflicts down the line.</p>
<p>If you cannot upgrade to the latest version, you’ll need to specify versions for packages that will be installed in this tutorial to get around any version conflicts. Be warned that this can be frustrating.</p>
<p>Now you’re ready to open up the app in your code editor and proceed as follows.</p>
<p><strong>MONOREPO NOTE:</strong> A monorepo is a Git repository that contains multiple projects, each in its own subfolder. For example <a target="_blank" href="https://github.com/naveedausaf/flowmazondotnet">this repo</a> is for a full-stack solution that contains two projects: a Next.js app in folder <code>flowmazonfrontend</code> and a .NET Core API project in folder <code>flowmazonbackend</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743632328084/670c756d-a5f9-4333-80dc-446b3a5975bb.png" alt="Repo screenshot" class="image--center mx-auto" width="905" height="327" loading="lazy"></p>
<p>If your Next.js app is contained in a monorepo that contains both a Next.js app and other projects, <strong>I</strong> recommend that you do all of the setup below within the Next.js app's folder. This is the folder you should open in your code editor and <code>cd</code> into on the terminal. There is one bit of setup (lint-staged and Husky) that will need to be done in the monorepo root folder. I shall point it out in due course.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>For this tutorial, I assume that you know how to:</p>
<ul>
<li><p>write a basic Next.js app with two or more pages.</p>
</li>
<li><p>install additional NPM packages into your app</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-introduction">Introduction</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-table-of-contents">Table of Contents</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-prettier">Set Up Prettier</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-a-note-on-line-endings-in-prettier">A note on line endings in Prettier</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-eslint">Set Up ESLint</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-basics-of-eslint-configuration">Basics of ESLint configuration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-eslint-setup-for-typescript">ESLint Setup for TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-eslint-setup-for-tailwind">ESLint Setup for Tailwind</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-eslint-setup-for-prettier">ESLint Setup for Prettier</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-stylelint">Set Up Stylelint</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-packagejson-scripts">Set Up package.json Scripts</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-lint-staged">Set Up lint-staged</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-vs-code-extensions">Set UP VS Code Extensions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-checks-and-troubleshooting">Final Checks and Troubleshooting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-set-up-prettier">Set Up Prettier</h2>
<p><a target="_blank" href="https://prettier.io/">Prettier</a> is an opinionated code formatter that can format pretty much any file (<code>.html</code>, <code>.json</code>, <code>.js</code>, <code>.ts</code>, <code>.css</code>, <code>.scss</code> and so on).</p>
<p>Set it up in yuor app as follows:</p>
<ol>
<li><p>Install Prettier:</p>
<pre><code class="lang-bash"> npm install --save-dev prettier
</code></pre>
</li>
<li><p>If you chose Tailwind for styling when generating the app, then install <code>prettier-plugin-tailwindcss</code>:</p>
<pre><code class="lang-bash">   npm install --save-dev prettier-plugin-tailwindcss
</code></pre>
<p> This package is a Prettier plugin and provides rules for reordering of Tailwind classes used in a <code>class</code> or <code>className</code> attribute according to a canonical ordering. It helps keep the ordering of Tailwind classes used in the markup consistent.</p>
<p> %[https://youtu.be/tQkBJXwzY8A?autoplay=1] </p>
</li>
<li><p>Create <code>.prettierrc.json</code> in youyr project root. If you’re using SCSS for styling, paste the following snippet into this file:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"singleQuote"</span>: <span class="hljs-literal">true</span>,
   <span class="hljs-attr">"jsxSingleQuote"</span>: <span class="hljs-literal">true</span>
 }
</code></pre>
<p> If you’re using Tailwind instead, paste the following into <code>.prettierrc.json</code>:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"plugins"</span>: [<span class="hljs-string">"prettier-plugin-tailwindcss"</span>],
   <span class="hljs-attr">"singleQuote"</span>: <span class="hljs-literal">true</span>,
   <span class="hljs-attr">"jsxSingleQuote"</span>: <span class="hljs-literal">true</span>
 }
</code></pre>
</li>
<li><p>Create <code>.prettierignore</code> file in the app root, with the following content:</p>
<pre><code class="lang-plaintext"> node_modules
 .next
 .husky
 coverage
 .prettierignore
 .stylelintignore
 .eslintignore
 stories
 storybook-static
 *.log
 playwright-report
 .nyc_output
 test-results
 junit.xml
 docs
</code></pre>
<p> This file ensure that files which are not app code (that is, those which are not <code>.js</code>, <code>.ts</code>, <code>.css</code> files and so on.) do not get formatted. Otherwise Prettier will end up spending too much time processing files whose formatting you don't really care about.</p>
<p> <code>'prettierignore</code> (the file we just created), <code>.eslintignore</code>, and <code>.stylelintignore</code> have been ignored because these are plain text files with no structure so Prettier would complain that it cannot format them.</p>
</li>
<li><p>Finally, <strong>I recommend</strong> that you follow the steps in <a target="_blank" href="https://nausaf.hashnode.dev/lf-vs-crlf-configure-git-and-vs-code-on-windows-to-use-unix-line-endings">this post</a> to set LF as the EOL character, both in the repo and in your VS Code settings. Reasoning for this is given in the following subsection.</p>
</li>
</ol>
<h3 id="heading-a-note-on-line-endings-in-prettier">A note on line endings in Prettier</h3>
<p>Prettier <a target="_blank" href="https://prettier.io/docs/en/options#end-of-line">defaults to LF (Line Feed character) for line endings</a>. This means that when it formats files, it will change all occurrences of the CRLF character sequence, if any, to LF.</p>
<p>LF is also the default in text editors and other tools in Unix-based systems (Linux, MacOS etc.). But on Windows, the default for line endings is CRLF (Carriage Return character, followed immediately by Line Feed character).</p>
<p>Windows tooling such as text and code editors can easily handle LF as line ending. But CRLF can be problematic for tools on Unix-based systems such as Linux and various flavours of Unix. Therefore it makes sense to only use LF as line endings in code as this would work on both Windows and Unix-based systems.</p>
<p>Configuring LF as the EOF character in Git repo and in code editors will bring your tooling in line with Prettier's default. It will also ensure that all files in the Git repo consistently have LF line endings. Thus if a contributor to your repo is on Windows which uses CRLF as EOL character, the code they add or modify in the repo would still use LF: the code editor would default new code files to LF; <code>git</code> commit` would convert any CRLFs to LF when committing.</p>
<p>Finally, setting LF as the line endings for the whole repo would avoid strange things that happen when on Windows, Prettier retains its default of LF but Git and your code editor continue to use their default of CRLF for line endings:</p>
<ul>
<li><p>When VS Code Prettier extension formats a file (for example, when the extension is set up to "autoformat on save"), it does not change CRLF line endings. But formatting the same file by running Prettier on the command line <strong>does change line endings to LF</strong>. This discrepancy can be annoying.</p>
</li>
<li><p>Git may show warnings like this when when you run <code>git add .</code>:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725930986122/b5630966-e8dd-4f47-bb58-eed6eb023ea6.png" alt="Warnings shown by git add command when some of the files being added contain LF but the repo's line ending default is CRLF" class="image--center mx-auto" width="800" height="78" loading="lazy"></p>
</li>
</ul>
<h2 id="heading-set-up-eslint">Set Up ESLint</h2>
<h3 id="heading-basics-of-eslint-configuration">Basics of ESLint configuration</h3>
<p>ESLint comes with a number of linting rules out of the box. But you can also supplement these with ESLint plugins.</p>
<p>An <strong>ESLint plugin</strong> defines some linting rules. For example, if you look in the <a target="_blank" href="https://github.com/vercel/next.js/tree/canary/packages/eslint-plugin-next">GitHub repo for Next's ESLint plugin</a>, eslint-plugin-next, each file in the <a target="_blank" href="https://github.com/vercel/next.js/tree/canary/packages/eslint-plugin-next/src/rules"><code>src/rules</code></a> folder defines a linting rule as a TypeScript function. The <a target="_blank" href="https://github.com/vercel/next.js/blob/canary/packages/eslint-plugin-next/src/index.ts"><code>index.js</code></a> of the package then exports these rule functions in the <code>rules</code> object in its default export:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = {
  rules: {
    <span class="hljs-string">'google-font-display'</span>: <span class="hljs-built_in">require</span>(<span class="hljs-string">'./rules/google-font-display'</span>),
    <span class="hljs-string">'google-font-preconnect'</span>: <span class="hljs-built_in">require</span>(<span class="hljs-string">'./rules/google-font-preconnect'</span>),
    <span class="hljs-string">'inline-script-id'</span>: <span class="hljs-built_in">require</span>(<span class="hljs-string">'./rules/inline-script-id'</span>),
    ...
</code></pre>
<p>The basic way to use these rules in your app is to install the plugin package, then reference it in the ESLint configuration file in the app's root folder.</p>
<p>For example, we can use rules from the <code>eslint-plugin-next</code> mentioned above by running <code>npm install --save-dev eslint-plugin-next</code>, then placing the following content in the ESLint config file <code>.eslintrc.json</code> in the app root:</p>
<pre><code class="lang-json">{
    plugins: [<span class="hljs-string">"next"</span>],
    <span class="hljs-attr">"rules"</span>: {
        <span class="hljs-attr">"google-font-display"</span>: <span class="hljs-string">"warning"</span>,
        <span class="hljs-attr">"google-font-preconnect"</span>: <span class="hljs-string">"warning"</span>,
        <span class="hljs-attr">"inline-script-id"</span>: <span class="hljs-string">"error"</span>,
    }
}
</code></pre>
<p>If you now run <code>npx eslint .</code> in your app's root folder, ESLint will lint every JavaScript file in the app against each of the three rules configured above.</p>
<p>There are three severities you can assign to a rule when configuring it for use: <code>off</code>, <code>warning</code> and <code>error</code>. As the snippet above shows, you enable a rule by assigning to it a severity of <code>warning</code> or <code>error</code> in the app's <code>.eslintrc.json</code>.</p>
<p>When referencing a plugin in your app's ESLint configuration file, the prefix <code>eslint-plugin-</code> in the plugin's package name is omitted. This is why the package that contains linting rules for Next.js, <code>eslint-plugin-next</code>, is referenced only as <code>"next"</code> in the snippet above.</p>
<p>Since it is quite cumbersome to configure a severity level - <code>off</code>, <code>warning</code> or <code>error</code> - for every single rule from every plugin that you want to use, the norm is to reference an <a target="_blank" href="https://eslint.org/docs/latest/extend/shareable-configs">ESLint sharable config</a> , or <strong>config</strong> for short, that is exported by an NPM package. This is a JavaScript object that declares plugins and configures rules from these with severity levels just as we did above.</p>
<p>For example, the default export from <code>eslint-plugin-next</code> also contains several ESLint configs. Here is a another snippet from <a target="_blank" href="https://github.com/vercel/next.js/blob/canary/packages/eslint-plugin-next/src/index.ts"><code>index.js</code> of the plugin</a>, this time showing exported ESLint configs in addition to the <code>rules</code> object for exporting rule functions:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = {
  rules: {
    <span class="hljs-string">'google-font-display'</span>: <span class="hljs-built_in">require</span>(<span class="hljs-string">'./rules/google-font-display'</span>),
    <span class="hljs-string">'google-font-preconnect'</span>: <span class="hljs-built_in">require</span>(<span class="hljs-string">'./rules/google-font-preconnect'</span>),
    <span class="hljs-string">'inline-script-id'</span>: <span class="hljs-built_in">require</span>(<span class="hljs-string">'./rules/inline-script-id'</span>),
    ...
},    
configs: {
    recommended: {
      plugins: [<span class="hljs-string">'@next/next'</span>],
      rules: {
        <span class="hljs-comment">// warnings</span>
        <span class="hljs-string">'@next/next/google-font-display'</span>: <span class="hljs-string">'warn'</span>,
        <span class="hljs-string">'@next/next/google-font-preconnect'</span>: <span class="hljs-string">'warn'</span>,
        ...

        <span class="hljs-comment">// errors</span>
        <span class="hljs-string">'@next/next/inline-script-id'</span>: <span class="hljs-string">'error'</span>,
        <span class="hljs-string">'@next/next/no-assign-module-variable'</span>: <span class="hljs-string">'error'</span>
        ...

      }
    },
    <span class="hljs-string">'core-web-vitals'</span>: {
      plugins: [<span class="hljs-string">'@next/next'</span>],
      <span class="hljs-keyword">extends</span>: [<span class="hljs-string">'plugin:@next/next/recommended'</span>],
      rules: {
        <span class="hljs-string">'@next/next/no-html-link-for-pages'</span>: <span class="hljs-string">'error'</span>,
        <span class="hljs-string">'@next/next/no-sync-scripts'</span>: <span class="hljs-string">'error'</span>,
      },
    },
}
</code></pre>
<p>As you can see, in addition to the rules (there are many more than those shown above), the plugin also exports two configs - <code>recommended</code> and <code>core-web-vitals</code> - that enable different selections of the rules defined in the plugin by assigning severity levels of <code>error</code> or <code>warning</code> to them.</p>
<p>The config that is normally used in Next.js projects is <code>core-web-vitals</code>. We can use this config object in our app’s ESLint configuration file (<code>.eslintrc.json</code> in app root) as follows:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: [<span class="hljs-string">"plugin:next/core-web-vitals"</span>]
}
</code></pre>
<p>Thus is much simpler than declaring the plugin in <code>plugins</code> object and then assigning a severity level of <code>error</code> or <code>warning</code> to each rule from the plugin that we want to use.</p>
<p>Note the difference between <em>Configuration File</em> – this is  the ESLint configuration file named <code>.eslintrc.json</code> - and <em>Shareable Config</em> (or <em>config</em> for short) – this is an object that configures some rules from an ESLint plugin for use in a client project by assigning severities to selected rules.</p>
<p>Contents of the configuration file are themselves a config. But in configuration files, we do not typically import a plugin and configure all rules from it that we want to use. Instead we almost always import a well-known/trusted config object that is exported by an NPM package. Such a config object - one that is exported by an NPM package for use in ESLint configuration files (in other packages/apps) - is also known as a <a target="_blank" href="https://eslint.org/docs/latest/extend/shareable-configs">shareable config</a>.</p>
<p>Typically, plugins - these define ESLint rules as JavaScript/TypeScript functions - also bundle their rules into one or more shareable configs. The <code>recommended</code> config from plugin <code>eslint-plugin-next</code> that we used above is just one such config.</p>
<p>Shareable configs do not only come from plugin packages, although it is customary for plugins to also export one or more shareable configs composed of their own rules. Other packages, whose names begin with <code>eslint-config-</code> (as opposed to <code>eslint-plugin-</code>) can provide one or more named configs.</p>
<p>Next.js provides one such package named <code>eslint-config-next</code>. This re-exports configs <code>recommended</code> and <code>core-web-vitals</code> from the plugin. It also re-exports (in v15 and above of the package) a config of TypeScript linting rules from plugin <code>typescript-eslint/eslint-plugin</code>. So instead of using <code>recommended</code> config from the plugin like we have done above:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: [<span class="hljs-string">"plugin:next/core-web-vitals"</span>]
}
</code></pre>
<p>we could have installed the package <code>eslint-config-next</code> and used that in <code>.eslintrc.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: [<span class="hljs-string">"next/core-web-vitals"</span>]
}
</code></pre>
<p>Since the package's name is not prefixed with <code>plugin:</code>, ESLint considers it to be a config package, reconstructing the name as <code>eslint-config-next</code> rather than as <code>eslint-plugin-next</code>. Notice how with config packages also, we delete the canonical prefix <code>eslint-config-</code> when referencing it in the ESLint configuration file.</p>
<p><strong>NOTE:</strong> In order to reference a shareable config provided by a package that only provides such configs but does not define or export linting rules, such as <code>eslint-config-next</code>, all you need to do is install the package and add the shareable config to <code>"extends"</code> in your <code>.eslintrc.js</code>. This is shown in the snippet above.</p>
<p>But if you want to reference a shareable config from an ESLint <strong>plugin</strong>, for example the package <code>eslint-plugin-storybook</code> which exports a shareable config named <code>recommended</code>, but you do NOT want to configure individual rules exported by the plugin, then I am not sure whether you need to declare the package in <code>"plugins"</code> array in your <code>.eslintrc.js</code> or not. I would declare it in <code>"plugins"</code> anyway as it may actually be necessary but wouldn't do any harm if it's not. Almost every ESLint example that I have come across, such as <a target="_blank" href="https://typescript-eslint.io/getting-started/legacy-eslint-setup#step-2-configuration">this one</a><a target="_blank" href="https://typescript-eslint.io/getting-started/legacy-eslint-setup#step-2-configuration">, does t</a>he same. </p>
<p>For example, if I want to use shareable config <code>recommended</code> from plugin <code>eslint-plugin-storybook</code>, and even though I would not be configuring any individual linting rules provided by this plugin, I would both reference the config in <code>"extends"</code> and, just to be safe, declare the plugin itself in <code>"plugins"</code> (after installing the plugin via command <code>npm install -D eslint-plugin-storybook</code>):</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"plugins"</span>: [<span class="hljs-string">"storybook"</span>],
    <span class="hljs-attr">"extends: ["</span>plugin:storybook/recommended<span class="hljs-string">",]
}</span>
</code></pre>
<p>It is possible to reference multiple shareable configs in <code>extends</code>. In this case, all the rules from all configs are used - except where there are multiple configs that each provide a rule with the same name. In this case the last config, proceeding left to right, wins. This is to say where there is a naming conflict, ESLint will use the rule from the last config on the list.</p>
<p>It is possible to use <a target="_blank" href="https://eslint.org/docs/latest/use/configure/configuration-files-deprecated">ESLint configuration file formats other than JSON</a>. You can provid<a target="_blank" href="https://typescript-eslint.io/getting-started/legacy-eslint-setup#step-2-configuration">e the sa</a>me information as in an <code>.eslintrc.json</code> file in a JavaScript (<code>.eslintrc.js</code> or <code>.eslintrc.cjs</code>) or yaml (<code>.eslintrc.yml</code> or <code>.eslintrc.yaml</code>) file instead.</p>
<p>Also, ESLint has a new configuration file format often called <a target="_blank" href="https://eslint.org/docs/latest/use/configure/configuration-files">flat config</a> (which I haven't used here) where the config files are either JavaScript or TypeScript files.</p>
<p>Armed with an understanding of how to configure ESLint for use, you are ready to set up ESLint in your Next.js project. The sections below shows you how to do this.</p>
<h3 id="heading-eslint-setup-for-typescript">ESLint Setup for TypeScript</h3>
<p><strong>If your app uses TypeScript</strong>, modify the ESLint configuration file (.esilntrc.json) as follows:</p>
<ol>
<li><p>On the terminal, in app's root folder, run the following command:</p>
<pre><code class="lang-bash"> npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript
</code></pre>
<p> <code>@typescript-eslint/eslint-plugin</code> provides a number of linting rules for TypeScript files, as well as shareable configs, that augment the checking that the TypeScript compiler does.</p>
<p> <code>@typescript-eslint/parser</code> is a parser that allows ESLint to parse TypeScript files (by default it can only parser JavaScript files).</p>
<p> I am adding TypeScript compiler as a package - <code>typescript</code> - because <a target="_blank" href="https://typescript-eslint.io/getting-started"><code>typescript-eslint</code> Getting Started instructions</a> do the same.</p>
</li>
<li><p>In app root folder, rename <code>.eslintrc.json</code> to <code>.eslintrc.js</code>. Then Replace contents of <code>.eslintrc.js</code> in app root with the following:</p>
<pre><code class="lang-javascript"> <span class="hljs-comment">/* eslint-env node */</span>
 <span class="hljs-built_in">module</span>.exports = {
   <span class="hljs-attr">root</span>: <span class="hljs-literal">true</span>,
   <span class="hljs-attr">extends</span>: [
     <span class="hljs-string">'next/core-web-vitals'</span>,
   ],
   <span class="hljs-attr">plugins</span>: [<span class="hljs-string">'@typescript-eslint'</span>, <span class="hljs-string">'tailwindcss'</span>],
   <span class="hljs-attr">parser</span>: <span class="hljs-string">'@typescript-eslint/parser'</span>,
   <span class="hljs-attr">overrides</span>: [
     {
       <span class="hljs-attr">files</span>: [<span class="hljs-string">'*.ts'</span>, <span class="hljs-string">'*.tsx'</span>],
       <span class="hljs-attr">parserOptions</span>: {
         <span class="hljs-attr">project</span>: [<span class="hljs-string">'./tsconfig.json'</span>],
         <span class="hljs-attr">projectService</span>: <span class="hljs-literal">true</span>,
         <span class="hljs-attr">tsconfigRootDir</span>: __dirname,
       },
       <span class="hljs-attr">extends</span>: [
         <span class="hljs-string">'next/core-web-vitals'</span>,
         <span class="hljs-string">'plugin:@typescript-eslint/recommended'</span>,
         <span class="hljs-comment">//'plugin:@typescript-eslint/recommended-type-checked',</span>
         <span class="hljs-comment">// 'plugin:@typescript-eslint/strict-type-checked',</span>
         <span class="hljs-comment">// 'plugin:@typescript-eslint/stylistic-type-checked',</span>
       ]
     },
   ],
 };
</code></pre>
<p> <strong>This is what the various lines of this file do</strong>:</p>
<p> <code>/* eslint-env node */</code> stops ESLint from complaining that this is a CommonJS module. We have had to put this in because ESLint, as we have configured it here, does not allow CommonJS modules (which <code>.eslintrc.js</code> is, see <code>module.exports = ...</code> at the top) and expects modules in the project to be ES6.</p>
<p> <code>root: true</code> says this is the topmost ESLint configuration file even though there may be nested ESLint configs in subfolders.</p>
<p> <code>extends:</code> specifies various ESLint configs, each of which enables a collection of linting rules.</p>
<p> <code>'next/core-web-vitals'</code> is a config provided by <code>eslint-config-next</code> that bundles Next.js-specific rules (both for JavaScript and TypeScript, from an inspection of its code on GitHub).</p>
<p> The <code>recommended-type-checked</code> config (used in a nested <code>extends</code> within <code>overrides</code> object - this is explained shortly) is provided by <code>@typescript-eslint/eslint-plugin</code>. This plugin is part of the <a target="_blank" href="https://typescript-eslint.io/">typescript-eslint</a> project that publishes packages for linting rules and parsers to support linting of TypeScript files by ESLint.</p>
<p> The configs used is <a target="_blank" href="https://typescript-eslint.io/users/configs#recommended-configurations">described here</a>. It is a superset of the non-type checked versions of the config, <code>recommended</code>. It adds linting rules which use TypeScript's type checking API for additional type information. These rules are more powerful than those contained in the base, non-type-checked <code>recommended</code> config that only rely on the ESLint parser for TypeScript - package <code>@typescript-eslint/parser</code>.</p>
<p> You might prefer to use the <code>strict-type-checked</code> and <code>stylistic-type-checked</code> configs, also provided by <code>@typescript-eslint/eslint-plugin</code>. These are stricter than what I have used.</p>
<p> The least strict choice for TypeScript linting would probably be the <code>recommended</code> config. This is what is re-exported by <code>eslint-plugin-next</code> as config named <code>typescript</code> and is referenced in <a target="_blank" href="https://nextjs.org/docs/app/building-your-application/configuring/eslint#typescript">Next.js instructions for setting up ESLint with TypeScript</a> as <code>next/typescript</code> (at least as of the time of this writing, September 2024). I prefer the config I have used instead.</p>
<p> <code>parser: '@typescript-eslint/parser'</code> specifies the ESLint TypeScript parser to be used instead of the default Espree parser which cannot parser TypeScript files.</p>
<p> <code>parserOptions:</code> tells the parser where to find the <code>tsconfig.json</code> file. This information allows the rules in the type-checked config used above - <code>recommended-type-checked</code> - to use TypeScript type checking APIs.</p>
<p> If we were using non-type-checked rules contained in other configs exported by the plugin, such as the <code>recommended</code> config, we would not need to provide this information.</p>
<p> <code>plugins: ['@typescript-eslint']</code> : I don't know what the purpose of this line is. It shouldn't be necessary and I have tested that the given ESLint configuration works fine without it. But it doesn't do any harm and was contained in <a target="_blank" href="https://typescript-eslint.io/getting-started/typed-linting">an example</a> in the plugin's documentation from which I adapted the above config. So I have kept it.</p>
<p> The <code>overrides</code> section ensures that the TypeScript parser options that we’ve had to configure in order to support type-checked configs apply only to <code>.ts</code> and <code>.tsx</code> extensions (from <a target="_blank" href="https://stackoverflow.com/questions/58510287/parseroptions-project-has-been-set-for-typescript-eslint-parser">this excellent StackOverflow answer</a>). Otherwise, if <code>parser</code> and <code>parserOptions</code> objects had been at the top level, then running ESLint on the project would throw errors on <code>.js</code> files.</p>
<p> This is a problem as we have several <code>.js</code> config files including the <code>.eslintrc.js</code> itself, so there will be linting errors. We can avoid these errors by using the override.</p>
</li>
</ol>
<h3 id="heading-eslint-setup-for-tailwind">ESLint Setup for Tailwind</h3>
<p><strong>If your app uses Tailwind,</strong> modify the config as follows:</p>
<ol>
<li><p>On the terminal, in app's root folder, run <code>npm install --save-dev eslint-plugin-tailwindcss</code></p>
</li>
<li><p>In ESLint config, add <code>"plugin:tailwindcss/recommended"</code> to the END of <code>extends</code>:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"extends"</span>: [<span class="hljs-string">"next/core-web-vitals"</span>, ..., <span class="hljs-string">"plugin:tailwindcss/recommended"</span>],

 }
</code></pre>
</li>
<li><p>In the ESLint config, add <code>"tailwindcss"</code> to <code>plugins</code> and add a <code>rules</code> object as shown below:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"plugins"</span>: [..., <span class="hljs-string">"tailwindcss"</span>],
   <span class="hljs-attr">"rules"</span>: {
     <span class="hljs-attr">"tailwindcss/classnames-order"</span>: <span class="hljs-string">"off"</span>
   },          
 }
</code></pre>
</li>
<li><p><strong>If your app uses TYPESCRIPT,</strong> then also add <code>"plugin:tailwindcss/recommended"</code> to inner <code>extends</code> inside <code>overrides</code> and duplicate the <code>rules</code> object inside <code>overrides</code>:</p>
<pre><code class="lang-javascript">   {
     ...
     overrides: [
     {
        <span class="hljs-attr">extends</span>: [<span class="hljs-string">"next/core-web-vitals"</span>, ..., <span class="hljs-string">"plugin:tailwindcss/recommended"</span>],
        <span class="hljs-attr">rules</span>: {
          <span class="hljs-string">'tailwindcss/classnames-order'</span>: <span class="hljs-string">'off'</span>,
        },
     }
   }
</code></pre>
</li>
</ol>
<p>In the Tailwind setup steps above, we have installed the package for the ESLint plugin for Tailwind, <code>eslint-plugin-tailwindcss</code>, and used the config <code>recommended</code> provided by the plugin.</p>
<p><code>eslint-plugin-tailwind</code> provides some useful linting rules for Tailwind CSS classes used in HTML or JSX/TSX markup. The biggest one for me is that if a class used in code is not a Tailwind class, there would be a linting error. This makes sense as when I am using Tailwind, I only use Tailwind-generated classes and do not define my own CSS classes.</p>
<p>The plugin also has a rule that checks that the sequence of Tailwind class names used in the <code>class</code> or <code>className</code> attribute in markup follows a canonical ordering. But we installed <code>prettier-plugin-tailwindcss</code> in our Prettier configuration above which also reorders Tailwind class names. So we don’t need this rule in ESLint and it might conflict with what Prettier does in our workflow.</p>
<p>We’ll turn this rule off, which is named <code>tailwindcss/classnames-order</code>, in the configuration above by declaring the plugin in <code>plugins</code> object, then setting the rule to <code>off</code> in the <code>rules</code> object.</p>
<h3 id="heading-eslint-setup-for-prettier">ESLint Setup for Prettier</h3>
<ol>
<li><p>On the terminal run:</p>
<pre><code class="lang-bash"> npm install --save-dev eslint-config-prettier
</code></pre>
</li>
<li><p>In ESLint config, add <code>"prettier"</code> to the END of <code>extends</code>:</p>
<pre><code class="lang-javascript"> {
   <span class="hljs-string">"extends"</span>: [<span class="hljs-string">"next/core-web-vitals"</span>, ..., <span class="hljs-string">"prettier"</span>]          
 }
</code></pre>
</li>
<li><p><strong>If your app uses TypeScript</strong>, then also add <code>"plugin:tailwindcss/recommended"</code> to the inner <code>extends</code> inside <code>overrides</code> also:</p>
<pre><code class="lang-javascript">  {
    ...
    overrides: [
    {
       <span class="hljs-string">"extends"</span>: [<span class="hljs-string">"next/core-web-vitals"</span>, ..., <span class="hljs-string">"prettier"</span>],
    }
  }
</code></pre>
<p> In the Prettier setup steps above, the config referenced as <code>prettier</code> is the name of the NPM package <a target="_blank" href="https://www.npmjs.com/package/eslint-config-prettier">eslint-config-prettier</a> with <code>eslint-config-</code> deleted. The default export from the package is an entire ESLint config object and this is the config we want to use.</p>
<p> So in this case, we do not suffix the name <code>prettier</code> with <code>/&lt;name of config&gt;</code> as we have done when referencing the named config <code>core-web-vitals</code> from package <code>eslint-config-next</code> when we referenced is as <code>next/core-web-vitals</code> (see step 1 above).</p>
<p> This config switches off those rules in ESLint that conflict with the code formatting done by Prettier. This should be the last config in <code>extends</code>.</p>
</li>
<li><p>Create <code>.eslintignore</code> in the project root. It doesn't need to have any content for now, but will come in handy in the future if ever you need to add folders or files that should be ignored by ESLint (see the final section of this post for an example).</p>
</li>
</ol>
<h2 id="heading-set-up-stylelint">Set Up Stylelint</h2>
<p>Stylelint is a linter for CSS and SCSS stylesheets.</p>
<p><strong>If you are using SCSS and NOT Tailwind,</strong> then set up Stylelint by following the instructions below. This set up will work for both CSS and SCSS files:</p>
<ol>
<li><p>On the terminal in project root run this command:</p>
<pre><code class="lang-bash"> npm install --save-dev sass
</code></pre>
<p> Next.js has <a target="_blank" href="https://nextjs.org/docs/basic-features/built-in-css-support#sass-support">built-in SASS/SCSS support</a> (so the Webpack config knows how to handle <code>.scss</code> and <code>.sass</code> files). But you still need to install a version of the <code>sass</code> package yourself, which is what we did above.</p>
</li>
<li><p>Next, install packages for Stylelint and its rule configs:</p>
<pre><code class="lang-bash"> npm install --save-dev stylelint stylelint-config-standard-scss stylelint-config-prettier-scss
</code></pre>
<p> Of these three packages:</p>
<ul>
<li><p><a target="_blank" href="https://stylelint.io/"><code>stylelint</code></a> is the linter.</p>
</li>
<li><p><a target="_blank" href="https://github.com/stylelint-scss/stylelint-config-standard-scss"><code>stylelint-config-standard-scss</code></a> is a Stylelint config that provides linting rules. It uses the Stylelint plugin <a target="_blank" href="https://www.npmjs.com/package/stylelint-scss">stylelint-css</a> and extends configs <a target="_blank" href="https://github.com/stylelint/stylelint-config-standard">stylelint-config-standard</a> which defines rules for vanilla CSS, and <a target="_blank" href="https://github.com/stylelint-scss/stylelint-config-recommended-scss">stylelint-config-recommended-scss</a> which defines SCSS specific rules. As a result, extending from this one config is enough to get linting support for both CSS and SCSS files.</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/stylelint-config-prettier-scss"><code>stylelint-config-prettier-scss</code></a> extends <a target="_blank" href="https://www.npmjs.com/package/stylelint-config-prettier">stylelint-config-prettier</a> and turns off those Stylint rules that conflict with Prettier's code formatting. This should be declared last in <code>extends:</code> array in <code>.stylelintrc.json</code> (as shown below).</p>
</li>
</ul>
</li>
<li><p>Now, create <code>.stylelintrc.json</code> in project root with the following contents:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"extends"</span>: [
     <span class="hljs-string">"stylelint-config-standard-scss"</span>,
     <span class="hljs-string">"stylelint-config-prettier-scss"</span>
   ],
   <span class="hljs-attr">"rules"</span>: {
     <span class="hljs-attr">"selector-class-pattern"</span>: <span class="hljs-literal">null</span>
   }
 }
</code></pre>
<p> The <code>"extends"</code> section declares the two Stylelint configs whose NPM packages we installed in the previous step.</p>
<p> The <code>"rules"</code> section is used to configure stylints rules. Here you can turn on or off, or configure the behavior of, individual Stylelint rules.</p>
<p> You can turn off a rule by setting it to <code>null</code>, as I have done for <code>"selector-class-pattern"</code>. I turned it off because it insists on having CSS classes in the so called kebab case (for example, <code>.panel-quiz</code> instead of <code>.panelQuiz</code>). I find it inconvenient for various reasons so I turned it off.</p>
</li>
<li><p>Next, create <code>.stylelintignore</code> in the project root with the following contents:</p>
<pre><code class="lang-json"> styles/globals.css
 styles/Home.module.css
 coverage
</code></pre>
<p> I created this file so that the two stylesheets generated by the Next.js CLI which do not comply with the linting rules can get ignored (there might be a better way of doing this but this works for me). Also, files in <code>coverage</code> folder do not need to be linted and would likely throw up errors.</p>
</li>
</ol>
<h2 id="heading-set-up-packagejson-scripts">Set Up <code>package.json</code> Scripts</h2>
<ol>
<li><p>The most important script is <code>"build"</code>. The default command for this script, <code>next build</code>, runs ESLint but not Prettier or (if you are using SCSS) Stylelint. So modify it in <code>package.json</code> file as follows:</p>
<p> <strong>If your app uses Tailwind, then:</strong></p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"scripts"</span>: {
     <span class="hljs-attr">"build"</span>: <span class="hljs-string">"prettier --check . &amp;&amp; next build"</span>,
     ...
</code></pre>
<p> <strong>Otherwise, if your app uses SCSS, then:</strong></p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"scripts"</span>: {
     <span class="hljs-attr">"build"</span>: <span class="hljs-string">"prettier --check . &amp;&amp; stylelint --allow-empty-input \"**/*.{css,scss}\" &amp;&amp; next build"</span>,
     ...
</code></pre>
<p> With this tweak to the existing <code>build</code> script, we can run <code>npm run build</code> either locally or in a CI/CD pipeline and it will fail not only on ESLint failure (this was the case before) but also on Prettier formatting or Stylelint failure.</p>
<p> Indeed if you <a target="_blank" href="https://nextjs.org/learn/basics/deploying-nextjs-app/deploy">deploy your app to Vercel</a>, the default pipeline there also calls <code>npm run build</code>. So when I introduced an error in one of my stylesheets, then deployed to Vercel, I got the following Stylelint error during deployment:</p>
<p> <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/om6228598f4pvb2iuwj9.png" alt="Stylelint errors when build script is run to build the app in Vercel's deployment pipeline" width="957" height="692" loading="lazy"></p>
<p> Note that I used the <code>--check</code> flag with <code>prettier</code> in the script (that is, I used the command <code>prettier --check .</code>). This runs Prettier in check mode, so it only checks for correct formatting and does not change the formatting.</p>
<p> I did this because the <code>build</code> script is what Vercel's deployment pipeline calls by default to build the code, and I don't want formatting to change during a CI build (nor do I want to tinker with Vercel defaults unless I absolutely have to).</p>
<p> To run Prettier locally to actually format the codebase, I define a separate <code>build:local</code> script which is same as <code>build</code> but runs Prettier without the <code>--check</code> flag, as well as a separate <code>format</code> script just to format with Prettier (but not build). These are set up below.</p>
</li>
<li><p>Set up the <code>"format"</code> script in your <code>package.json</code>. This formats the codebase with Prettier and comes in handy every now and then:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"scripts"</span>: {
     ...
     <span class="hljs-attr">"format"</span>: <span class="hljs-string">"prettier --write ."</span>
</code></pre>
</li>
<li><p>I recommend setting up a <code>build:local</code> script as follows:</p>
<p> <strong>If your app uses Tailwind,</strong> then:</p>
<pre><code class="lang-json"> <span class="hljs-string">"build:local"</span>: <span class="hljs-string">"prettier --write . &amp;&amp; next build"</span>
</code></pre>
<p> <strong>Otherwise, if your app uses SCSS,</strong> then:</p>
<pre><code class="lang-json"> <span class="hljs-string">"build:local"</span>: <span class="hljs-string">"prettier --write . &amp;&amp; stylelint --allow-empty-input \"**/*.{css,scss}\" &amp;&amp; next build"</span>
</code></pre>
<p> Since we cannot format the code with Prettier prior to executing <code>next build</code> in the existing <code>build</code> script (for reasons described above), we can use this script locally to format code then lint and build in one go.</p>
</li>
</ol>
<h2 id="heading-set-up-lint-staged">Set Up lint-staged</h2>
<p><a target="_blank" href="https://github.com/okonet/lint-staged">lint-staged</a> is a package that you can use to run formatting and linting commands on staged files in a Git repo. Staged files are those that have been added to the Git index using <code>git add .</code>. These are the files that have changed since the last commit and will get committed when you next run <code>git commit</code>.</p>
<p><a target="_blank" href="https://github.com/typicode/husky">Husky</a> is the typical choice in Node.js packages for registering commands to run in Git hooks. For example, registering the command <code>npx lint-staged</code> with Husky to run in the Git pre-commit hook means lint-staged will run automatically whenever you execute <code>git commit</code>.</p>
<p>At that time, the formatter (Prettier) and linters (ESLint or Stylelint) that have been configured to run in the lint-staged configuration file will run on the staged files. If there are any errors during formatting checks or linting, the commit will fail.</p>
<p>Whenever <code>git commit</code> fails due to linting errors, we can fix those, then run <code>git add .</code> and <code>git commit</code> again. Thus code only ever gets into the repo after it has been consistently formatted and verified to be free of linting errors. This is particularly advantageous in a team setting.</p>
<p>I prefer to only run <code>prettier --check .</code> on staged files. In particular, I do not change formatting of staged files and do not lint during a commit, for the following reasons:</p>
<p><strong>Reason for not formatting code:</strong> I almost always build and test my code before committing. Any code formatting should have happened prior to or during this local build and test.</p>
<p>I find the idea that code going into my repo should change automatically just as it is being committed <em>after</em> I have ascertained that any code changes are good to go, a little bit unappealing.</p>
<p><strong>Reason for not linting code:</strong> With TypeScript code, the compiler can catch a huge number of issues in code. The additional linting rules provided by <code>eslint-typescript/eslint-plugin</code> <a target="_blank" href="https://typescript-eslint.io/troubleshooting/faqs/typescript#why-dont-i-see-typescript-errors-in-my-eslint-output">only supplement the checks made by the TypeScript compiler</a>. So if I am linting code in staged files at commit time, I should build as well (so that the TypeScript compiler runs).</p>
<p>But building can be very time consuming on a large codebase. Besides, I almost always build and test before committing. Doing so implicitly runs linting (<code>next build</code> runs ESLint and my build scripts in <code>package.json</code>, as set up above, run any other linting that is necessary e.g. StyleLint). So I don’t feel the need to repeat lint or build on staged files via lint-staged.</p>
<p>Any lint- or build errors that might occasionally slip through into the Git repo would be pointed out when my Continuous Delivery pipeline builds the codebase (let's say in GitHub Actions on in Vercel's build pipeline). I can fix them then, in the pull request, before merge to <code>main</code>.</p>
<p>So, my personal preference is only to check for formatting on staged files, and neither reformat nor lint the code. This prevents inconsistently formatted code from getting into the Git repo where inconsistent formatting would make comparisons between different versions of the same file difficult.</p>
<p><strong>So now, set up lint-staged and Husky as follows:</strong></p>
<p><strong>Note</strong>: These tools need to be set up in the root of the Git repo. If you have a monorepo that contains multiple projects, including the Next.js app in which you performed the setup above, then open the root folder of this monorepo in your code editor and also <code>cd</code> into it on the terminal. Otherwise stay in the folder of your Next.js app.</p>
<ol>
<li><p>If you have a monorepo and its root folder does NOT have a <code>package.json</code> file, initialize an NPM package in the root:</p>
<pre><code class="lang-bash"> npm init -y
</code></pre>
</li>
<li><p>Install the lint-staged package:</p>
<pre><code class="lang-bash"> npm install --save-dev lint-staged
</code></pre>
</li>
<li><p>Create a file named <code>lint-staged.config.js</code> with the following contents:</p>
<pre><code class="lang-javascript"> <span class="hljs-comment">/* eslint-env node */</span>
 <span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);
 <span class="hljs-keyword">const</span> formatCommand = <span class="hljs-string">'prettier . --check'</span>;

 <span class="hljs-built_in">module</span>.exports = {
   <span class="hljs-string">'*'</span>: formatCommand,
 };
</code></pre>
</li>
<li><p>If you are in a monorepo, the <code>lint-staged.config.js</code> created above needs needs to be tweaked because in this, Prettier would be invoked from multi-repo root but the files it needs to format, as well as its config file (<code>.prettierrc.json</code>) and its ignore file (<code>.prettierignore</code>), are all in the subfolder that contains the Next.js app. Do the following:</p>
<ul>
<li><p><code>npm install --save-dev prettier prettier-plugin-tailwindcss</code></p>
</li>
<li><p>Replace the contents of <code>lint-staged.config.js</code> with the following. Make sure to substitute <code>&lt;Next.js app subfolder&gt;</code> with the actual name of your Next.js app folder:</p>
<pre><code class="lang-bash">  /* eslint-env node */
  const path = require(<span class="hljs-string">"path"</span>);
  const formatCommand =
    <span class="hljs-string">"prettier --check --config ./&lt;Next.js app subfolder&gt;/.prettierrc.json --ignore-path ./&lt;Next.js app subfolder&gt;/.prettierignore"</span>;

  module.exports = {
    <span class="hljs-string">"./&lt;Next.js app subfolder&gt;/**/*"</span>: formatCommand,
  };
</code></pre>
</li>
</ul>
</li>
<li><p>Install the Husky NPM package.</p>
<pre><code class="lang-bash"> npm install --save-dev husky
</code></pre>
</li>
<li><p>Run the following on the terminal in app root to configure Husky to run <code>lint-staged</code> whenever <code>git commit</code> runs (in Git's pre-commit hook):</p>
<pre><code class="lang-bash"> npx husky init
 <span class="hljs-built_in">echo</span> <span class="hljs-string">"npx lint-staged"</span> &gt; .husky/pre-commit
</code></pre>
<p> You should now have a file <code>.husy/pre-commit</code> in your app's folder (or in your monorepo root folder, if you're  working in a monorepo) with only one line: <code>npx lint-staged</code>.</p>
</li>
</ol>
<h2 id="heading-set-up-vs-code-extensions">Set Up VS Code Extensions</h2>
<p>If you use VS Code as your code editor, you can install the following VS Code extensions to provide linting and formatting on file save and syntax highlight on linting errors:</p>
<ul>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">ESLint extension</a></p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier extension</a></p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint">Stylelint extension</a> (if you're using SCSS and not Tailwind)</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss">TaliwindCSS extension</a> (if you are using Tailwind and not SCSS)</p>
</li>
</ul>
<p><strong>Put the following in a</strong> <code>settings.json</code> <strong>file in the</strong> <code>.vscode</code> <strong>folder in the project</strong> (you can of course put these settings in you User Preferences file also. You can access it from Command Palette <strong>Ctrl + P</strong>).</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"[javascript]"</span>: {
    <span class="hljs-attr">"editor.defaultFormatter"</span>: <span class="hljs-string">"esbenp.prettier-vscode"</span>
  },
  <span class="hljs-attr">"[typescript]"</span>: {
    <span class="hljs-attr">"editor.defaultFormatter"</span>: <span class="hljs-string">"esbenp.prettier-vscode"</span>
  },
  <span class="hljs-attr">"[typescriptreact]"</span>: {
    <span class="hljs-attr">"editor.defaultFormatter"</span>: <span class="hljs-string">"esbenp.prettier-vscode"</span>
  },
  <span class="hljs-attr">"[javascriptreact]"</span>: {
    <span class="hljs-attr">"editor.defaultFormatter"</span>: <span class="hljs-string">"esbenp.prettier-vscode"</span>
  },
  <span class="hljs-attr">"[scss]"</span>: {
    <span class="hljs-attr">"editor.defaultFormatter"</span>: <span class="hljs-string">"esbenp.prettier-vscode"</span>
  },
  <span class="hljs-attr">"stylelint.validate"</span>: [<span class="hljs-string">"css"</span>, <span class="hljs-string">"scss"</span>],
  <span class="hljs-attr">"editor.formatOnSave"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">"eslint.useFlatConfig"</span>: <span class="hljs-literal">false</span>
}
</code></pre>
<p>As they’re set up, the extensions will lint and format on Save.</p>
<p><code>"eslint.useFlatConfig": false</code> ensures that the ESLint extension is able to pick up your <code>.eslintrc.js</code> and doesn't look for <code>eslint.config.js</code> which is the filename for a config file in ESLint's new <a target="_blank" href="https://eslint.org/blog/2022/08/new-config-system-part-2/">flat config format</a> that I have NOT used in this post.</p>
<p>If you encounter any problems in getting the ESLint extension to work, turn on the extension's debug output by adding line <code>"eslint.debug": true</code> to the end of the <code>.vscode/settings.json</code> you just created. Then restart VS Code and look at ESLint extension's output in the OUTPUT window:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746569174032/3e705307-1588-4dd2-bd28-98c2d118a07a.png" alt="3e705307-1588-4dd2-bd28-98c2d118a07a" class="image--center mx-auto" width="1207" height="301" loading="lazy"></p>
<h2 id="heading-final-checks-and-troubleshooting">Final Checks and Troubleshooting</h2>
<p>Now it’s time to build and commit:</p>
<pre><code class="lang-bash">npm run format
npm run build
git add .
git commit -m <span class="hljs-string">"fix: set up linting and formatting"</span>
</code></pre>
<p>Building and committing is a good sanity check for the setup we just did.</p>
<p>If anything had not been set up correctly, you might get errors either during build or at commit.</p>
<p>If you already had some code in the project, then there might be a few errors when you commit. Typically, these can be resolved by:</p>
<ul>
<li><p>Adding folders or files to one of the <code>*ignore</code> files. For example, I already had some code in my project with <a target="_blank" href="https://storybook.js.org/">Storybook</a> installed. So I had to add folders <code>.storybook</code> and <code>storybook-static</code> to each of <code>.stylelintignore</code>, <code>.eslintignore</code> and <code>.prettierignore</code> as all three tools complained about them.</p>
<pre><code class="lang-bash">  stories
  storybook-static
</code></pre>
</li>
<li><p>If Prettier complains (shows a <code>[warn]</code> or <code>[error]</code> on the terminal) about some files not being formatted properly, you either need to add them to <code>.prettierignore</code> or format them by running <code>npm run format</code> in the folder of your Next.js app.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743632684491/fa5f073f-4de4-4b55-be00-184f8a36c802.png" alt="fa5f073f-4de4-4b55-be00-184f8a36c802" class="image--center mx-auto" width="719" height="444" loading="lazy"></p>
</li>
<li><p>Adding plugins for specific file types. For example, I had Gherkin <code>.feature</code> files in my project to describe integration tests. Prettier couldn't format these. So I added the <a target="_blank" href="https://www.npmjs.com/package/prettier-plugin-gherkin?activeTab=readme">prettier-plugin-gherkin</a> by simply running:</p>
<pre><code class="lang-plaintext">  npm install  prettier-plugin-gherkin --save-dev
</code></pre>
<p>  Note that <a target="_blank" href="https://prettier.io/docs/en/plugins.html">usually it is enough to install the package for a Prettier plugin</a> for Prettier to locate it and additional configuration is not required.</p>
<p>  Likewise, ESLint complained when it encountered <code>.cy.ts</code> files containing Cypress interaction tests for my app. To resolve this linting error, I installed the NPM package for Cypress ESLint plugin and configured it <a target="_blank" href="https://github.com/cypress-io/eslint-plugin-cypress#installation">as described here</a> (unlike Prettier, to get this ESLint package to work, some configuration was required).</p>
</li>
<li><p>The typescript config might be too strict and there might be a lot of errors when you build, such as:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725931098287/9618aff2-844b-4b15-9b14-2c8d377bf8c8.png" alt="ESLint error that occur on build when an ESLint config for TypeScript is used that is too strict." width="725" height="478" loading="lazy"></p>
<p>  If you do not want to fix individual errors in your existing codebase, and they are too many to disable specific rules at error locations using ESLint comments (see below), then the simplest solution would be to disable the <code>@typescript-eslint/recommended-type-checked</code> config by commenting it out in <code>.eslintrc.js</code> and uncommenting <code>@typescript-eslint/recommended</code> which is less strict.</p>
</li>
<li><p>Sometimes it is safe to turn off a linting rule at a specific line or for a whole file. While I am always wary of doing this, in a (deliberately bad) experimental code file, I had many instances of an error that VS Code ESLint extension pointed. This was not caught before but was now being pointed out because strict TypeScript linting rules had been enabled:</p>
<p>  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sg2ne9v2w8dbquv9ytad.png" alt="Intellisense in VS Code showing an ESLint error on a lint of code in a file." width="865" height="217" loading="lazy"></p>
<p>  So I pressed <code>Ctrl + .</code> to Show Code Actions (I could instead have clicked the yellow lighbulb icon shown next to the issue), then selected “Disable <code>@typescript/no-non-null-assertion</code> for the entire file”.</p>
<p>  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pqume7pod3jzflvblrus.png" alt="VS Code shows helpful tooltips when you click the bulb icon in the gutter. One of these allows y uoto disable an ESLint rule that is causing an error on the currently selected line of code." width="878" height="326" loading="lazy"></p>
<p>  This placed the comment <code>/* eslint-disable @typescript-eslint/no-non-null-assertion */</code> on top of my file to disable all instances of that particular error within the file:</p>
<p>  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d2kml4o11xpqaenk93b.png" alt="Special &quot;eslint-disable&quot; comment on top of a code file that disable a specific ESLint rule through the file." width="625" height="133" loading="lazy"></p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This tutorial showed you how to configure linting and formatting tools in your Next.js app. I hope that it also gave you the background necessary both to understand the configurations given, and to customize them as needed.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
