<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Leosvel Pérez Espinosa</title>
    <description>The latest articles on Forem by Leosvel Pérez Espinosa (@leosvelperez).</description>
    <link>https://forem.com/leosvelperez</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F340141%2Fd975daeb-c896-4dd7-8dd4-b0244eab9004.jpeg</url>
      <title>Forem: Leosvel Pérez Espinosa</title>
      <link>https://forem.com/leosvelperez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/leosvelperez"/>
    <language>en</language>
    <item>
      <title>Set up Tailwind CSS with Angular in an Nx workspace</title>
      <dc:creator>Leosvel Pérez Espinosa</dc:creator>
      <pubDate>Fri, 28 Jan 2022 15:18:44 +0000</pubDate>
      <link>https://forem.com/nx/set-up-tailwind-css-with-angular-in-an-nx-workspace-1km4</link>
      <guid>https://forem.com/nx/set-up-tailwind-css-with-angular-in-an-nx-workspace-1km4</guid>
      <description>&lt;p&gt;&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; is a utility-first CSS framework packed with a lot of good functionality out of the box while providing a high level of customization. It has gained a lot of attention since it came out and it’s a good option when it comes to styling our applications.&lt;/p&gt;

&lt;p&gt;In this blog post, we are going to see how we can use Tailwind CSS with &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; in an &lt;a href="https://nx.dev/" rel="noopener noreferrer"&gt;Nx&lt;/a&gt; monorepo. We are going to be looking at different scenarios and how to approach them.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Please note that this blog post is not meant to be a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; or &lt;strong&gt;Angular&lt;/strong&gt; tutorial. The purpose is to show how to use them together and how &lt;strong&gt;Nx&lt;/strong&gt; can help to improve the developer experience and expand the native &lt;strong&gt;Angular&lt;/strong&gt; capabilities when working with &lt;strong&gt;Tailwind CSS&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What are we going to build?
&lt;/h2&gt;

&lt;p&gt;The final result of what we are going to be building can be found in this Github repository: &lt;a href="https://github.com/leosvelperez/angular-tailwind-nx" rel="noopener noreferrer"&gt;https://github.com/leosvelperez/angular-tailwind-nx&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We are going to create 2 simple applications with the following layout:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2030%2F1%2AOXo64rzyF-5nKO2fbpG4xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2030%2F1%2AOXo64rzyF-5nKO2fbpG4xg.png" alt="Applications mockup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll start by creating one application with the required markup and &lt;strong&gt;Tailwind CSS&lt;/strong&gt; utility classes to achieve the above layout. Then, we’re going to leverage &lt;strong&gt;Nx&lt;/strong&gt;’s library support and extract some common UI components into 2 different shared libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a regular non-buildable library containing the header,&lt;/li&gt;
&lt;li&gt;a buildable library containing the card elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, we’ll create the second application using the components exposed by those shared libraries. Finally, we’ll extract the button elements to a publishable library and adjust both applications to use them.&lt;/p&gt;

&lt;p&gt;The idea is to show how different applications can still use the same components and have them styled differently using &lt;strong&gt;Tailwind CSS&lt;/strong&gt;. Both applications in this blog post will share the same layout, but the approach explained here would apply to applications with different layouts sharing the same UI components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Discussing the different types of libraries and the motivation to use them goes beyond the scope of this blog post. We’ll use the three different types just to showcase how to use &lt;strong&gt;Tailwind CSS&lt;/strong&gt; with each of them. To know more about them, please check &lt;a href="https://nx.dev/structure/creating-libraries" rel="noopener noreferrer"&gt;https://nx.dev/structure/creating-libraries&lt;/a&gt; and &lt;a href="https://nx.dev/structure/buildable-and-publishable-libraries" rel="noopener noreferrer"&gt;https://nx.dev/structure/buildable-and-publishable-libraries&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setting up the Nx workspace
&lt;/h2&gt;

&lt;p&gt;First things first! We start by creating a new &lt;strong&gt;Nx&lt;/strong&gt; workspace where our applications and libraries will be located. To do that, we can run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ npx create-nx-workspace@latest angular-tailwind-nx --pm=yarn
✔ What to create in the new workspace · angular
✔ Application name                    · app1
✔ Default stylesheet format           · css
✔ Use Nx Cloud? (It's free and doesn't require registration.) · No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Passing the &lt;code&gt;--packageManager&lt;/code&gt; (or &lt;code&gt;--pm&lt;/code&gt;) flag allows us to change the package manager. If not passed, it defaults to &lt;code&gt;npm&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The above command creates a workspace called &lt;code&gt;angular-tailwind-nx&lt;/code&gt; and asks us a few questions to help us set up the workspace. We chose the &lt;code&gt;angular&lt;/code&gt; preset, provided &lt;code&gt;app1&lt;/code&gt; for the initial &lt;strong&gt;Angular&lt;/strong&gt; application name, chose &lt;code&gt;css&lt;/code&gt; as the stylesheet to use, and this time chose not to use &lt;a href="https://nx.app/" rel="noopener noreferrer"&gt;Nx Cloud&lt;/a&gt; but feel free to opt-in to use the &lt;strong&gt;Nx Cloud&lt;/strong&gt; free tier to benefit from distributing the computation caching of your projects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Any of the stylesheet options can be used. Also, using &lt;strong&gt;Nx Cloud&lt;/strong&gt; or not doesn’t affect setting up &lt;strong&gt;Tailwind CSS&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have a workspace with an &lt;strong&gt;Angular&lt;/strong&gt; application ready to be used, let’s start adding some &lt;strong&gt;Tailwind CSS&lt;/strong&gt; magic!&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Angular&lt;/strong&gt; added native support for building applications using &lt;strong&gt;Tailwind CSS&lt;/strong&gt; a while ago. Still, we need to set it up in the workspace, and to do so, we can use the &lt;code&gt;@nrwl/angular:setup-tailwind&lt;/code&gt; generator by simply running:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:setup-tailwind app1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The above command will do a few things for us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It will check if &lt;code&gt;tailwindcss&lt;/code&gt; is already installed and if not installed, it will install the necessary packages (&lt;code&gt;tailwindcss&lt;/code&gt;, &lt;code&gt;postcss&lt;/code&gt; and &lt;code&gt;autoprefixer&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;It will create a &lt;code&gt;tailwind.config.js&lt;/code&gt; file in the project root with the default configuration to get started (specific to the installed version)&lt;/li&gt;
&lt;li&gt;It will recognize the project type and for applications, it will update the application styles entry point file located at &lt;code&gt;apps/app1/src/styles.css&lt;/code&gt; by including the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; base styles&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The above generator supports &lt;strong&gt;Tailwind CSS&lt;/strong&gt; v2 and v3.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s take a look at the generated &lt;code&gt;apps/app1/tailwind.config.js&lt;/code&gt; file:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;We can see the &lt;code&gt;content&lt;/code&gt; property is configured to scan for all the HTML and TypeScript files within our application and besides that, there’s also a call to a function called &lt;code&gt;createGlobPatternsForDependencies&lt;/code&gt;. This is a pretty handy function that will identify the dependencies of the application and return the glob patterns for them. This ensures that &lt;strong&gt;Tailwind CSS&lt;/strong&gt; utility classes that are used in the application’s dependencies are also taken into account and included in the final CSS of the application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instead of using the &lt;code&gt;createGlobPatternsForDependencies&lt;/code&gt;, we could just add the &lt;code&gt;libs/**/*&lt;/code&gt; glob pattern that captures all libraries, but that could lead to bundling more CSS than needed in monorepos with multiple applications. This would happen because all applications would be scanning all libraries, regardless of whether they are dependencies or not. Such a glob pattern could also lead to scaling issues in large monorepos due to the amount of libraries to scan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can also see that the generator updated the &lt;code&gt;apps/app1/src/styles.css&lt;/code&gt; file with the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; bases styles:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And that’s all we need. We can now go ahead and add our custom theme and layout to achieve the desired design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a custom theme and the application markup
&lt;/h2&gt;

&lt;p&gt;First, we are going to update the &lt;code&gt;theme&lt;/code&gt; section of the generated &lt;code&gt;apps/app1/tailwind.config.js&lt;/code&gt;. We are going to overwrite the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; default theme and provide the custom palette of colors and spacing of our theme to be used throughout the application:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Next, we update the &lt;code&gt;apps/app1/src/app/app.component.html&lt;/code&gt; file with the required markup and several &lt;strong&gt;Tailwind CSS&lt;/strong&gt; utility classes to style the application with the look &amp;amp; feel we are looking for:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;With all set, let’s see it in action by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx run app1:serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Visiting &lt;a href="https://localhost:4200" rel="noopener noreferrer"&gt;https://localhost:4200&lt;/a&gt; in your browser should show the application looking like the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8128%2F1%2ATkIVfzbeier6j0nlAeX7ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8128%2F1%2ATkIVfzbeier6j0nlAeX7ig.png" alt="Application 1 screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it! We have successfully created our application to fulfill the requirements we had. Next, we are going to start extracting pieces of the UI into shared libraries to reuse them with the second application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tailwind CSS and Angular libraries in an Nx workspace
&lt;/h2&gt;

&lt;p&gt;Before extracting our UI components into libraries, we need to take a step back and make sure we understand how &lt;strong&gt;Tailwind CSS&lt;/strong&gt; works and the implications of the different types of libraries in an &lt;strong&gt;Nx&lt;/strong&gt; workspace.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://tailwindcss.com/docs/installation" rel="noopener noreferrer"&gt;Tailwind CSS docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tailwind CSS works by scanning all of your HTML files, JavaScript components, and any other templates for class names, generating the corresponding styles and then writing them to a static CSS file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Any project can use the &lt;a href="https://tailwindcss.com/blog/standalone-cli" rel="noopener noreferrer"&gt;Tailwind CSS CLI&lt;/a&gt; or &lt;a href="https://postcss.org/" rel="noopener noreferrer"&gt;PostCSS&lt;/a&gt; with the &lt;code&gt;tailwindcss&lt;/code&gt; plugin to scan the relevant files in the project and collect the usage of the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; utility classes, functions, and custom CSS directives (custom CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule" rel="noopener noreferrer"&gt;at-rules&lt;/a&gt;). With that information, the final CSS styles are generated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular&lt;/strong&gt; uses &lt;strong&gt;PostCSS&lt;/strong&gt; to support &lt;strong&gt;Tailwind CSS&lt;/strong&gt;. As we saw in a previous section, with the help of an &lt;strong&gt;Nx&lt;/strong&gt; generator, it’s pretty straightforward to configure a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; for applications. Libraries can also be easily configured, but there are some nuances regarding how they are processed and whether they need to be configured or not.&lt;/p&gt;

&lt;p&gt;In an &lt;strong&gt;Nx&lt;/strong&gt; workspace, a regular library (non-buildable and non-publishable) is just a slice of an application that is only built as part of the build process of an application that consumes it. Because of that, as long as the application that consumes it has &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configured, the library code will be processed as expected even though the library itself doesn’t have a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration. In fact, adding a &lt;code&gt;tailwind.config.js&lt;/code&gt; file to the library won’t have any effect whatsoever (it’ll be ignored) because the library is never built on its own.&lt;/p&gt;

&lt;p&gt;On the other hand, buildable and publishable libraries are meant to be built on their own and their compiled output to be shared with the consumers. Therefore, they need to be able to process any &lt;strong&gt;Tailwind CSS&lt;/strong&gt; directive or function (e.g. &lt;code&gt;@apply&lt;/code&gt;, &lt;code&gt;theme()&lt;/code&gt;) when they are built. If no &lt;strong&gt;Tailwind CSS&lt;/strong&gt; directive or function is used, then the configuration is not needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Providing the configuration to a buildable or publishable library that doesn’t need it adds some unnecessary overhead to the build process. The executor will still load the configuration and the &lt;strong&gt;PostCSS&lt;/strong&gt; plugin will process the stylesheets unnecessarily.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How does this work?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt; produces the relevant CSS code where the following directives and functions are used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@tailwind&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@apply&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;theme()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;screen()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the &lt;strong&gt;PostCSS&lt;/strong&gt; plugin processes a file containing these, it processes them and produces the corresponding CSS code based on the provided configuration. If none of the above is used in a buildable or publishable library, no CSS is generated, and therefore, no configuration is needed. The actual CSS will be generated when building the application consuming those libraries.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To know more about the above directives and functions you can take a look at &lt;a href="https://tailwindcss.com/docs/functions-and-directives" rel="noopener noreferrer"&gt;https://tailwindcss.com/docs/functions-and-directives&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But we do use &lt;strong&gt;Tailwind CSS&lt;/strong&gt; utility classes in the libraries and CSS needs to be generated for them. So, how is the CSS generated for those classes if the libraries are not configured?&lt;/p&gt;

&lt;p&gt;If we recall from a previous section, in our application’s &lt;code&gt;tailwind.config.js&lt;/code&gt; file, we have the following:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;content&lt;/code&gt; property of the configuration tells &lt;strong&gt;Tailwind CSS&lt;/strong&gt; where to look for usages of utility classes. When the &lt;strong&gt;PostCSS&lt;/strong&gt; plugin finds a file using the &lt;code&gt;@tailwind&lt;/code&gt; directive, it will collect all the utility classes for the layer specified by the directive in the files matching the glob patterns set in the &lt;code&gt;content&lt;/code&gt; property of the configuration, and it will produce the CSS replacing the directive. It’s worth noting that the &lt;strong&gt;PostCSS&lt;/strong&gt; plugin only scans the files collecting the utility classes that are used, it doesn’t process them. Only the file containing the &lt;code&gt;@tailwind&lt;/code&gt; directive is updated with the resulting CSS.&lt;/p&gt;

&lt;p&gt;Since we have our application configured to scan the relevant files within itself and also within its dependencies, the utility classes used in the libraries that are dependencies of the application will be picked up correctly and the CSS will be generated for them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s important to note that the support for &lt;strong&gt;Tailwind CSS&lt;/strong&gt; for &lt;strong&gt;Angular&lt;/strong&gt; libraries is only available using the &lt;code&gt;@nrwl/angular:ng-packagr-lite&lt;/code&gt; executor for buildable libraries and the &lt;code&gt;@nrwl/angular:package&lt;/code&gt; executor for publishable libraries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Below is a small decision tree to check whether a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration is needed for your library in an &lt;strong&gt;Nx&lt;/strong&gt; workspace:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2Ax_nZUiADymUMNsmDpAU3aA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2Ax_nZUiADymUMNsmDpAU3aA.png" alt="Decision tree for the need of Tailwind CSS configuration in Angular libraries"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting the header into a library
&lt;/h2&gt;

&lt;p&gt;Our application is looking good. At the same time, there’s a great opportunity to reuse some of its components in another application. Therefore, we are going to extract the shared components into several shared libraries.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The applications of this blog post are very simple and it’s probably not worthy to extract the components into multiple libraries. We are doing it for illustrative purposes to cover the different scenarios we might find in real-life applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’ll start by extracting the header of the application into a reusable component and placing it into a library. To do so, we start by creating a new &lt;strong&gt;Angular&lt;/strong&gt; library in our workspace by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:lib lib1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Next, we create the component for the header in the library we just generated and we export it so it can be imported by consumers:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:component header --project=lib1 --export
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Add the markup for the header to the &lt;code&gt;libs/lib1/src/lib/header/header.component.html&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Import &lt;code&gt;Lib1Module&lt;/code&gt; into our application’s &lt;code&gt;AppModule&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And finally, replace the existing markup for the header in the &lt;code&gt;apps/app1/src/app/app.component.html&lt;/code&gt; file with the newly created header component and leaving the rest of the file as-is:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this point, if we serve again the application, everything should still be working the same way as before. We successfully extracted the header into a shared library and made it reusable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting  the card into a buildable library
&lt;/h2&gt;

&lt;p&gt;Similar to the previous section we are going to start by creating a new library to add the card component to. The only difference is that this library is going to be buildable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are not aware of what buildable libraries are or what problem do they intend to solve, please make sure to read &lt;a href="https://nx.dev/ci/incremental-builds" rel="noopener noreferrer"&gt;https://nx.dev/ci/incremental-builds&lt;/a&gt; and particularly pay attention to &lt;a href="https://nx.dev/ci/incremental-builds#when-should-i-use-incremental-builds" rel="noopener noreferrer"&gt;when should they be used&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run the following command to generate the library:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:lib lib2 --buildable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Next, we configure &lt;strong&gt;Tailwind CSS&lt;/strong&gt; for it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:setup-tailwind lib2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;As explained in a previous section when we did the same for the application, the above command will install any required dependencies if needed, create the &lt;code&gt;tailwind.config.js&lt;/code&gt; file and in the specific case of libraries, it will also add the &lt;code&gt;tailwindConfig&lt;/code&gt; property to the build target of the project configuration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Angular&lt;/strong&gt; applications, buildable and publishable libraries can be created with &lt;strong&gt;Tailwind CSS&lt;/strong&gt; support with a single command. We’ll see that in the upcoming section. The &lt;code&gt;@nrwl/angular:setup-tailwind&lt;/code&gt; generator is used to add &lt;strong&gt;Tailwind CSS&lt;/strong&gt; support to existing projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, we create the card component:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:component card --project=lib2 --export
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;We add the component to the library entry point located in &lt;code&gt;libs/lib2/src/index.ts&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Then, we update the card component files to provide the desired functionality:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Import &lt;code&gt;Lib2Module&lt;/code&gt; into our application’s &lt;code&gt;AppModule&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And finally, replace the existing markup for the cards in the &lt;code&gt;apps/app1/src/app/app.component.html&lt;/code&gt; file with the newly created card component:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;With that in place, we can serve the application and it should be working exactly as before, but our application is still not fully set up to consume the library build output. As it stands right now, when the application that’s consuming it is built, the library will be built together with it and its files will be processed as part of the application build pipeline.&lt;/p&gt;

&lt;p&gt;To finish the buildable library setup, we can follow the instructions in &lt;a href="https://nx.dev/ci/setup-incremental-builds-angular#adjust-the-app-executor" rel="noopener noreferrer"&gt;https://nx.dev/ci/setup-incremental-builds-angular#adjust-the-app-executor&lt;/a&gt;. We need to install the &lt;code&gt;@nrwl/web&lt;/code&gt; package, change the application &lt;code&gt;build&lt;/code&gt; target executor to &lt;code&gt;@nrwl/angular:webpack-browser&lt;/code&gt;, and change the application &lt;code&gt;serve&lt;/code&gt; target executor to &lt;code&gt;@nrwl/web:file-server&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add -D @nrwl/web@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;You can now go ahead and serve the application to check everything is working as expected. You should see the buildable library being built on its own before the application is built and served.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;@nrwl/web:file-server&lt;/code&gt; executor uses the &lt;a href="https://www.npmjs.com/package/http-server" rel="noopener noreferrer"&gt;http-server&lt;/a&gt; package to serve the built artifacts of our application. This is a lightweight HTTP server and it doesn’t do things like live-reload or HMR. Be sure to refresh your browser while making changes to see them reflected.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Using Tailwind CSS directives and functions in buildable libraries
&lt;/h2&gt;

&lt;p&gt;Our application is consuming a buildable library and still working as intended, but if we think about it, we didn’t configure our theme in the library’s &lt;code&gt;tailwind.config.js&lt;/code&gt; file. So, how is it still working?&lt;/p&gt;

&lt;p&gt;If we go back to the decision tree shared in a previous section, we’ll see that a buildable library only needs a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration if we use a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; directive or function. As of right now, our library is not using any. We are just using some utility classes and those are processed correctly as part of the application build. You could go ahead and delete the &lt;code&gt;tailwind.config.js&lt;/code&gt; file from the library and check that everything still works the same (if you do, please make sure to restore it before we continue).&lt;/p&gt;

&lt;p&gt;Next, we are going to refactor our newly created card component to make use of some of these directives and functions and see the implications.&lt;/p&gt;

&lt;p&gt;Update the card component files content as shown below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We created some CSS classes where we are applying the same styles we had in the component template. We are applying those styles by using a combination of the &lt;code&gt;@apply&lt;/code&gt; directive and the &lt;code&gt;theme&lt;/code&gt; function.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note we are not recommending creating CSS classes for scenarios like the above. The card component is already a reusable component and there’s no need to extract CSS classes to style it. We are only extracting these classes to showcase how &lt;strong&gt;Tailwind CSS&lt;/strong&gt; directives and functions can be correctly processed in buildable libraries. Make sure to check out the &lt;a href="https://tailwindcss.com/docs/reusing-styles" rel="noopener noreferrer"&gt;Tailwind CSS recommendations for reusing styles&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we now serve our application (or build the library), we’ll find ourselves with the following error:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;------------------------------------------------------------------------------
Building entry point '@angular-tailwind-nx/lib2'
------------------------------------------------------------------------------
/angular-tailwind-nx/libs/lib2/src/lib/card/card.component.css:2:3: The `p-lg` class does not exist. If `p-lg` is a custom class, make sure it is defined within a `@layer` directive.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This is to be expected. The library build is failing because now we are using some &lt;strong&gt;Tailwind CSS&lt;/strong&gt; directives and functions, and therefore, those directives and functions are being processed within the library context. Since we haven’t touched the &lt;code&gt;tailwind.config.js&lt;/code&gt; file, &lt;strong&gt;Tailwind CSS&lt;/strong&gt; doesn’t know about our custom theme.&lt;/p&gt;

&lt;p&gt;To solve the issue, we need to configure the library to be aware of our custom theme so it can process the library’s files correctly. Let’s update the &lt;code&gt;theme&lt;/code&gt; property of the &lt;code&gt;libs/lib2/tailwind.config.js&lt;/code&gt; file to match our application theme:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now, we should see our application working correctly if we serve it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing the Tailwind CSS configuration between the application and the buildable library
&lt;/h2&gt;

&lt;p&gt;Though we have successfully solved the issue and our workspace now has a library that can be built on its own and be cached, the experience is not great. We had to duplicate the application configuration in the buildable library. This introduces a maintainability concern and it will most likely be a cause for errors due to having to maintain them in sync. Also, we only have one buildable library in this small example, but imagine a real-life scenario where hundreds of these libraries need to be kept in sync. A nightmare!&lt;/p&gt;

&lt;p&gt;Well, no need to fret!&lt;/p&gt;

&lt;p&gt;If we think about it, the same reasoning behind creating shared libraries applies to this. We just need to share the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration. To do so, we have a couple of options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a shared file containing and exporting the theme so it can be imported by every project’s &lt;code&gt;tailwind.config.js file&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://tailwindcss.com/docs/presets" rel="noopener noreferrer"&gt;Tailwind CSS preset&lt;/a&gt; to expose a base configuration for your projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last option is the better one. We can take advantage of the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; built-in support for defining a base configuration to be reused across different projects. The first option is almost the same, with the difference that we have to manually handle merging the configurations.&lt;/p&gt;

&lt;p&gt;We’ll go ahead and create a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; preset and we’ll then use it in our projects. Start by creating a &lt;code&gt;tailwind.config.js&lt;/code&gt; file in the root of the workspace with the following content:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We just added the configuration that is common to our projects to use as a base in each of them. Next, we need to add the preset configuration to each project.&lt;/p&gt;

&lt;p&gt;Update both &lt;code&gt;apps/app1/tailwind.config.js&lt;/code&gt; and &lt;code&gt;libs/lib2/tailwind.config.js&lt;/code&gt; files to match the following:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Notice how we added the preset and removed almost all the configuration because it’s already defined in the preset.&lt;/p&gt;

&lt;p&gt;That’s all it takes. You can go ahead and serve the application (or refresh the browser if you are already serving it) to check everything is running correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing the Tailwind CSS preset in a library
&lt;/h2&gt;

&lt;p&gt;We now only have to maintain our theme in a single place as opposed to keeping in sync the configuration of all the different projects. But we can still improve the experience. As it stands, if you now make a change on the &lt;code&gt;tailwind.config.js&lt;/code&gt; file located at the root of the workspace (our preset), the file server doesn’t pick up the change and therefore, it doesn’t rebuild the affected projects.&lt;/p&gt;

&lt;p&gt;This happens because the file server is watching for changes under the &lt;code&gt;apps&lt;/code&gt; and &lt;code&gt;libs&lt;/code&gt; folders. The preset configuration is not under those directories, it’s in the root of the workspace.&lt;/p&gt;

&lt;p&gt;It would be better if we place the preset configuration in a small shared library. By doing that, we not only solve the issue regarding detecting changes on it, but we also make its library appear on the &lt;a href="https://nx.dev/using-nx/mental-model#the-project-graph" rel="noopener noreferrer"&gt;Nx project graph&lt;/a&gt;, and with that, we benefit from all the goodies associated with the project graph (affected commands, enforcing module boundaries constraints, etc.).&lt;/p&gt;

&lt;p&gt;This library is only going to contain the &lt;code&gt;tailwind.config.js&lt;/code&gt; file and no targets in the project configuration. There’s no generator among the &lt;strong&gt;Nx&lt;/strong&gt; core plugins that generate such an empty library. We could use one of the library generators and remove some content, but let’s create it manually.&lt;/p&gt;

&lt;p&gt;Start by creating a new folder &lt;code&gt;libs/tailwind-preset&lt;/code&gt; and moving the &lt;code&gt;tailwind.config.js&lt;/code&gt; file we created in the previous section at the root of the workspace to that folder.&lt;/p&gt;

&lt;p&gt;Next, add the project to the &lt;code&gt;angular.json&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Create the configuration for the project in &lt;code&gt;libs/tailwind-preset/project.json&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And finally, adjust both &lt;code&gt;apps/app1/tailwind.config.js&lt;/code&gt; and &lt;code&gt;libs/lib2/tailwind.config.js&lt;/code&gt; files to import the preset from the correct location:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Once again, if we serve our application everything should still be working as expected, but now our file server will pick up the changes made to the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; preset configuration.&lt;/p&gt;

&lt;p&gt;Also, if we visualize the workspace projects we’ll see how &lt;code&gt;app1&lt;/code&gt; and &lt;code&gt;lib2&lt;/code&gt; now have a dependency on tailwind-preset:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AF1p0OfL4WrmubTQ2QPX04Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AF1p0OfL4WrmubTQ2QPX04Q.png" alt="Project graph showing dependencies between the projects in the workspace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the second application
&lt;/h2&gt;

&lt;p&gt;We are now at a stage where we can develop our second application without having to duplicate the common functionality. So, before going ahead and distributing our buttons in a publishable library, let’s first create the second application to see how we can reuse what we have been putting into libraries.&lt;/p&gt;

&lt;p&gt;There’s one important thing to note though, this new application will have a different theme.&lt;/p&gt;

&lt;p&gt;Generate the application by running the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:app app2 --addTailwind --style=css --routing=false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The above command will generate the new application and it will configure &lt;strong&gt;Tailwind CSS&lt;/strong&gt; as well. Using the &lt;code&gt;--addTailwind&lt;/code&gt; flag will instruct the application generator to automatically run the &lt;code&gt;@nrwl/angular:setup-tailwind&lt;/code&gt; generator when creating a new application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;--addTailwind&lt;/code&gt; flag is available in both &lt;code&gt;@nrwl/angular:app&lt;/code&gt; and &lt;code&gt;@nrwl/angular:lib&lt;/code&gt; generators.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s now update the application to use the shared components and achieve the layout we are after. Start by updating the &lt;code&gt;apps/app2/src/app/app.module.ts&lt;/code&gt; to import &lt;code&gt;Lib1Module&lt;/code&gt; and &lt;code&gt;Lib2Module&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Next, update the &lt;code&gt;apps/app2/src/app/app.component.html&lt;/code&gt; file with the required markup and &lt;strong&gt;Tailwind CSS&lt;/strong&gt; utility classes to achieve our application’s layout and using the component exported by the shared libraries we previously created:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Like we did with &lt;code&gt;app1&lt;/code&gt;, we also need to update the &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;serve&lt;/code&gt; targets configuration for &lt;code&gt;app2&lt;/code&gt; to be able to consume the buildable library compiled output. We do so by updating the &lt;code&gt;app2&lt;/code&gt; configuration located in the &lt;code&gt;apps/app2/project.json&lt;/code&gt; file:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Last but not least, we need to configure &lt;strong&gt;Tailwind CSS&lt;/strong&gt; with our custom theme for &lt;code&gt;app2&lt;/code&gt;. We’ll do that by updating the &lt;code&gt;apps/app2/tailwind.config.js&lt;/code&gt; file with the following:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now that we have the second application configured, let’s run it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx run app2:serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You can pass a &lt;code&gt;--port=4201&lt;/code&gt; to the above command to run it in a different port if it’s in use or if you want to have both applications running side-by-side.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, open your browser and navigate to it where you should see the application looking like the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8128%2F1%2ALoOmmjSzfB33ho9NhcqQKQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8128%2F1%2ALoOmmjSzfB33ho9NhcqQKQ.png" alt="Application 2 screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That does indeed look different, but something is off. The card background color is not right, it’s still the same used for &lt;code&gt;app1&lt;/code&gt; even though we provided a different theme. Also, some of the spacing for the elements within the card doesn’t seem to have changed according to our configuration.&lt;/p&gt;

&lt;p&gt;What is going on here?&lt;/p&gt;

&lt;p&gt;You might have realized a couple of things by now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The card component comes from &lt;code&gt;lib2&lt;/code&gt; which is a buildable library and as such, it’s built on its own using its own &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app1&lt;/code&gt; and &lt;code&gt;lib2&lt;/code&gt; use a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; preset to share the common configuration, while &lt;code&gt;app2&lt;/code&gt; is adding its own&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, the first bullet point above would explain why the card component looks like the one rendered using the theme for &lt;code&gt;app1&lt;/code&gt;. But that’s not exactly what we are seeing, the buttons inside the card look different than what we have in &lt;code&gt;app1&lt;/code&gt;. This is explained by the fact that the buttons are styled without using any &lt;strong&gt;Tailwind CSS&lt;/strong&gt; directive or function, they just use utility classes, so the CSS for them is generated in the &lt;code&gt;app2&lt;/code&gt; build using the application configuration. The rest of the card does use directives and functions, so the CSS for that is generated in the &lt;code&gt;lib2&lt;/code&gt; build using the library configuration.&lt;/p&gt;

&lt;p&gt;Also, we previously created a &lt;strong&gt;Tailwind CSS&lt;/strong&gt; preset so we could share the base configuration among different projects. The problem is that all those projects shared a common theme, but &lt;code&gt;app2&lt;/code&gt; requires a different one, so we can’t just use the preset as it is right now.&lt;/p&gt;

&lt;p&gt;So, how do we solve this?&lt;/p&gt;

&lt;p&gt;Enter CSS variables!&lt;/p&gt;

&lt;p&gt;We can configure the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; preset to use CSS variables. This will allow each application to provide its own values for the variables and therefore, it enables us to have multiple themes using the same &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration.&lt;/p&gt;

&lt;p&gt;Let’s update our preset in the &lt;code&gt;libs/tailwind-preset/tailwind.config.js&lt;/code&gt; file to use CSS variables instead of literal values:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Next, we update the &lt;code&gt;apps/app2/tailwind.config.js&lt;/code&gt; file to remove the explicit theme configuration and add the preset instead:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Since our preset no longer has any literal values for the theme properties, we need to set the values for the CSS variables in the application. Edit the &lt;code&gt;apps/app2/src/styles.css&lt;/code&gt; file with the values for the theme variables:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We need to do the same for &lt;code&gt;app1&lt;/code&gt;. Edit the &lt;code&gt;apps/app1/src/styles.css&lt;/code&gt; file with the values for the theme variables:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let’s serve again &lt;code&gt;app2&lt;/code&gt; and navigate to it to check the results of our changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8128%2F1%2ATwcAfPTrKRm1D9ydEFwWWA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F8128%2F1%2ATwcAfPTrKRm1D9ydEFwWWA.png" alt="Application 2 screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are talking!&lt;/p&gt;

&lt;p&gt;This is what we wanted to see. Also &lt;code&gt;app1&lt;/code&gt; is still working as expected with its different theme. We are successfully styling two different applications with different themes while sharing some UI components and using the same &lt;strong&gt;Tailwind CSS&lt;/strong&gt; base configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting the button into a publishable library
&lt;/h2&gt;

&lt;p&gt;Now that both our applications are looking great, we want to share our awesome buttons with the community. So we are going to create a button component in a publishable library to be able to distribute it.&lt;/p&gt;

&lt;p&gt;First, we create the publishable library with &lt;strong&gt;Tailwind CSS&lt;/strong&gt; support:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:lib lib3 --publishable --importPath=@angular-tailwind-nx/lib3 --addTailwind
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then, we update the &lt;code&gt;libs/lib3/tailwind.config.js&lt;/code&gt; to use the shared preset:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Then, we create the button component:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx generate @nrwl/angular:component button --project=lib3 --export
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;We add the component to the library entry point located in &lt;code&gt;libs/lib3/src/index.ts&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Then, we update the button component files to provide the desired functionality:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Next, we need to update the card component in &lt;code&gt;lib2&lt;/code&gt; to use the button component. Import &lt;code&gt;Lib3Module&lt;/code&gt; into &lt;code&gt;Lib2Module&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And finally, we replace the existing markup for the button in the &lt;code&gt;libs/lib2/src/lib/card/card.component.html&lt;/code&gt; file with the new button component:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Once more, we can check both applications and make sure everything is still working and nothing was affected by the changes made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributing the publishable library styles
&lt;/h2&gt;

&lt;p&gt;The recently created publishable library is already being used successfully by both applications, but it’s still not ready for distribution. If we were to share it now, external consumers will need to provide their own CSS for it because the library itself is not bundling any CSS with the styling for the button. We only used some &lt;strong&gt;Tailwind CSS&lt;/strong&gt; utility classes and as we have seen throughout this blog post, the CSS for them is generated in files containing &lt;code&gt;@tailwind&lt;/code&gt; directives (normally in application style entry points).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Within the workspace, it works as expected because the applications consuming it use the same shared &lt;strong&gt;Tailwind CSS&lt;/strong&gt; preset and their dependencies are included in the &lt;code&gt;content&lt;/code&gt; of their configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The library needs to contain everything that’s needed for it to work and to achieve this, we are going to do something we already did with our buildable library: create our own classes using the &lt;code&gt;@apply&lt;/code&gt; directive.&lt;/p&gt;

&lt;p&gt;As we learned in a previous section, the &lt;code&gt;@apply&lt;/code&gt; directive will be transformed into the CSS corresponding to the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; classes being applied. Thanks to this, our button component will contain the CSS needed to style it.&lt;/p&gt;

&lt;p&gt;Go ahead and update the button component files with a new CSS class for the button:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I used the prefix &lt;code&gt;atn&lt;/code&gt; (initials of &lt;strong&gt;A&lt;/strong&gt;ngular, &lt;strong&gt;T&lt;/strong&gt;ailwind CSS, and &lt;strong&gt;N&lt;/strong&gt;x) for the CSS class name to prevent potential name collisions with the consumers' applications CSS.&lt;/p&gt;

&lt;p&gt;Also, let’s update the &lt;code&gt;libs/lib3/src/lib/button/button.component.ts&lt;/code&gt; file to set the component’s &lt;code&gt;encapsulation&lt;/code&gt; to &lt;code&gt;ViewEncapsulation.None&lt;/code&gt; to allow consumers to overwrite its styles more easily:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;If we build our library now, the styles for the button component will be correctly generated, but because we are using CSS variables for our theme, consumers would still need to provide their own values for them before they can use it.&lt;/p&gt;

&lt;p&gt;We need to provide an initial theme that sets those CSS variables so the library components can be consumed without any additional setup. Actually, we are going to generate a couple of theme options so we can see how multiple themes can be provided.&lt;/p&gt;

&lt;p&gt;Let’s start by creating a &lt;code&gt;libs/lib3/src/styles/teal.css&lt;/code&gt; theme file where we are going to import the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; &lt;code&gt;components&lt;/code&gt; and &lt;code&gt;utilities&lt;/code&gt; layers and define the values for the CSS variables of our theme:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Notice that we didn’t include the &lt;code&gt;base&lt;/code&gt; layer like we have done so far in the applications style entry points. This is because this is a component library and the &lt;code&gt;base&lt;/code&gt; layer generates a set of application-wide base styles and that’s not what we want to generate here.&lt;/p&gt;

&lt;p&gt;Next, we generate our second theme by creating the &lt;code&gt;libs/lib3/src/styles/indigo.css&lt;/code&gt; theme file with different values for the CSS variables:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;With that in place, we now need to make sure those theme files are processed when we build the library. The &lt;code&gt;@nrwl/angular:package&lt;/code&gt; executor is powered by the &lt;a href="https://github.com/ng-packagr/ng-packagr" rel="noopener noreferrer"&gt;ng-packagr&lt;/a&gt; package to build the library. This is a tool recommended by &lt;strong&gt;Angular&lt;/strong&gt; to ensure libraries are distributed using the &lt;a href="https://angular.io/guide/angular-package-format" rel="noopener noreferrer"&gt;Angular Package Format&lt;/a&gt;. Unfortunately, it doesn’t have native support for building standalone stylesheets that are not referenced by a component, so we need to configure it ourselves.&lt;/p&gt;

&lt;p&gt;To do so, we are going to use the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; CLI to process our stylesheets when the library is built and we’ll do it in parallel since they don’t depend on each other. One aspect to consider is that the &lt;code&gt;@nrwl/angular:package&lt;/code&gt; executor will delete the destination folder before building. When running both processes in parallel, the styles might be generated first and then the directory containing them is deleted by the &lt;code&gt;@nrwl/angular:package&lt;/code&gt; executor. Therefore, we are going to disable that behavior and we are going to control when to delete the destination folder to avoid any issues.&lt;/p&gt;

&lt;p&gt;Another thing to consider is that the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; CLI only supports processing one file at a time, it doesn’t accept glob patterns or directories. We’ll need to run a command per theme in our library.&lt;/p&gt;

&lt;p&gt;To orchestrate this, we are going to make the following changes to the &lt;code&gt;lib3&lt;/code&gt; project configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rename the existing &lt;code&gt;build&lt;/code&gt; target to &lt;code&gt;build-angular&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;build-themes&lt;/code&gt; target that runs, in parallel, the &lt;strong&gt;Tailwind CSS&lt;/strong&gt; CLI for every theme in our library&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;build-lib&lt;/code&gt; target that runs, in parallel, the &lt;code&gt;build-angular&lt;/code&gt; and &lt;code&gt;build-themes&lt;/code&gt; targets&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;build&lt;/code&gt; target that first, deletes the destination folder, and then runs the &lt;code&gt;build-lib&lt;/code&gt; target&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Edit the project configuration for the &lt;code&gt;lib3&lt;/code&gt; project located in the &lt;code&gt;libs/lib3/project.json&lt;/code&gt; file with the changes described above and shown below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The only thing remaining is to update the &lt;code&gt;libs/lib3/ng-package.json&lt;/code&gt; to prevent the &lt;strong&gt;Angular&lt;/strong&gt; build to delete the destination folder. We do that by setting the &lt;code&gt;deleteDestPath&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We can now build the library by running:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx nx run lib3:build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we check the output folder &lt;code&gt;dist/libs/lib3&lt;/code&gt;, we’ll see there’s a &lt;code&gt;themes&lt;/code&gt; folder in it with a couple of files &lt;code&gt;indigo.css&lt;/code&gt; and &lt;code&gt;teal.css&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AvcHqTWEppPbVtGRlVIqEPg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AvcHqTWEppPbVtGRlVIqEPg.png" alt="Publishable library with the produced theme files highlighted"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These theme files can now be used by the consumers of our library to properly style the components exposed by it. All they would need to do is to import one of those themes into their application styles entry point or &lt;code&gt;index.html&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;They can also customize the included themes by overwriting any of the CSS variables of the theme or the specific styles of the &lt;code&gt;atn-button&lt;/code&gt; CSS class. &lt;/p&gt;

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

&lt;p&gt;We covered a lot in this article and hopefully, it gave a good walkthrough over the different scenarios we might find ourselves when using &lt;strong&gt;Angular&lt;/strong&gt; and &lt;strong&gt;Tailwind CSS&lt;/strong&gt; in an &lt;strong&gt;Nx&lt;/strong&gt; workspace.&lt;/p&gt;

&lt;p&gt;Doing a quick recap, we learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to add support for &lt;strong&gt;Tailwind CSS&lt;/strong&gt; in existing &lt;strong&gt;Angular&lt;/strong&gt; projects using an &lt;strong&gt;Nx&lt;/strong&gt; generator&lt;/li&gt;
&lt;li&gt;How to create &lt;strong&gt;Angular&lt;/strong&gt; projects with &lt;strong&gt;Tailwind CSS&lt;/strong&gt; already configured using an &lt;strong&gt;Nx&lt;/strong&gt; generator&lt;/li&gt;
&lt;li&gt;How to share &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration among an application and its dependencies using presets&lt;/li&gt;
&lt;li&gt;How to share &lt;strong&gt;Tailwind CSS&lt;/strong&gt; configuration among multiple applications and their dependencies while still being able to have different styles&lt;/li&gt;
&lt;li&gt;How to create and distribute multiple themes in an &lt;strong&gt;Angular&lt;/strong&gt; publishable library using &lt;strong&gt;Tailwind CSS&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tailwindcss</category>
      <category>angular</category>
      <category>nx</category>
      <category>monorepo</category>
    </item>
    <item>
      <title>Creating my personal website with Astro, Tailwind CSS, and Nx</title>
      <dc:creator>Leosvel Pérez Espinosa</dc:creator>
      <pubDate>Tue, 25 Jan 2022 12:26:51 +0000</pubDate>
      <link>https://forem.com/leosvelperez/creating-my-personal-website-with-astro-tailwind-css-and-nx-149a</link>
      <guid>https://forem.com/leosvelperez/creating-my-personal-website-with-astro-tailwind-css-and-nx-149a</guid>
      <description>&lt;p&gt;It is certainly something I have been thinking about doing for quite some time, but I never really went for it until now. Several reasons have deterred me in the past from creating a personal website, and while some of them might still be valid, I decided to give it a go and create something it could push me to try to create more content and a place where I can experiment with different technology stacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can take a look at the source code in the website's &lt;a href="https://github.com/leosvelperez/leosvel.dev"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology stack
&lt;/h2&gt;

&lt;p&gt;I have been working with &lt;a href="https://angular.io"&gt;Angular&lt;/a&gt; for several years and it would have been the most comfortable choice, but I wanted to try something new and different; after all, that was one of the main reasons I decided to create my personal website.&lt;/p&gt;

&lt;p&gt;A few months ago, I came across &lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;, a modern static site builder that promises to deliver great performance by shipping &lt;em&gt;zero&lt;/em&gt; JavaScript by default. With other interesting features like the ability to use other frameworks, on-demand partial hydration, and Markdown support, it immediately caught my attention and became my first choice.&lt;/p&gt;

&lt;p&gt;I also wanted to use &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt;. I've previously played with it and I really like its flexibility and how easy and fast you can prototype and style your site. I'm not a designer and I'm not a creative person, so I particularly like the ability to quickly try things out and see how they look to find out what I like the most.&lt;/p&gt;

&lt;p&gt;For the hosting, I decided to go with &lt;a href="https://pages.cloudflare.com"&gt;Cloudflare Pages&lt;/a&gt;. It has the features I was looking for and more: automatic deployments from GitHub, preview PRs, ease of use, etc. There are several other great choices out there (&lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;, &lt;a href="https://vercel.com"&gt;Vercel&lt;/a&gt;, and &lt;a href="https://pages.github.com"&gt;GitHub Pages&lt;/a&gt; to name a few) that I'll most likely be trying out in the future.&lt;/p&gt;

&lt;p&gt;Last but not least, I chose to use &lt;a href="https://nx.dev"&gt;Nx&lt;/a&gt; to benefit from its generation features, smart build system, and the many different features it provides. In reality, "chose" is not the right word here. I was always going to use Nx from the start. I can't see myself not using it for any project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Though it was not necessary, I took the opportunity to create an Nx plugin called &lt;a href="https://www.npmjs.com/package/@nxtensions/astro"&gt;@nxtensions/astro&lt;/a&gt; that adds first-class support for Astro (maybe a story for a different blog post).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To summarize, this is the stack I ended up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://astro.build"&gt;Astro&lt;/a&gt;: a modern static site builder.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt;: a utility-first CSS framework.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pages.cloudflare.com/"&gt;Cloudflare Pages&lt;/a&gt;: a JAMstack platform for frontend developers to collaborate and deploy websites.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nx.dev"&gt;Nx&lt;/a&gt;: a next generation build system with first class monorepo support and powerful integrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Astro basic features
&lt;/h2&gt;

&lt;p&gt;Before diving into creating the required pages and components, let's have a quick overview of some of the basic Astro features I used to build the website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Components
&lt;/h3&gt;

&lt;p&gt;Astro comes with its own &lt;a href="https://docs.astro.build/core-concepts/astro-components/"&gt;component syntax&lt;/a&gt;. Any file with the &lt;code&gt;.astro&lt;/code&gt; extension represents a single Astro component and it follows the &lt;strong&gt;Single-File Component (SFC)&lt;/strong&gt; pattern by containing the HTML, CSS, and JavaScript needed to render the component in the same file.&lt;/p&gt;

&lt;p&gt;The Astro component syntax is very similar to HTML and JSX. In fact, it's a superset of HTML and every component must include an HTML template.&lt;/p&gt;

&lt;p&gt;Astro also has the concept of a &lt;a href="https://docs.astro.build/core-concepts/astro-components/#frontmatter-script"&gt;Frontmatter component script&lt;/a&gt; to build dynamic components. The component script natively supports JavaScript and Typescript and it will only be processed at build time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pages
&lt;/h3&gt;

&lt;p&gt;An &lt;a href="https://docs.astro.build/core-concepts/astro-pages/"&gt;Astro page&lt;/a&gt; is just a special type of component with additional responsibilities. While a component can return partial HTML templates, pages must return a full HTML document. Astro supports the &lt;code&gt;.astro&lt;/code&gt; and &lt;code&gt;.md&lt;/code&gt; files for pages, and they should be placed in the &lt;code&gt;src/pages&lt;/code&gt; directory (or the directory specified in the &lt;a href="https://docs.astro.build/reference/configuration-reference/#pages"&gt;configuration &lt;code&gt;pages&lt;/code&gt; option&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;Astro uses an approach called &lt;strong&gt;file-based routing&lt;/strong&gt; to generate the application URLs at build time based on the &lt;code&gt;pages&lt;/code&gt; directory structure. It supports static routes as well as dynamic routes. You can check more about this in the &lt;a href="https://docs.astro.build/core-concepts/routing/"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an Nx workspace with an Astro application
&lt;/h2&gt;

&lt;p&gt;Having a clearer grasp of Astro's features, I started by creating a new empty Nx workspace by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-nx-workspace@latest leosvel-dev &lt;span class="nt"&gt;--preset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;empty &lt;span class="nt"&gt;--pm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I specified the &lt;code&gt;--pm=yarn&lt;/code&gt; option to use &lt;a href="https://yarnpkg.com/"&gt;Yarn&lt;/a&gt; as the package manager but this is not a requirement. You can use other package managers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the workspace was generated, I navigated to it and installed the &lt;code&gt;@nxtensions/astro&lt;/code&gt; plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;leosvel-dev &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; @nxtensions/astro@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I proceeded to generate the Astro application by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx g @nxtensions/astro:app website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't choose to use any renderer (to support other frameworks) because I just wanted to use &lt;a href="https://docs.astro.build/core-concepts/astro-components/"&gt;Astro components&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: If you use &lt;a href="https://code.visualstudio.com/"&gt;VSCode&lt;/a&gt;, you can use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console"&gt;Nx Console&lt;/a&gt; extension that provides a UI for the Nx CLI. With it, you can use a GUI to run any Nx command. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, I had a new Nx workspace with an Astro application properly configured and I was already able to start the Astro development server by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx dev website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visting &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt; in my browser displayed the landing page that was automatically generated when I created the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning up application files
&lt;/h2&gt;

&lt;p&gt;The generated application comes with a default landing page with some content to help get started. Before moving forward, I deleted the content of the &lt;code&gt;apps/website/src/pages/index.astro&lt;/code&gt; file and deleted the &lt;code&gt;apps/website/src/components/Tour.astro&lt;/code&gt; and &lt;code&gt;apps/website/public/styles/home.css&lt;/code&gt; files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;To configure Tailwind CSS, I started by installing the required packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss@latest postcss@latest autoprefixer@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I added the configuration for it in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/website/tailwind.config.cjs&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./public/**/*.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.{astro,md,js,jsx,svelte,ts,tsx,vue}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../libs/**/*.{astro,md,js,jsx,svelte,ts,tsx,vue}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/website/postcss.config.cjs&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I proceeded to add the Tailwind CSS base styles to the existing &lt;code&gt;apps/website/public/styles/global.css&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the files in the &lt;code&gt;public&lt;/code&gt; directory are never processed by the Astro build process, I later moved the &lt;code&gt;apps/website/public/styles/global.css&lt;/code&gt; file out of the &lt;code&gt;public&lt;/code&gt; directory, so it gets processed by the PostCSS plugin for Tailwind CSS. In the coming sections, I'll cover where I placed it and how this file is referenced on the website's pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the common layout
&lt;/h2&gt;

&lt;p&gt;The website currently has 3 types of pages: the landing page, the blog page, and the blog post page. All of them share a common layout consisting of a header, the main content, and a footer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There's also a 404 page, but I'm not going to cover it here since it's a very simple page. It's a page that will appear when the user tries to access a page that doesn't exist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Astro has a concept of &lt;a href="https://docs.astro.build/core-concepts/layouts/"&gt;Layouts&lt;/a&gt;. They are basically components with the specific purpose of providing a reusable page structure to reduce duplicating the same code on multiple pages.&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;apps/website/src/layouts/BaseLayout.astro&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import { Footer, Head, Header } from '@leosvel/common';

export interface Props {
  title: "string;"
  description: "string;"
  socialImage?: string;
  socialImageAlt?: string;
}

const { title: "pageTitle, description, socialImage, socialImageAlt } = Astro.props;"
const { canonicalURL } = Astro.request;
const siteName = canonicalURL.hostname;
const title = `${pageTitle} | ${siteName}`;
---

&amp;lt;html lang="en" class="scroll-smooth"&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;Head {title} {description} {canonicalURL} {siteName} {socialImage} {socialImageAlt} /&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body class="min-h-screen w-screen bg-white flex flex-col font-mono text-white selection:bg-cyan-700 selection:text-white overflow-x-hidden"&amp;gt;
    &amp;lt;Header currentPage={Astro.request.url.pathname} /&amp;gt;

    &amp;lt;main class="flex flex-1"&amp;gt;
      &amp;lt;slot /&amp;gt;
    &amp;lt;/main&amp;gt;

    &amp;lt;Footer /&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright! There's a lot going on there. Let's break it down to see what's going on.&lt;/p&gt;

&lt;p&gt;The section at the top delimited by the &lt;code&gt;---&lt;/code&gt; lines is the Frontmatter script for the component. That's the place where we can import other components and write JavaScript/Typescript code that is going to be executed at build time. In this particular layout, I'm importing some components that we are going to be using, exporting the &lt;code&gt;Props&lt;/code&gt; interface to define what props are expected, and finally, I'm getting a reference to those props from the &lt;code&gt;Astro&lt;/code&gt; global object and some other values I need from the &lt;a href="https://docs.astro.build/en/reference/api-reference/#astrorequest"&gt;&lt;code&gt;Astro.request&lt;/code&gt;&lt;/a&gt; object.&lt;/p&gt;

&lt;p&gt;Outside of that section, we can write our HTML markup as well as include &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags. For this particular case, I'm defining the HTML content with the desired structure for the website's pages and some basic styling using Tailwind CSS. This is where I'm making use of the imported components and I'm passing some props for them as required.&lt;/p&gt;

&lt;p&gt;One important thing to note is the &lt;a href="https://docs.astro.build/core-concepts/astro-components/#slots"&gt;&lt;code&gt;slot&lt;/code&gt; tag&lt;/a&gt;. This element allows us to render children elements passed inside the layout when consuming it.&lt;/p&gt;

&lt;p&gt;As you can see in the code, I'm importing several components from &lt;code&gt;@leosvel/common&lt;/code&gt;. This is a library I created in the workspace where I placed some common components used by the different website's pages. I created the library by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx g @nxtensions/astro:lib common
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this library, I placed the &lt;code&gt;global.css&lt;/code&gt; file mentioned in the previous section that contains the Tailwind CSS base styles. Also, among the components created in that library, we can find the &lt;code&gt;Head&lt;/code&gt; component, which contains metadata, scripts, and styles for the pages. This is the component that includes the &lt;code&gt;global.css&lt;/code&gt; file so it's available for every page.&lt;/p&gt;

&lt;p&gt;The following is the specific portion of code in the &lt;code&gt;libs/common/src/lib/components/Head.astro&lt;/code&gt; file that includes the global styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
&amp;lt;!-- Global styles --&amp;gt;
&amp;lt;style global&amp;gt;
  @import '../styles/global.css';
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the website's pages
&lt;/h2&gt;

&lt;p&gt;Now that I had the base layout ready, it was time to use it to create some pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Landing page
&lt;/h3&gt;

&lt;p&gt;When it comes to pages, I like to keep them clean and simple and extract their presentation content into components. This is also in line with the Nx philosophy of keeping our apps light and extracting the functionality into libraries.&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;landing&lt;/code&gt; library where I placed a component with the UI of the landing page. This page is quite simple right now and as it stands, it might seem too much to have a library for a single component, but creating a library is cheap and I plan to have more things in it in the near future.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This website in general is pretty simple, so it could have been done without any library at all. However, I like splitting my projects into smaller and more focused units (libraries) and I wanted to make the most of using Astro with Nx.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following is the source code for the landing page located in &lt;code&gt;apps/website/src/pages/index.astro&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import Layout from '../layouts/BaseLayout.astro';
import { Landing } from '@leosvel/landing';

const title = 'Home';
const description = 'My personal website with my projects and blog.';
---

&amp;lt;Layout {title} {description}&amp;gt;
  &amp;lt;Landing /&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can notice above how I made use of the layout I created in the previous section and how I'm passing the &lt;code&gt;Landing&lt;/code&gt; component as a child to it. This causes it to be rendered in the &lt;code&gt;slot&lt;/code&gt; tag we previously added to the layout which is placed between the &lt;code&gt;Header&lt;/code&gt; and &lt;code&gt;Footer&lt;/code&gt; components. The &lt;code&gt;Landing&lt;/code&gt; component doesn't have anything worthy of showing, it only contains the needed HTML markup and Tailwind CSS classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blog page
&lt;/h3&gt;

&lt;p&gt;The next page to look at is the blog page located in &lt;code&gt;apps/website/src/pages/blog/index.astro&lt;/code&gt;. Following Astro's file-based routing approach, this page will be available at the &lt;code&gt;/blog&lt;/code&gt; URL.&lt;/p&gt;

&lt;p&gt;The blog page displays a list of blog posts. These blog posts are written in Markdown files and placed in the &lt;code&gt;apps/website/src/data/blog-posts&lt;/code&gt; directory. So, I needed to get the list of blog posts and display them.&lt;/p&gt;

&lt;p&gt;Let's have a look at the &lt;code&gt;apps/website/src/pages/blog/index.astro&lt;/code&gt; file to see how I did it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import Layout from '../../layouts/BaseLayout.astro';
import { Blog } from '@leosvel/blog';

const title = 'Blog';
const description = 'My blog with articles about web development and programming in general.';

const posts = Astro.fetchContent('../../data/blog-posts/*.md').sort(
  (a, b) =&amp;gt; new Date(b.date).valueOf() - new Date(a.date).valueOf()
);
---

&amp;lt;Layout {title} {description} socialImage="/assets/blog-leosvel.dev.png" socialImageAlt="Leosvel's blog social image"&amp;gt;
  &amp;lt;Blog {description} {posts} /&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the landing page, it's very simple and it delegates the presentation concerns to the &lt;code&gt;Blog&lt;/code&gt; component (located in the &lt;code&gt;blog&lt;/code&gt; library) while providing a title and the list of posts. The interesting bit is the loading of the Markdown files with the blog posts. To do that, I used the &lt;a href="https://docs.astro.build/reference/api-reference/#astrofetchcontent"&gt;&lt;code&gt;Astro.fetchContent()&lt;/code&gt; helper function&lt;/a&gt; passing a glob to those files. This function returns an array of objects containing, among other things, the Frontmatter properties specified in the Markdown files. I used the &lt;code&gt;date&lt;/code&gt; property to sort the posts by date in descending order.&lt;/p&gt;

&lt;p&gt;The following is the Frontmatter script section for this blog post Markdown file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// apps/website/src/data/blog-posts/creating-my-personal-website-with-astro-tailwindcss-and-nx.md
---
title: 'Creating my personal website with Astro, Tailwind CSS, and Nx'
description: 'How I went about creating my personal website using Astro, Tailwind CSS, and Nx.'
date: 'January 25, 2022'
heroImage: '/assets/blog/creating-my-personal-website-with-astro-tailwindcss-and-nx/hero.png'
heroImageAlt: 'Astro, Tailwind CSS, and Nx logos'
thumbnailImage: '/assets/blog/creating-my-personal-website-with-astro-tailwindcss-and-nx/thumbnail.png'
thumbnailImageAlt: 'Astro, Tailwind CSS, and Nx logos'
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the &lt;code&gt;date&lt;/code&gt; property that is used on the blog page to sort the blog posts.&lt;/p&gt;

&lt;p&gt;Let's also have a look at the source code portion of the &lt;code&gt;Blog&lt;/code&gt; component where I use the received &lt;code&gt;posts&lt;/code&gt; to display a list with a preview for each of them (the rest of the file is omitted for brevity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// libs/blog/src/lib/components/Blog.astro
...
&amp;lt;section class="grid justify-center sm:grid-cols-2 lg:grid-cols-3 gap-8"&amp;gt;
  {posts.map((post) =&amp;gt; {
    const link = `/blog/${post.file.pathname.split('/').pop().split('.').shift()}`;

    return (
      &amp;lt;BlogPostPreview
        post={{
          title: post.title,
          description: post.description,
          date: post.date,
          link,
          thumbnailImage: post.thumbnailImage,
          thumbnailImageAlt: post.thumbnailImageAlt,
        }}
      /&amp;gt;
    );
  })}
&amp;lt;/section&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've used JSX before, this probably looks very familiar. I'm basically iterating over the &lt;code&gt;posts&lt;/code&gt; array and creating a &lt;code&gt;BlogPostPreview&lt;/code&gt; component for each blog post. I'm also building the link to it using the blog post Markdown file path as the URL segment. The &lt;code&gt;BlogPostPreview&lt;/code&gt; component is a simple component that only contains the needed HTML markup and Tailwind CSS classes to display a preview of the blog post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blog post page
&lt;/h3&gt;

&lt;p&gt;The blog post page renders the blog post content. This is a dynamic route from which many URLs will be generated (one per available blog post). In order for Astro to know what pages to generate at build time, we must provide a &lt;a href="https://docs.astro.build/reference/api-reference/#getstaticpaths"&gt;&lt;code&gt;getStaticPaths()&lt;/code&gt; function&lt;/a&gt;. This function must return an array of objects containing the &lt;code&gt;params&lt;/code&gt; property with any parameters that the route uses.&lt;/p&gt;

&lt;p&gt;This particular page is located in &lt;code&gt;apps/website/src/pages/blog/[slug].astro&lt;/code&gt; and will be available at the &lt;code&gt;/blog/[slug]&lt;/code&gt; URL. Therefore, we need to return a &lt;code&gt;slug&lt;/code&gt; parameter with a value matching what we want to be the URL segment for our blog posts. As shown in the previous section, I chose to use the blog post Markdown file path as the URL segment.&lt;/p&gt;

&lt;p&gt;We can see it in action in the source code of the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
import Layout from '../../layouts/BlogPostLayout.astro';
import { BlogPost } from '@leosvel/blog';

export function getStaticPaths() {
  const posts = Astro.fetchContent('../../data/blog-posts/*.md');

  return posts.map((post) =&amp;gt; ({
    params: { slug: post.file.pathname.split('/').pop().split('.').shift() },
    props: { post },
  }));
}

const { Content, title, description, date, heroImage, heroImageAlt, thumbnailImage, thumbnailImageAlt } = Astro.props.post;
---

&amp;lt;Layout {title} {description} socialImage={thumbnailImage} socialImageAlt={thumbnailImageAlt}&amp;gt;
  &amp;lt;BlogPost {title} {date} {heroImage} {heroImageAlt}&amp;gt;
    &amp;lt;Content /&amp;gt;
  &amp;lt;/BlogPost&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see above, I'm also using the &lt;code&gt;Content&lt;/code&gt; property that is returned from the compiler when fetching Markdown files. It is a dynamically built component that contains the content of the Markdown file (the blog post in this case).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might have noticed this page is using a different layout. It's a clone of the base layout with an extra stylesheet for the Prisma theme used by the code blocks in the blog posts. Due to &lt;a href="https://github.com/withastro/astro/issues/2128"&gt;an existing issue&lt;/a&gt; on Astro, it's currently not possible to compose it using slots.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;BlogPost&lt;/code&gt; component renders and styles the blog post. Since I don't have direct access to the generated markup for the blog post, I'm using global styles scoped to the &lt;code&gt;.blog-content&lt;/code&gt; CSS class to ensure they are only applied to the blog post content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// libs/blog/src/lib/BlogPost.astro
...
&amp;lt;article class="max-w-full sm:max-w-xl md:max-w-2xl lg:max-w-4xl mx-auto px-4 py-12 sm:px-8 md:px-12 text-sm sm:text-base text-cyan-900 leading-8 sm:leading-8 transition-all"&amp;gt;
  ...
  &amp;lt;main class="blog-content"&amp;gt;
    &amp;lt;slot /&amp;gt;
  &amp;lt;/main&amp;gt;
  ...
&amp;lt;/article&amp;gt;

&amp;lt;style lang="scss" global&amp;gt;
  .blog-content {
    &amp;gt; * + * {
      @apply mt-4;
    }

    h2 {
      @apply mt-12 text-xl sm:text-2xl font-bold;
    }
    ...
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Improving the blog post page
&lt;/h2&gt;

&lt;p&gt;With the blog post page in place, I wanted to make some improvements to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a link to the headings when hovering them.&lt;/li&gt;
&lt;li&gt;Make external links to open in new tabs and add an icon to them to indicate that they are external.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, I can't directly modify the HTML markup of the blog post page. It's generated by the Astro compiler when parsing the Markdown files. Fortunately, the &lt;a href="https://docs.astro.build/en/guides/markdown-content/"&gt;Astro's out-of-the-box Markdown support&lt;/a&gt; is very powerful and extensible. It allows you to extend the default functionality by providing &lt;a href="https://remark.js.org/"&gt;Remark&lt;/a&gt; and/or &lt;a href="https://github.com/rehypejs/rehype"&gt;Rehype&lt;/a&gt; plugins.&lt;/p&gt;

&lt;p&gt;So to achieve my goals, I configured the &lt;a href="https://github.com/rehypejs/rehype-slug"&gt;rehype-slug&lt;/a&gt; and &lt;a href="https://github.com/rehypejs/rehype-autolink-headings"&gt;rehype-autolink-headings&lt;/a&gt; plugins to generate links to the headings in the blog post. I also configured the &lt;a href="https://github.com/rehypejs/rehype-external-links"&gt;rehype-external-links&lt;/a&gt; plugin to add the &lt;code&gt;target="_blank"&lt;/code&gt; and &lt;code&gt;rel="nofollow noopener noreferrer"&lt;/code&gt; attributes to external links, as well as adding an icon to them.&lt;/p&gt;

&lt;p&gt;Below is the configuration in the &lt;code&gt;apps/website/astro.config.mjs&lt;/code&gt; file to enable those plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="cm"&gt;/** @type {import('astro').AstroUserConfig} */&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;markdownOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@astrojs/markdown-remark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;rehypePlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rehype-slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rehype-autolink-headings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prepend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading-link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/link.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
                  &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rehype-external-links&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/external-link.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;External link icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;contentProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external-link-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final workspace structure
&lt;/h2&gt;

&lt;p&gt;One of the benefits of using Nx is that you can easily visualize your workspace projects and their dependencies. By running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx dep-graph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I got the following visualization of my website's projects:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MqY3Zsf2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybqhhoi8rxppup4lyes6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MqY3Zsf2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybqhhoi8rxppup4lyes6.png" alt="Website workspace structure" width="489" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Cloudflare
&lt;/h2&gt;

&lt;p&gt;Setting up automatic deployments to Cloudflare Pages from the GitHub repository was really easy. To do so, I did the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessed the &lt;code&gt;Pages&lt;/code&gt; page in my Cloudflare dashboard&lt;/li&gt;
&lt;li&gt;Clicked on the &lt;code&gt;Create a project&lt;/code&gt; button&lt;/li&gt;
&lt;li&gt;Added my GitHub account, selected the repository to deploy, and clicked on the &lt;code&gt;Begin setup&lt;/code&gt; button:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QoM5uaie--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ktvziezj0f4ab5lnmn32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QoM5uaie--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ktvziezj0f4ab5lnmn32.png" alt="Screenshot with Github account and repository selected" width="831" height="802"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updated the build settings with:

&lt;ul&gt;
&lt;li&gt;Project name: &lt;code&gt;leosvel-dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Production branch: &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Framework preset: &lt;code&gt;None&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Build command: &lt;code&gt;nx build website&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Build output directory: &lt;code&gt;dist/apps/website&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added the &lt;code&gt;NODE_VERSION&lt;/code&gt; environment variable and set it to &lt;code&gt;16.13.2&lt;/code&gt; so the build command runs with it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QieZCst6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/39g3bwrnojrt71g3idaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QieZCst6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/39g3bwrnojrt71g3idaf.png" alt="Screenshot with build settings" width="824" height="1404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicked on the &lt;code&gt;Save and deploy&lt;/code&gt; button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The build was immediately kicked off and the website was deployed to Cloudflare Pages in just a couple of minutes. By default, it was available on a subdomain of the &lt;code&gt;pages.dev&lt;/code&gt; domain. To have it with my own domain, I had to set it up and I did it by following the steps below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the &lt;code&gt;leosvel-dev&lt;/code&gt; project page, I clicked on the &lt;code&gt;Custom domains&lt;/code&gt; tab&lt;/li&gt;
&lt;li&gt;Clicked on the &lt;code&gt;Set up a custom domain&lt;/code&gt; button&lt;/li&gt;
&lt;li&gt;Entered my domain name and clicked on the &lt;code&gt;Continue&lt;/code&gt; button&lt;/li&gt;
&lt;li&gt;I was shown a confirmation page with the new DNS record for my domain and I clicked on the &lt;code&gt;Activate domain&lt;/code&gt; button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was it! The website was live and available on my domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Cloudflare Web Analytics
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developers.cloudflare.com/analytics/web-analytics"&gt;Cloudflare Web Analytics&lt;/a&gt; provides, in their own words, free, privacy-first analytics for your website. It allows you to track how your website is being used and how it is performing.&lt;/p&gt;

&lt;p&gt;To enable it, I just had to add my website to it. This is done on the &lt;code&gt;Web Analytics&lt;/code&gt; page of the Cloudflare dashboard. By default, Cloudflare injects the analytics script in the website pages, but since I wanted to use &lt;a href="https://github.com/BuilderIO/partytown"&gt;Partytown&lt;/a&gt; to move third-party scripts execution off of the main thread, I disabled the automatic setup.&lt;/p&gt;

&lt;p&gt;I installed &lt;code&gt;@builder.io/partytown&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; @builder.io/partytown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I added it to the &lt;code&gt;Head&lt;/code&gt; component alongside the Cloudflare Web Analytics script with its &lt;code&gt;type&lt;/code&gt; set to &lt;code&gt;text/partytown&lt;/code&gt;. That &lt;code&gt;type&lt;/code&gt; attribute with that value prevents browsers from executing the script and it provides a selector for Partytown to query for and &lt;a href="https://github.com/BuilderIO/partytown/wiki/How-Does-It-Work%3F"&gt;do its magic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below is the code snippet for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// libs/common/src/lib/components/Head.astro
---
import { Partytown } from '@builder.io/partytown/react';
...
---
...
&amp;lt;!-- Partytown --&amp;gt;
&amp;lt;Partytown /&amp;gt;

&amp;lt;!-- Cloudflare Web Analytics --&amp;gt;
&amp;lt;script type="text/partytown" defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "30e7f4a2b20548998ad672795c296f1c"}'&amp;gt;&amp;lt;/script&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One extra thing I needed to set up was to &lt;a href="https://github.com/BuilderIO/partytown/wiki/Getting-Started#user-content-copy-partytown-library-files"&gt;copy the required Partytown library files&lt;/a&gt; from the &lt;code&gt;@builder.io/partytown&lt;/code&gt; package to the built application. This is necessary because those files are required to be served for Partytown to work correctly.&lt;/p&gt;

&lt;p&gt;I made this change by renaming the existing &lt;code&gt;build&lt;/code&gt; target in the &lt;code&gt;website&lt;/code&gt; project configuration (&lt;code&gt;apps/website/project.json&lt;/code&gt;) to &lt;code&gt;build-astro&lt;/code&gt; and create a new target called &lt;code&gt;build&lt;/code&gt; where I run the &lt;code&gt;build-astro&lt;/code&gt; target and a small script I created to copy the relevant files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"targets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build-astro"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"outputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dist/apps/website"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"executor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@nxtensions/astro:build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"executor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@nrwl/workspace:run-commands"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"outputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dist/apps/website"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"commands"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"nx run website:build-astro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"node ./tools/scripts/copy-partytown-files-to-dist.js"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"parallel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;With everything in place and the website up and running, it was time to get some performance insights. Running Lighthouse on the live website for mobile gave me the following results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kq8yGR7K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i1j3p7bo2s0zg47u6qut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kq8yGR7K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i1j3p7bo2s0zg47u6qut.png" alt="Lighthouse score of 100" width="880" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, that's a good-looking report! Isn't it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Building my website was a really fun experience. I enjoyed the process and the fact that I took the chance to experiment with new technologies.&lt;/p&gt;

&lt;p&gt;I'm really glad I chose Astro to build the website. Even though it still hasn't reached a stable release, it already works reasonably well and delivers on its promise of achieving great performance. The fact that it's not stable yet and still works this well excites me the more, it's only going to get better!&lt;/p&gt;

&lt;p&gt;I achieved the goals I initially had in mind, creating a personal space where I can experiment with new technologies and blog about them and other topics.&lt;/p&gt;

&lt;p&gt;It's this the end of the journey? Hell no!&lt;/p&gt;

&lt;p&gt;This is just the beginning. I plan to add a couple more pages to the website, continue to improve its design, add more features to the blog, refactor some bits to clean up the code, and from time to time, I might rewrite or build multiple versions of it with different stacks (I'm looking at you &lt;a href="https://remix.run/"&gt;Remix&lt;/a&gt; and &lt;a href="https://github.com/BuilderIO/qwik"&gt;Qwik&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Stay tuned! Happy coding!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>tailwindcss</category>
      <category>nx</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
