<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Siddharth Venkatesh</title>
    <description>The latest articles on DEV Community by Siddharth Venkatesh (@siddharthvenkatesh).</description>
    <link>https://dev.to/siddharthvenkatesh</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%2F228196%2F844ec4de-2664-484c-846f-0ee2f7ae393b.jpeg</url>
      <title>DEV Community: Siddharth Venkatesh</title>
      <link>https://dev.to/siddharthvenkatesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siddharthvenkatesh"/>
    <language>en</language>
    <item>
      <title>Building a realtime performance monitoring system with Kafka and Go</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Mon, 20 Feb 2023 15:04:12 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/building-a-realtime-performance-monitoring-system-with-kafka-and-go-h64</link>
      <guid>https://dev.to/siddharthvenkatesh/building-a-realtime-performance-monitoring-system-with-kafka-and-go-h64</guid>
      <description>&lt;p&gt;Recently, I had a chance to try out Apache's &lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;Kafka&lt;/a&gt; for a monitoring service and I was pleasantly surprised how you could set up a full fledged event streaming system in a few lines of code. I quickly realised we could be building powerful systems with Kafka at the centre of things. Notification systems, distributed database synchronisations, monitoring systems are some of the applications that come to mind when thinking of Kafka's use-cases.&lt;br&gt;
In my quest to understand Kafka a bit more deeply, I tried setting up a system monitoring application which looks for system stats like CPU and RAM usage and visualises them in a dashboard.&lt;/p&gt;

&lt;p&gt;The tools I went with for this are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kafka as a message queue&lt;/li&gt;
&lt;li&gt;Golang for a couple of microservices&lt;/li&gt;
&lt;li&gt;React for the dashboard&lt;/li&gt;
&lt;li&gt;Docker and docker compose for orchestration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The architecture I'm going for is this.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bav1q82w9lio5veh9ac.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bav1q82w9lio5veh9ac.png" alt="architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Services
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;System stats&lt;/td&gt;
&lt;td&gt;Golang service which captures CPU and RAM usage from host machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;Kafka broker which facilitates event streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Producer&lt;/td&gt;
&lt;td&gt;Golang service which receives usage data from system stats service and pushes it into Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer&lt;/td&gt;
&lt;td&gt;Golang service which subscribes to Kafka broker and streams data to dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dashboard&lt;/td&gt;
&lt;td&gt;Visualises system stats in real time. Gets system stats data from Consumer through a WebSocket. Built using React&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;If you understand the architecture and the purpose of each service above, you can find the implementation in the below repository&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/sidv93/kafka-test" rel="noopener noreferrer"&gt;Realtime system monitoring &lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you do decide to stick around, we'll be going over in detail about these services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive
&lt;/h2&gt;

&lt;p&gt;Alright! Thanks for sticking around. We'll go over key parts of each service and finally we'll see how this comes together with docker compose.&lt;/p&gt;

&lt;h3&gt;
  
  
  System Stats
&lt;/h3&gt;

&lt;p&gt;The main purpose of this service is to collect system usage statistics from the host machine and send it to the &lt;code&gt;producer&lt;/code&gt; service.&lt;br&gt;
Here, I'm reading memory stats from the &lt;code&gt;/proc/meminfo&lt;/code&gt; file which contains information about the current memory usage. This file is available in all linux/unix based systems&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We could have used a much more focussed tool like &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; or &lt;a href="https://github.com/google/cadvisor" rel="noopener noreferrer"&gt;Cadvisor&lt;/a&gt; to gather system stats, but that is not the main objective of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We'd like to collect memory stats every second. So, we need a cron job which runs every second and reads memory info from &lt;code&gt;/proc/meminfo&lt;/code&gt; file. We'll write a function which reads, parses and returns memory stats.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyq4o0omj11qccrbkfwob.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyq4o0omj11qccrbkfwob.png" alt="read memory stats"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need a function which takes in the stats and sends it to the producer. This will be a simple HTTP POST call.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70fdzusy7sbccujjuju7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70fdzusy7sbccujjuju7.png" alt="push to producer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we need to put these two together in our cron job. Every time the job runs, these two functions need to be called in series. Our &lt;code&gt;main&lt;/code&gt; function for this service looks like this.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy0ihvni0jnfly5x9boa.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy0ihvni0jnfly5x9boa.png" alt="main_system_stats"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome, now we have a service which collects memory info from our host machine every second and sends it to the producer&lt;/p&gt;

&lt;h2&gt;
  
  
  Producer
&lt;/h2&gt;

&lt;p&gt;The purpose of this producer service is to receive system stats and push it into the Kafka broker. To do this, we have a HTTP POST endpoint which get the stats in the body and writes it into a Kafka topic.&lt;/p&gt;

&lt;p&gt;Our main function in this service is very simple. There is a single POST endpoint to receive stats. I'm using &lt;a href="https://gin-gonic.com/" rel="noopener noreferrer"&gt;Gin&lt;/a&gt; framework for routing here.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh06pkatu6rug4xiouckm.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh06pkatu6rug4xiouckm.png" alt="producer_main"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we write to Kafka, we need to setup our Kafka Writer. Let's do that. &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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pi9v4y762ce497csi78.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pi9v4y762ce497csi78.png" alt="kafka writer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can setup our &lt;code&gt;PushToKafka&lt;/code&gt; function which is the handler for our POST endpoint.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4crcdi7tzwmgtadehta.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft4crcdi7tzwmgtadehta.png" alt="push to kafka"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! We have a &lt;code&gt;Producer&lt;/code&gt; service which receives system stats via a POST endpoint and writes this data into our Kafka broker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consumer
&lt;/h2&gt;

&lt;p&gt;The Consumer service does two things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscribe to Kafka topic to receive system stats data&lt;/li&gt;
&lt;li&gt;Relay system stats data to &lt;code&gt;Dashboard&lt;/code&gt; via WebSockets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before our service can listen to Kafka messages, we need to setup a Kafka Reader&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft359dx6rl96jyi4hdmw6.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft359dx6rl96jyi4hdmw6.png" alt="consumer kafka reader"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our service can subscribe and listen to data pushed into Kafka queue by our &lt;code&gt;Producer&lt;/code&gt;. We'll setup a listen function which reads messages from Kafka&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvxemi6votyfz171ld3s.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flvxemi6votyfz171ld3s.png" alt="consumer listen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that it takes in a callback function. This callback function gets called whenever there is a message from Kafka. We'll use this callback function to relay systems stats to &lt;code&gt;Dashboard&lt;/code&gt; service&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next step is to send system stats data to Dashboard whenever we receive it from Kafka. But before we do this, we need an endpoint, specifically a socket endpoint which our Dashboard service can connect to. We'll define this endpoint in our &lt;code&gt;main&lt;/code&gt; function&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19qnamc4lzh5c9gdjdt7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19qnamc4lzh5c9gdjdt7.png" alt="socket endpoint"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And our upgrade handler looks like this&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4mmdtchryukefjl6fvr.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4mmdtchryukefjl6fvr.png" alt="socket upgrader"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To establish a Websocket connection, the HTTP connection between the service and client has to be &lt;em&gt;upgraded&lt;/em&gt; to use WebSocket protocol.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;connections.Subscribe(conn)&lt;/code&gt; call is to keep track of all the socket connections.&lt;/p&gt;

&lt;p&gt;The final step in our &lt;code&gt;Consumer&lt;/code&gt; service is to relay the messages from Kafka queue to Dashboard service. For this, we setup a function called &lt;code&gt;SendMessage&lt;/code&gt;, which will be our callback function for Kafka &lt;code&gt;Listen&lt;/code&gt; function.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wdp7fyhslrpyltdvu3p.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wdp7fyhslrpyltdvu3p.png" alt="consumer main"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! Now we have a &lt;code&gt;Consumer&lt;/code&gt; service which listens to messages in our Kafka queue and relays that message to our Dashboard service through a WebSocket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dashboard
&lt;/h2&gt;

&lt;p&gt;Dashboard is a very simple service which connects to the &lt;code&gt;Consumer&lt;/code&gt; service via a WebSocket and renders the system stats in a table UI.&lt;/p&gt;

&lt;p&gt;Without going into much detail about how to setup a React application or how the markup and styling will be, the important part here is to create a socket connection with &lt;code&gt;Consumer&lt;/code&gt; service's socket endpoint and setup an &lt;code&gt;onmessage&lt;/code&gt; callback function on our socket.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66pfreaw4dhy98p9zrk6.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66pfreaw4dhy98p9zrk6.png" alt="dashboard effect"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have a React component with system stats as our state. We update this state whenever we receive data from the WebSocket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kafka Broker
&lt;/h2&gt;

&lt;p&gt;The final service to setup is indeed Kafka Broker which facilitates this whole message queue. &lt;/p&gt;

&lt;p&gt;I'm using an example docker compose config from Kafka's Github&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/confluentinc/cp-all-in-one/blob/7.3.0-post/cp-all-in-one-kraft/docker-compose.yml#L5" rel="noopener noreferrer"&gt;Kafka-docker-compose&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2szvvf0wp6y61c6wgizo.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2szvvf0wp6y61c6wgizo.png" alt="kafka compose"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! We're done with our setup. &lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Now if we run all of the services using &lt;code&gt;docker-compose&lt;/code&gt;,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose build
docker-compose up


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

&lt;/div&gt;

&lt;p&gt;Navigate to &lt;code&gt;http://localhost:WEB_UI_PORT&lt;/code&gt;, we can see our dashboard getting updated every second with our system stats.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqw1hpeacl56ej55yflm.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqw1hpeacl56ej55yflm.gif" alt="output gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! As you can see, our table gets update with latest value of systems stats every second.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I haven't gone into detail about how to setup build process for our services. Please refer &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;docker-compose.yml&lt;/code&gt; files for more information.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;This is a barebones Kafka setup which does nothing more than relay messages in a single topic. I wrote this article just to get a basic understanding of what Kafka is, and to understand what Kafka could be used for. Hope folks reading this got something out of it.&lt;/p&gt;

&lt;p&gt;Thanks for coming through, cheers!&lt;/p&gt;

</description>
      <category>go</category>
      <category>kafka</category>
      <category>docker</category>
      <category>react</category>
    </item>
    <item>
      <title>Your first Turborepo</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Fri, 29 Apr 2022 20:42:22 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/your-first-turborepo-1lo</link>
      <guid>https://dev.to/siddharthvenkatesh/your-first-turborepo-1lo</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Monorepos are fantastic. They let you maintain all your projects in a single repository. I use one at my workplace and I see its advantages everyday. If you know anything about monorepos, setting them up can be tricky sometimes. Recently, I've been following the developments over at &lt;a href="https://turborepo.org/" rel="noopener noreferrer"&gt;Turborepo&lt;/a&gt;, which attempts to make setting up the tooling for monorepose simpler. The more I look through their docs, the more I get exited about using it. So, I gave it a shot and I have to say, the experience has been fantastic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this article?
&lt;/h2&gt;

&lt;p&gt;If you're wondering you can just go to their docs and set it up yourself, yes, you absolutely can. They have a cli which can help you setup a new project and they have a solid set of &lt;a href="https://github.com/vercel/turborepo/tree/main/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; for most scenarios. But, it is super fun setting things up from scratch, and I wanted to see how much of work it is with Turborepo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I'll setting up a new monorepo with a couple of simple apps and a UI library which would be shared by the apps. The goal is not the design and functionalities of these apps, but the tooling and features Turborepo provides. There will be two apps &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt;, both of them will be bundled using &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;. Vite is blazing fast and you should definitely give it a try just for its speed. The UI library, which will contain just a button component, which is written in TypeScript, will be bundled using &lt;a href="https://tsup.egoist.sh/" rel="noopener noreferrer"&gt;tsup&lt;/a&gt;. &lt;code&gt;tsup&lt;/code&gt; uses &lt;code&gt;esbuild&lt;/code&gt; underneath, so we can expect blazing fast build times. I'll be using yarn for package management. We will also be using a common &lt;code&gt;eslint&lt;/code&gt; configuration which will be shared across all three codebases.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Turborepo stuff
&lt;/h2&gt;

&lt;p&gt;Let's first create a folder for our project and start initialising our monorepo.&lt;br&gt;
As with any JS project, we start with a &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;package.json&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jk23re27riic7i31ykl.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jk23re27riic7i31ykl.png" alt="package.json"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;This is the initial config I'm using. It has &lt;code&gt;turbo&lt;/code&gt; and &lt;code&gt;eslint&lt;/code&gt; installed as a devDependency. If you are familiar with monorepos, the &lt;code&gt;workspaces&lt;/code&gt; array should make sense. All the projects in your monorepo should be listed as a workspace. Here, we have two directories, &lt;code&gt;apps&lt;/code&gt; contain &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt;, and &lt;code&gt;packages&lt;/code&gt;, which contains the UI library and the eslint configuration. Anything that can be shared across multiple projects can live in the &lt;code&gt;packages&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Next is our &lt;code&gt;turbo.json&lt;/code&gt;. This is Turborepo's config file. I browsed through their examples and found the simplest config to get started.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;turbo.json&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folr5dk21y29trh8ngwbg.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folr5dk21y29trh8ngwbg.png" alt="turbo.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll be covering this in a later section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up apps
&lt;/h2&gt;

&lt;p&gt;Vite has a cli which makes it easier for us to bootstrap a React app.&lt;br&gt;
In our &lt;code&gt;apps&lt;/code&gt; folder, run&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn create vite admin --template react


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

&lt;/div&gt;

&lt;p&gt;This will create a new react app named &lt;code&gt;admin&lt;/code&gt;. Similarly, we can create &lt;code&gt;products&lt;/code&gt; app as well.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn create vite products --template react


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

&lt;/div&gt;

&lt;p&gt;Now we have two apps named &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt; in our &lt;code&gt;apps&lt;/code&gt; directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the library
&lt;/h2&gt;

&lt;p&gt;I've added all the dependencies needed for a TS library with types and eslint packages. Also added are the scripts for &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;lint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;packages/ui/package.json&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u2h4msn579hgn1gljyp.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u2h4msn579hgn1gljyp.png" alt="packages/ui/package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, lets simply add a &lt;code&gt;Button&lt;/code&gt; component and export it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;packages/ui/Button.tsx&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqlrh899aeg46s2ox7h7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqlrh899aeg46s2ox7h7.png" alt="packages/ui/Button.tsx"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;packages/ui/index.tsx&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggz6qnrltup31inh50j2.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggz6qnrltup31inh50j2.png" alt="packages/ui/index.tsx"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, our project looks like this&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpv9lsqfns5acsy3lyh2.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpv9lsqfns5acsy3lyh2.png" alt="project structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have setup our apps and library, we can setup the tooling to link(&lt;em&gt;turbocharge&lt;/em&gt;) them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add library as a dependency
&lt;/h2&gt;

&lt;p&gt;The next step is to add the library as a dependency to our apps. It is as simple as adding it to &lt;code&gt;devDependecies&lt;/code&gt; in both &lt;code&gt;apps/admin/package.json&lt;/code&gt; and &lt;code&gt;apps/products/package.json&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyzfyxahzswddlkb2ilk.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyzfyxahzswddlkb2ilk.png" alt="apps/admin/package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turborepo will use the &lt;code&gt;name&lt;/code&gt; field in the library's &lt;code&gt;package.json&lt;/code&gt; to resolve it in the apps.&lt;/p&gt;

&lt;p&gt;We can now use this &lt;code&gt;Button&lt;/code&gt; component in &lt;code&gt;admin&lt;/code&gt; and products.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;apps/admin/src/App.jsx&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2m6mhyghpivgh9u47pnb.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2m6mhyghpivgh9u47pnb.png" alt="apps/admin/src/App.jsx"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can do the same thing in &lt;code&gt;apps/products/src/App.jsx&lt;/code&gt; as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding scripts
&lt;/h2&gt;

&lt;p&gt;The final step before we test this is to add scripts for &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;lint&lt;/code&gt; and &lt;code&gt;dev&lt;/code&gt;. In our root &lt;code&gt;package.json&lt;/code&gt;, we can add&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo75anh1l8dp223i8a0ue.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo75anh1l8dp223i8a0ue.png" alt="package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These commands are directly tied to the &lt;code&gt;pipeline&lt;/code&gt; configurations in &lt;code&gt;turbo.json&lt;/code&gt;. For example, if we look at the &lt;code&gt;build&lt;/code&gt; command, with the &lt;code&gt;"dependsOn": ["^build"],&lt;/code&gt; option, we are letting Turborepo know that build commands should only be run after all its dependencies are built. Turborepo is smart enough to realise &lt;code&gt;admin&lt;/code&gt; has a dependency &lt;code&gt;ui&lt;/code&gt;, which needs to be built before building &lt;code&gt;admin&lt;/code&gt;. So, it builds &lt;code&gt;ui&lt;/code&gt; first and then bundle &lt;code&gt;admin&lt;/code&gt;. &lt;code&gt;Pipelines&lt;/code&gt; are a powerful feature in Turborepo and you can read about it &lt;a href="https://turborepo.org/docs/core-concepts/pipelines" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, there is nothing left but to run our two apps. First, we would need to install our dependencies by running,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn install


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

&lt;/div&gt;

&lt;p&gt;Then, we start the dev server using&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn dev


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

&lt;/div&gt;

&lt;p&gt;If we inspect the terminal messages, we can see that &lt;code&gt;admin&lt;/code&gt; is running in &lt;code&gt;localhost:3000&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt; is running in &lt;code&gt;localhost:3001&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgmnrb42daoeuiol1jdw.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgmnrb42daoeuiol1jdw.png" alt="Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Look at the insane 2.914s start times! Vite FTW!)&lt;/p&gt;

&lt;p&gt;Now if we navigate to &lt;code&gt;localhost:3000&lt;/code&gt;, we see&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuivvp2cdf8m52d299hkl.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuivvp2cdf8m52d299hkl.png" alt="localhost:3000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see our button component is rendering as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up shared lint config
&lt;/h2&gt;

&lt;p&gt;Similar to how we shared a library across apps, we can share config files across apps as well. We'll be using a single &lt;code&gt;eslint&lt;/code&gt; config in all our apps and library. For that we shall create a folder called &lt;code&gt;config&lt;/code&gt; in our &lt;code&gt;packages&lt;/code&gt; directory.&lt;br&gt;
Inside it, we'll create a file &lt;code&gt;eslint-preset.js&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkopz2ybyk1gshslfua9c.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkopz2ybyk1gshslfua9c.png" alt="eslint-preset.js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And a &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;packages/config/package.json&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlv3wbzrj55en0d14elc.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlv3wbzrj55en0d14elc.png" alt="packages/config/package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The package.json contains all the &lt;code&gt;eslint&lt;/code&gt; packages we'll be needing, and notice the &lt;code&gt;files&lt;/code&gt; property includes the lint config file.&lt;/p&gt;

&lt;p&gt;Now, we add &lt;code&gt;config&lt;/code&gt; as a dev dependency in &lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;products&lt;/code&gt; and &lt;code&gt;ui&lt;/code&gt;. In each of their &lt;code&gt;package.json&lt;/code&gt;, add it as a &lt;code&gt;devDependency&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apps/admin/package.json&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4ttby16lje55uc24v0y.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4ttby16lje55uc24v0y.png" alt="apps/admin/package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, we would need a &lt;code&gt;.eslintrc.js&lt;/code&gt; which simply exports the lint config from &lt;code&gt;config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apps/admin/.eslintrc.js&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39rnvirximhfw5p9zg9i.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39rnvirximhfw5p9zg9i.png" alt="apps/admin/.eslintrc.js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we we run &lt;code&gt;yarn lint&lt;/code&gt; on our root folder, Turborepo will run the lint command on all of our projects.&lt;/p&gt;

&lt;p&gt;Notice that we did not need to install &lt;code&gt;eslint&lt;/code&gt;(except in the root) or its corresponding packages anywhere else other than the &lt;code&gt;config&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Awesome! We have setup our own monorepo with two apps, a library and a shared eslint config.&lt;/p&gt;

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

&lt;p&gt;This idea of monorepos can be extended and even backend code can be added to the same repo. One awesome use-case I can think of is sharing types between frontend and backend apps using a shared package. We have barely scratched the surface of Turborepo and its features. &lt;a href="https://turborepo.org/docs/core-concepts/remote-caching" rel="noopener noreferrer"&gt;Remote Caching&lt;/a&gt; &lt;br&gt;
  is one such feature I'm exited to try out. Meanwhile, this exercise was a great starting point.&lt;/p&gt;

&lt;p&gt;The source code for this can be found &lt;a href="https://github.com/sidv93/turborepo-test" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>monorepo</category>
    </item>
    <item>
      <title>Creating a simple file explorer with recursive components in React</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Wed, 16 Feb 2022 19:05:38 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/creating-a-simple-file-explorer-with-recursive-components-in-react-458h</link>
      <guid>https://dev.to/siddharthvenkatesh/creating-a-simple-file-explorer-with-recursive-components-in-react-458h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recursion is one of the most common programming constructs there is. Recursion in JavaScript land, is generally implemented via recursive functions, where a function calls itself. A very common example of a recursive function is the &lt;code&gt;factorial&lt;/code&gt; function. It goes like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function factorial(x) {
    if (x === 0) {
        return 1;
    }
    return x * factorial(x - 1);
}



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

&lt;/div&gt;

&lt;p&gt;As you can see, the function calls itself until the argument becomes 0. This idea can be extended to a variety of scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idea
&lt;/h2&gt;

&lt;p&gt;Where it gets interesting is when you add React into the mix. React components are &lt;em&gt;basically&lt;/em&gt; functions. So, it must be possible for a component to render instances of itself in it. &lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Let's build a simple file explorer to list files and folders. Each folder can in turn have multiple files and folders. When you click on a folder, it should expand to show its contents. It is exactly like the File Explorer sidebar in VSCode/Sublime etc.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46cik50bxy17xy3mb64k.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46cik50bxy17xy3mb64k.png" alt="VSCode file explorer"&gt;&lt;/a&gt;&lt;br&gt;
Let's create a component that mimics this behaviour and uses recursion in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Before we get started on our component, we need a list of files and folders. We'll create a json file with a files and folders from a typical React project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;files.json&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvblhzfcrpfczivavgeq6.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvblhzfcrpfczivavgeq6.png" alt="files.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, each entry will have a &lt;code&gt;name&lt;/code&gt; property, which denotes the name of the file/folder, a &lt;code&gt;type&lt;/code&gt; property, which denotes if it is a file or a folder, and an &lt;code&gt;items&lt;/code&gt; array, which in case of a folder will house all the contents in that folder. Each entry in the &lt;code&gt;items&lt;/code&gt; array will again be an entry with the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;items&lt;/code&gt; properties.&lt;br&gt;
With this, we are ready to create our recursive component&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursive  component
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;Directory&lt;/code&gt; component will accept a prop called &lt;code&gt;files&lt;/code&gt; which will be the contents from our &lt;code&gt;files.json&lt;/code&gt; file. First, let's get the easier part out of the way, displaying a file. If the &lt;code&gt;type&lt;/code&gt; property is &lt;code&gt;file&lt;/code&gt;, we simply render the file name&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Directory.jsx&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y2wfsccvmth3vdogvm2.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y2wfsccvmth3vdogvm2.png" alt="Render out file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now for the folder part, we first render the name of the folder.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3fr6n3qtb8q4i3w9tku.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3fr6n3qtb8q4i3w9tku.png" alt="Render out folder name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To render the &lt;code&gt;items&lt;/code&gt; in a folder, all we have to do is loop through the &lt;code&gt;items&lt;/code&gt; array and render the &lt;code&gt;&amp;lt;Directory /&amp;gt;&lt;/code&gt; component for each item.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkhow0ra762l0rieex06.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkhow0ra762l0rieex06.png" alt="Render out items in folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;&amp;lt;Directory /&amp;gt;&lt;/code&gt; component now uses recursion to traverse through our file list to render files and folders. One final thing left to do is, when you click on a folder, its contents should show up. We can do this by declaring a state variable in our component and toggling it on clicks.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30b39yzks9fk7kejp3ek.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30b39yzks9fk7kejp3ek.png" alt="Expand on clicking folder name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! This should be enough to get our app up and running. We'll import this component and pass in contents from &lt;code&gt;files.json&lt;/code&gt; as a prop.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;App.jsx&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6sg72t5xedh8j5a6bfq.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6sg72t5xedh8j5a6bfq.png" alt="Use directory component in app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if we run our app, it should give us something like this. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwujw7b8ucf7wepblqpw.gif" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwujw7b8ucf7wepblqpw.gif" alt="Complete video"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thats it! We've created a component which recursively calls itself.&lt;/p&gt;

&lt;p&gt;The complete source code can be found &lt;a href="https://github.com/sidv93/react-recursive-component" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Network aware preloading strategy in Angular</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Tue, 05 Oct 2021 17:39:11 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/network-aware-preloading-strategy-in-angular-3a4c</link>
      <guid>https://dev.to/siddharthvenkatesh/network-aware-preloading-strategy-in-angular-3a4c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Preloading is great. It lets us fetch parts of our application before it is even requested for. By doing this, our content is ready to be served when needed without any delay. There are many ways of doing this, I'll be touching upon some of the ways to do this in Angular and how we can create our own custom preloading strategy based on the user's network state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preloading strategies in Angular
&lt;/h2&gt;

&lt;p&gt;Angular provides us with route-based preloading out of the box. There are two strategies namely &lt;code&gt;PreloadAllModules&lt;/code&gt; and &lt;code&gt;NoPreloading&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NoPreloading&lt;/code&gt;, as the name suggests does not preload any modules. This is the default behaviour.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PreloadAllModules&lt;/code&gt;, on the other hand, preloads all modules. A simple example adding a preloading strategy in a routing module file can be seen below&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app.routing.module.ts&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vwqiwyljnr2g10j25l6.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vwqiwyljnr2g10j25l6.png" alt="image"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In the above code, the &lt;code&gt;ProductsModule&lt;/code&gt; is lazily loaded, which means, the bundle is downloaded only when the user lands on the &lt;code&gt;/products&lt;/code&gt; route. By passing the &lt;code&gt;preloadingStrategy&lt;/code&gt; property, we instruct Angular to preload all lazily loaded modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom preloading strategies
&lt;/h2&gt;

&lt;p&gt;Angular also lets us provide custom preloading strategies where we can determine if a component needs to be preloaded or not. This can be done by providing our own custom class to the &lt;code&gt;preloadingStrategy&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Our custom class needs to extend &lt;code&gt;PreloadingStrategy&lt;/code&gt; class from &lt;code&gt;@angular/router&lt;/code&gt; and implement the &lt;code&gt;preload&lt;/code&gt; function in it. A simple custom preloading strategy class would look something like this.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;customPreloadingStrategy.ts&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1kqsgo5q3y523oydzt7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1kqsgo5q3y523oydzt7.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, our &lt;code&gt;CustomPreloadingStrategy&lt;/code&gt; class implements &lt;code&gt;PreloadingStrategy&lt;/code&gt; class and has the &lt;code&gt;preload&lt;/code&gt; function as well. The &lt;code&gt;preload&lt;/code&gt; function should either return the &lt;code&gt;load&lt;/code&gt; function or an empty observable. Returning the load function means the module can be loaded. As for the &lt;code&gt;shouldPreload&lt;/code&gt; function, that is where our custom logic goes in. &lt;/p&gt;

&lt;p&gt;We can use this custom strategy in our routing module&lt;br&gt;
&lt;code&gt;app.routing.module.ts&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuagf7y1rx1dkvg17s40t.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuagf7y1rx1dkvg17s40t.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Network aware preloading strategy
&lt;/h2&gt;

&lt;p&gt;Now that we are familiar with setting up a custom preloading strategy, now we can move on to using our user's network connection information to make a decision for us. Let's say our user has a very slow bandwidth, so we wouldn't want to further tax our user by preloading all of our modules in the background. In that case, we can decide not to preload our modules. To do this, we rely on the &lt;strong&gt;navigator&lt;/strong&gt; object in our browser and specifically the &lt;strong&gt;connection&lt;/strong&gt; property in navigator.&lt;/p&gt;

&lt;p&gt;If you're reading this in desktop/laptop, open the browser's console and type in &lt;code&gt;navigator.connection&lt;/code&gt;, you would see something like this.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgw7vyketqui48ic1wce.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgw7vyketqui48ic1wce.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can leverage this information in our &lt;code&gt;shouldPreload&lt;/code&gt; function to let Angular know whether to preload the module or not.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;customPreloadingStrategy.ts&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvhg57yg84gt9v4gtjqs.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvhg57yg84gt9v4gtjqs.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example above, we're using the &lt;code&gt;saveData&lt;/code&gt; property to determine if the user has data-saver on, and we're using the &lt;code&gt;effectiveType&lt;/code&gt; property to see if the user is on &lt;code&gt;2g&lt;/code&gt; or &lt;code&gt;3g&lt;/code&gt; connections. If the user is on slower internet connections, we are returning false, which would in turn prevent preloading of the particular module&lt;/p&gt;

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

&lt;p&gt;We can extend this idea of custom preloading strategies to make intelligent decisions based on our user's state. I have no idea if this method would even result in a tangible improvement in the user experience, but nevertheless a fun topic to explore.&lt;/p&gt;

&lt;p&gt;You can find the source code &lt;a href="https://github.com/sidv93/ng-network-aware-preloading" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>angular</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Shared library in yarn workspaces</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Tue, 24 Aug 2021 11:03:33 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/shared-library-in-yarn-workspaces-1fbp</link>
      <guid>https://dev.to/siddharthvenkatesh/shared-library-in-yarn-workspaces-1fbp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, I wrote and article about setting up yarn workspaces and adding Docker support to it for orchestration. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can read it here &lt;a href="https://dev.to/sidv93/docker-setup-for-yarn-workspaces-4pnj"&gt;Docker setup for yarn workspaces&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought we can extend this setup to include a library as well. Shared libraries are fairly common in any organisation, so we'll be adding a component library based on React to our workspace.&lt;/p&gt;

&lt;p&gt;To go over our setup, we have an &lt;code&gt;apps&lt;/code&gt; folder where all our apps live in. We have two apps called &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;product&lt;/code&gt;. We are going to be adding a component library to this setup and this library will be used by both of our apps. Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a library
&lt;/h2&gt;

&lt;p&gt;I'm going to be using &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; as our build tool. It has a nice cli which lets us scaffold an application easily. &lt;br&gt;
To create a vite project in our &lt;code&gt;apps&lt;/code&gt;directory,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

cd apps
yarn create vite lib --template react


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

&lt;/div&gt;

&lt;p&gt;Our &lt;code&gt;lib&lt;/code&gt; folder would look something like this now&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgowzzcen0vl70hc8wb8.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgowzzcen0vl70hc8wb8.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to update our dependencies by running &lt;code&gt;yarn install&lt;/code&gt; from the root of our workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding components to our library
&lt;/h3&gt;

&lt;p&gt;If we open the &lt;code&gt;src&lt;/code&gt; folder in &lt;code&gt;lib&lt;/code&gt;, we can see that it is an ideal setup for an application, not a library. So, we'll remove all the files from &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;index.html&lt;/code&gt; and add our own.&lt;/p&gt;

&lt;p&gt;First, we'll add an entry file called &lt;code&gt;index.js&lt;/code&gt; in &lt;code&gt;src&lt;/code&gt;. This will be the starting point file in our bundling process.&lt;/p&gt;

&lt;p&gt;Next, we'll create a folder called &lt;code&gt;components&lt;/code&gt; which would house all our components, and add an &lt;code&gt;index.js&lt;/code&gt; file to it as well. We'll export all our component from this index file.&lt;/p&gt;

&lt;p&gt;Our project should look like this now.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgf0vr5msy779hbh30fm.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frgf0vr5msy779hbh30fm.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding a button component
&lt;/h4&gt;

&lt;p&gt;Great! We have our directory setup nailed down. We can finally start adding components. We can add a simple button component. I'm going to create a directory called &lt;code&gt;Button&lt;/code&gt; in &lt;code&gt;components&lt;/code&gt; directory, which would contain three files. &lt;code&gt;Button.jsx&lt;/code&gt;, &lt;code&gt;Button.css&lt;/code&gt; and an &lt;code&gt;index.js&lt;/code&gt;.&lt;br&gt;
Let's add the contents of each of these files&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Button.jsx&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjba15gapkoa6vk21s3n.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqjba15gapkoa6vk21s3n.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Button.css&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F798umb3993ka4bdinmtn.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F798umb3993ka4bdinmtn.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34crjymbhlf11r9yvf9x.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34crjymbhlf11r9yvf9x.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a very simple component with not a lot of emphasis on styling and attributes. You can customise this component to your liking.&lt;/p&gt;

&lt;p&gt;We have a component exported from our &lt;code&gt;Button&lt;/code&gt; directory. We need to export this component from our &lt;code&gt;components&lt;/code&gt; directory as well. We'll add this export like this&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/components/index.js&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wqvhl7ec6336pjpy2nu.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wqvhl7ec6336pjpy2nu.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to export the components from our &lt;code&gt;src&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;src/index.js&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagp521u55msosv1ruepm.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagp521u55msosv1ruepm.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This would export all our exports from &lt;code&gt;components&lt;/code&gt; directory. If we add more components, all of them would be exported from here. &lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;lib&lt;/code&gt; folder should look like this now&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjtrbor42wf8c1h6yws5.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjtrbor42wf8c1h6yws5.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Finetuning Vite config
&lt;/h4&gt;

&lt;p&gt;Before we can start using our library in applications, we need to modify our &lt;code&gt;vite.config.js&lt;/code&gt; to let vite know this is a library and should be bundled as one.&lt;br&gt;
The documentation for this can be found &lt;a href="https://vitejs.dev/guide/build.html#library-mode" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
We'll be adding the following config to &lt;code&gt;vite.config.js&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rl84kv097xygov0n0hk.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rl84kv097xygov0n0hk.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also need to add some options to our lib's &lt;code&gt;package.json&lt;/code&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyv3khe3u7qr7u5ta3ozl.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyv3khe3u7qr7u5ta3ozl.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;peerDependencies&lt;/code&gt; option tells the bundler not to add these dependencies in our final bundle. &lt;br&gt;
The &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;module&lt;/code&gt; and &lt;code&gt;exports&lt;/code&gt; options are needed for the application's bundler to figure out where the files are for &lt;code&gt;umd&lt;/code&gt; and &lt;code&gt;esm&lt;/code&gt; formats.  &lt;/p&gt;

&lt;p&gt;Great! Let's now move on to using library in our apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using our library in apps
&lt;/h3&gt;

&lt;p&gt;Adding a local library as a dependency is as simple as adding any other dependency.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn workspace admin add lib@0.1.0


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

&lt;/div&gt;

&lt;p&gt;This command would add &lt;code&gt;lib&lt;/code&gt; as a dependency to &lt;code&gt;admin&lt;/code&gt;. Notice we've mentioned the version of lib as well. This version must be the same as the &lt;code&gt;version&lt;/code&gt; property in &lt;code&gt;package.json&lt;/code&gt; in &lt;code&gt;lib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another way is to simply add &lt;code&gt;lib: 0.1.0&lt;/code&gt; entry to the &lt;code&gt;dependencies&lt;/code&gt; section of &lt;code&gt;package.json&lt;/code&gt; in admin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing it out
&lt;/h3&gt;

&lt;p&gt;We are now in a position to use our &lt;code&gt;Button&lt;/code&gt; component from lib. We'll do that in &lt;code&gt;App.jsx&lt;/code&gt; in admin&lt;/p&gt;

&lt;p&gt;&lt;code&gt;admin/src/App.js&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uo8ud73c60rssir92is.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uo8ud73c60rssir92is.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next step is to alter our scripts in &lt;code&gt;package.json&lt;/code&gt; to make sure our library is compiled when we run our applications.&lt;br&gt;
We'll add a few scripts to do this&lt;/p&gt;

&lt;p&gt;&lt;code&gt;package.json&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kutau29l56d6ulvqyq5.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kutau29l56d6ulvqyq5.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! We're almost done. Only thing left to do is to check our admin app.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn start:admin


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

&lt;/div&gt;

&lt;p&gt;If we open &lt;code&gt;http://localhost:3000&lt;/code&gt; on our browser, we can see our &lt;code&gt;Button&lt;/code&gt; component in red color as per our &lt;code&gt;type&lt;/code&gt; prop.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphpfrvy8pe5tgiaeic11.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphpfrvy8pe5tgiaeic11.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can repeat the same process to use the library in any other application in the workspace.&lt;/p&gt;

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

&lt;p&gt;Awesome! We have a yarn workspace with two applications and a component library in React. We can extend this idea and even add multiple libraries into this workspace.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are interested in setting up a React-Typescrippt component library from scratch, check out my article here &lt;a href="https://dev.to/sidv93/component-library-setup-with-react-typescript-and-rollup-onj"&gt;Component library setup with React, TypeScript and Rollup&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The source code for this can be found &lt;a href="https://github.com/sidv93/yarn-worspace-docker" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>yarn</category>
    </item>
    <item>
      <title>Docker setup for yarn workspaces</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Fri, 20 Aug 2021 18:06:44 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/docker-setup-for-yarn-workspaces-4pnj</link>
      <guid>https://dev.to/siddharthvenkatesh/docker-setup-for-yarn-workspaces-4pnj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As monorepos seem to be having their moment in the developer community right now, we can see quite a bit of new monorepo tools popping up. npm recently announced &lt;a href="https://docs.npmjs.com/cli/v7/using-npm/workspaces" rel="noopener noreferrer"&gt;npm workspaces&lt;/a&gt; with version 7, &lt;a href="https://nx.dev/" rel="noopener noreferrer"&gt;Nx&lt;/a&gt; has been gaining a lot popularity and &lt;a href="https://github.com/lerna/lerna" rel="noopener noreferrer"&gt;lerna&lt;/a&gt; has been around for quite a while now. I use &lt;a href="https://yarnpkg.com/" rel="noopener noreferrer"&gt;yarn&lt;/a&gt; in most of my projects now, and thought it would be fun to explore &lt;a href="https://yarnpkg.com/features/workspaces/" rel="noopener noreferrer"&gt;yarn workspaces&lt;/a&gt; with a simple monorepo setup.&lt;/p&gt;

&lt;p&gt;In this workspace, I am going to be adding two React applications. Further, we can also add docker support to make it easier for deployments. Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialising the workspace
&lt;/h2&gt;

&lt;p&gt;Let's start by creating a folder for our project and initialise yarn&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

mkdir yarn-docker-setup
cd yarn-docker-setup
yarn init -p


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;If you do not have yarn installed already, you can install by &lt;code&gt;npm install yarn -g&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After filling out basic questions, you would have a &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;To turn this project into a workspace, we need to add &lt;code&gt;workspaces&lt;/code&gt; option in our &lt;code&gt;package.json&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"workspaces": ["apps/*"]


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;apps&lt;/code&gt; is a directory where all our apps live.&lt;br&gt;
Great! We've initialised our workspace, next step is to add applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding apps
&lt;/h2&gt;

&lt;p&gt;We're going to be adding two React applications to this project namely &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;product&lt;/code&gt;. I'm using &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt; to scaffold our apps.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn create react-app apps/admin
yarn create react-app apps/product


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

&lt;/div&gt;

&lt;p&gt;This would take a couple of minutes to finish and by the end you would have two folders called &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;product&lt;/code&gt; inside the &lt;code&gt;apps&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Great! We've added two apps to our workspace. The next step is let yarn know about each app's dependencies, so it can optimise and cache them. In the project root folder, run&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

yarn install


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

&lt;/div&gt;

&lt;p&gt;This goes through the dependencies and moves them to a central &lt;code&gt;node_modules&lt;/code&gt; folder in the project's root.&lt;/p&gt;

&lt;p&gt;Let's test out our setup to see everything works. Let's add scripts in our &lt;code&gt;package.json&lt;/code&gt; to start and build our apps&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"scripts": {
    "admin": "yarn workspace admin start",
    "product": "yarn workspace product start",
    "build:admin": "yarn workspace admin build",
    "build:product": "yarn workspace product build"
}


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

&lt;/div&gt;

&lt;p&gt;We've also added build scripts to compile our apps into static files.&lt;br&gt;
If we run &lt;code&gt;yarn admin&lt;/code&gt; or &lt;code&gt;yarn product&lt;/code&gt;, we should see the standard create react app screen&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5xff49wngki2svk7eeg.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5xff49wngki2svk7eeg.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Docker support
&lt;/h2&gt;

&lt;p&gt;Docker provides us with a simple and effective way to package our apps into images that could be run anywhere without any dependence on the environment or operating system. With docker-compose, we can orchestrate multiple services(apps) with a simple configuration. Going too much into docker and docker-compose maybe a bit out of reach for this article, so let's dive into the docker setup.&lt;/p&gt;

&lt;p&gt;First step is add a &lt;code&gt;Dockerfile&lt;/code&gt;. We can add individual Dockerfiles for each app, but since the build process is same for both the apps, we can use a single Dockerfile for both of them.&lt;/p&gt;

&lt;p&gt;First, we need a &lt;code&gt;node&lt;/code&gt; environment to compile our React projects, and we need the name of the folder which we need to build, in this case &lt;code&gt;admin&lt;/code&gt; or &lt;code&gt;product&lt;/code&gt;. We get that using the &lt;code&gt;BUILD_CONTEXT&lt;/code&gt; argument.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FROM node:14.17.1 as build
ARG BUILD_CONTEXT


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

&lt;/div&gt;

&lt;p&gt;The next step is to copy over the source code into the image.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

WORKDIR /base
COPY package.json .
COPY yarn.lock .
COPY ./apps/$BUILD_CONTEXT/package.json apps/$BUILD_CONTEXT/
RUN yarn install


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

&lt;/div&gt;

&lt;p&gt;We are defining &lt;code&gt;/base&lt;/code&gt; as our working directory. All our code goes here.&lt;br&gt;
In the next 3 lines, we are copying &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;yarn.lock&lt;/code&gt; and the &lt;code&gt;package.json&lt;/code&gt; file of the particular app into the image.&lt;br&gt;
Then we run &lt;code&gt;yarn install&lt;/code&gt; to install our dependencies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Interesting thing to note here is, we could have copied our entire source code into the container in one go. The reason we don't do that is, every instruction in a Dockerfile is cached in the background. By copying just the &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;yarn.lock&lt;/code&gt; files, we can take advantage of this caching system. These files rarely change in the course of the project, so if we install our dependencies once, and if they don't change the next time we build, Docker will use the existing cache and not run &lt;code&gt;yarn install&lt;/code&gt; every-time we build. This will significantly reduce our build times.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next step is to copy the app's code and build.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

COPY ./apps/$BUILD_CONTEXT apps/$BUILD_CONTEXT
RUN yarn build:$BUILD_CONTEXT


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

&lt;/div&gt;

&lt;p&gt;Great, as of now our &lt;code&gt;Dockerfile&lt;/code&gt; looks like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FROM node:14.17.1 as build
ARG BUILD_CONTEXT

WORKDIR /fe
COPY package.json .
COPY yarn.lock .
COPY ./apps/$BUILD_CONTEXT/package.json apps/$BUILD_CONTEXT/
RUN yarn install
COPY ./apps/$BUILD_CONTEXT apps/$BUILD_CONTEXT
RUN yarn build:$BUILD_CONTEXT


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

&lt;/div&gt;

&lt;p&gt;Our compilation step is complete. Our React app has been compiled into static files and they are inside the image. But order to serve them, we need a web server. We could use &lt;code&gt;node&lt;/code&gt; as our web server as we are already using it for building. But a node image is significantly bigger(close to a gigabyte) in size compared to a traditional web server like &lt;a href="https://www.nginx.com/" rel="noopener noreferrer"&gt;nginx&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll add nginx configuration as part our build step in our &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FROM nginx:stable-alpine
ARG BUILD_CONTEXT
COPY --from=build /fe/apps/$BUILD_CONTEXT/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]


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

&lt;/div&gt;

&lt;p&gt;The first two lines are self-explanatory.&lt;br&gt;
The third line is where is gets interesting. If you see the first line of our &lt;code&gt;Dockerfile&lt;/code&gt;, it says &lt;code&gt;as build&lt;/code&gt; next to our node version. This is done so we can refer to this as context in later parts of our build steps.&lt;br&gt;
We have our compiled React app in the &lt;code&gt;node&lt;/code&gt; image. We need to take those files and put it in our &lt;code&gt;nginx&lt;/code&gt; image. That's what this line does. It copies the &lt;code&gt;/fe/apps/$BUILD_CONTEXT/build&lt;/code&gt; folder from &lt;code&gt;build&lt;/code&gt; context into &lt;code&gt;/usr/share/nginx/html&lt;/code&gt;.&lt;br&gt;
The last line is to start our &lt;code&gt;nginx&lt;/code&gt; web server.&lt;/p&gt;

&lt;p&gt;The next step is to define an &lt;code&gt;nginx.conf&lt;/code&gt; config file nginx can use to run our app, which looks like this. This is a barebones nginx web server configuration which can be used for any frontend application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

server {

  listen 80;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }

}


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

&lt;/div&gt;

&lt;p&gt;Our entire &lt;code&gt;Dockerfile&lt;/code&gt; now looks like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

#build
FROM node:14.17.1 as build
ARG BUILD_CONTEXT

WORKDIR /base
COPY package.json .
COPY yarn.lock .
COPY ./apps/$BUILD_CONTEXT/package.json apps/$BUILD_CONTEXT/
RUN yarn install
COPY ./apps/$BUILD_CONTEXT apps/$BUILD_CONTEXT
RUN yarn build:$BUILD_CONTEXT

#webserver
FROM nginx:stable-alpine
ARG BUILD_CONTEXT
COPY --from=build /base/apps/$BUILD_CONTEXT/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]


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

&lt;/div&gt;

&lt;p&gt;This setup is enough for us to build a Docker image of our app and run by running&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker run &amp;lt;image-name&amp;gt; -e BUILD_CONTEXT=admin/product


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

&lt;/div&gt;

&lt;p&gt;We want to go a bit further and add in an orchestration step using &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;docker-compose&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this, we need to add a &lt;code&gt;docker-compose.yml&lt;/code&gt; file in the root of our project.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

version: '3'

services:
  admin:
    container_name: admin
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - BUILD_CONTEXT=admin
    ports:
      - '8080:80'
  product:
    container_name: product
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - BUILD_CONTEXT=product
    ports:
      - '8082:80'



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

&lt;/div&gt;

&lt;p&gt;We define two services here, &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;product&lt;/code&gt; for our two apps.&lt;br&gt;
In our service section, we define three properties, &lt;code&gt;container_name&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;ports&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;container_name&lt;/code&gt; defines the name of the container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;context&lt;/code&gt; in &lt;code&gt;build&lt;/code&gt; refers to the directory this build needs to be executed on, &lt;code&gt;dockerfile&lt;/code&gt; refers to the name and location of the &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;args&lt;/code&gt; refer to build time arguments. These are the arguments that will be used in the &lt;code&gt;Dockerfile&lt;/code&gt; &lt;code&gt;ARG&lt;/code&gt; section&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ports&lt;/code&gt; lets us map ports on the host machine to the container port. Value &lt;code&gt;8082:80&lt;/code&gt; indicates that any request on port 8082 on host machine will be routed to port 80 on the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Awesome! We are done with our docker-compose setup. Final thing left to do is run and see for ourselves.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose build


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

&lt;/div&gt;

&lt;p&gt;command is used to build out both our apps. This will compile our app using instructions from our &lt;code&gt;Dockerfile&lt;/code&gt; and create an image.&lt;/p&gt;

&lt;p&gt;To run these images,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker-compose up


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

&lt;/div&gt;

&lt;p&gt;This command will take our images and create containers and run them.&lt;/p&gt;

&lt;p&gt;Now we can go to &lt;code&gt;http://localhost:8080&lt;/code&gt; and &lt;code&gt;http://localhost:8082&lt;/code&gt; to see our apps in action.&lt;/p&gt;

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

&lt;p&gt;What we have now is a very simple implementation of workspace and docker setup. We can use this as a starting point and start adding backend services and component libraries to this setup. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are interested in setting up a component library from scratch, check out my article on &lt;a href="https://dev.to/sidv93/component-library-setup-with-react-typescript-and-rollup-onj"&gt;Setting up a component library with React, TypeScript and Rollup&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can add new projects into the &lt;code&gt;apps&lt;/code&gt; folder and yarn would take care of the dependency resolutions for us. &lt;/p&gt;

&lt;p&gt;The source code for this setup can be found &lt;a href="https://github.com/sidv93/yarn-worspace-docker" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>yarn</category>
      <category>docker</category>
      <category>react</category>
    </item>
    <item>
      <title>Component library setup with React, TypeScript and Rollup</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Mon, 16 Aug 2021 18:48:42 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/component-library-setup-with-react-typescript-and-rollup-onj</link>
      <guid>https://dev.to/siddharthvenkatesh/component-library-setup-with-react-typescript-and-rollup-onj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Component libraries are becoming more and more popular by the day, especially at organisations with multiple products and teams. Organisations are dedicating teams just to maintain the component library. The end goal here might be a &lt;em&gt;Design System&lt;/em&gt;, with well thought our principles and practices. But, a &lt;em&gt;good&lt;/em&gt; design system takes months or even years of research and a dedicated team which a lot of organisation cannot afford. Google's &lt;a href="https://material.io/design" rel="noopener noreferrer"&gt;Material design&lt;/a&gt; and Atlassian's &lt;a href="https://atlassian.design/" rel="noopener noreferrer"&gt;Design system&lt;/a&gt; are some of the excellent ones that come to mind. A good place to start for majority of teams is a component library. A collection of commonly used components which can help attain consistency across applications. We can start out with simple components like &lt;code&gt;button&lt;/code&gt;, &lt;code&gt;inputs&lt;/code&gt;, &lt;code&gt;modal&lt;/code&gt; and add more along the way.&lt;/p&gt;

&lt;p&gt;Let's try to build a simple component library from scratch using React, Typescript, and Rollup to bundle it, and learn a thing or two along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialise the project
&lt;/h3&gt;

&lt;p&gt;Let's start by creating a directory and initialising an &lt;code&gt;npm&lt;/code&gt; project called &lt;code&gt;react-lib&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

mkdir react-lib
cd react-lib
npm init


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

&lt;/div&gt;

&lt;p&gt;You can fill out the questions or pass the &lt;code&gt;-y&lt;/code&gt; flag to initialise with default values. We now have a &lt;code&gt;package.json&lt;/code&gt; file in our project.&lt;/p&gt;

&lt;p&gt;Since we're going to be using &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;typescript&lt;/code&gt;, we can add those dependencies&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm i -D react typescript @types/react


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

&lt;/div&gt;

&lt;p&gt;Since we are going to be shipping this as a library, all our packages will be listed under &lt;code&gt;devDependencies&lt;/code&gt;. Also, the app in which this library will be used will come with react, we don't have to bundle react along. So, we'll add &lt;code&gt;react&lt;/code&gt; as a &lt;code&gt;peerDependency&lt;/code&gt;. Our &lt;code&gt;package.json&lt;/code&gt; looks like this now&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5e1ps2jsmfveyt73rge5.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5e1ps2jsmfveyt73rge5.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding components
&lt;/h3&gt;

&lt;p&gt;My preferred way of organising components is inside &lt;code&gt;src/components&lt;/code&gt; folder, where each component would have its own folder. For example, if we have a &lt;code&gt;Button&lt;/code&gt; component, there would be a folder called &lt;code&gt;Button&lt;/code&gt; in &lt;code&gt;src/components&lt;/code&gt; with all the button related files like &lt;code&gt;Button.tsx&lt;/code&gt;, &lt;code&gt;Button.css&lt;/code&gt;, &lt;code&gt;Button.types.ts&lt;/code&gt;, and an &lt;code&gt;index.ts&lt;/code&gt; file to export the component&lt;/p&gt;

&lt;p&gt;There are also a couple of index files along the way to export stuff. One is the main entrypoint to the project, at &lt;code&gt;src/index.ts&lt;/code&gt;, and one which exports all the components at &lt;code&gt;src/components/index.ts&lt;/code&gt;. The folder structure with the button component would look like this.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc03mm8zd7vwx978blac.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc03mm8zd7vwx978blac.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Button component
&lt;/h3&gt;

&lt;p&gt;Now, let's add the code for the &lt;code&gt;Button&lt;/code&gt; component. I'm going with a very simple component as this is not really our concern right now.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Button.tsx&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3epx7wn96a3fp0zhp013.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3epx7wn96a3fp0zhp013.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Button.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhw3ytv6fohyo6l8zd2jk.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhw3ytv6fohyo6l8zd2jk.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Button.types.ts&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7kd958a5ocot9i7t5t2.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7kd958a5ocot9i7t5t2.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Button/index.ts&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk005963bxnmn55gchecg.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk005963bxnmn55gchecg.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our &lt;code&gt;Button&lt;/code&gt; component, we can export it from components and from src.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/component/index.ts&lt;/code&gt; &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffumurod16fsf7u3rukq9.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffumurod16fsf7u3rukq9.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/index.ts&lt;/code&gt; &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7vk16q2pkuva14dplnwj.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7vk16q2pkuva14dplnwj.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TypeScript configuration
&lt;/h3&gt;

&lt;p&gt;We've added our components and now in order to build our library, we need to configure Typescript. We've already installed the typescript dependency, now we need to add the &lt;code&gt;tsconfig.json&lt;/code&gt;. We can do this by&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npx tsc --init


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

&lt;/div&gt;

&lt;p&gt;This creates a &lt;code&gt;tsconfig.json&lt;/code&gt; file with most of the available options commented. I use most of the defaults with some minor changes.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "jsx": "react",
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
  }
}


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

&lt;/div&gt;

&lt;p&gt;Let's add a build script in our &lt;code&gt;package.json&lt;/code&gt; to test this out.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"scripts": {
    "build": "tsc"
 },


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

&lt;/div&gt;

&lt;p&gt;If we run &lt;code&gt;npm run build&lt;/code&gt;, we should see a &lt;code&gt;dist&lt;/code&gt; folder with all our ts files transpiled into js files. If you notice, there are no css files in &lt;code&gt;dist&lt;/code&gt; and they are not bundled by out ts compiler. Let's do that using &lt;strong&gt;Rollup&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rollup configuration
&lt;/h3&gt;

&lt;p&gt;We'll be using &lt;a href="https://rollupjs.org/guide/en/" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; as the bundler of choice here. So, lets install it&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm i -D rollup


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  Plugins
&lt;/h4&gt;

&lt;p&gt;Rollup has a plugin system by which we can specify all the tasks that need to be performed during the bundling process. We'll need the following plugins&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@rollup/plugin-node-resolve&lt;/code&gt; - Resolve third party dependencies in &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@rollup/plugin-commonjs&lt;/code&gt; - To convert &lt;code&gt;commonjs&lt;/code&gt; modules into ES6&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@rollup/plugin-typescript&lt;/code&gt; - To transpile our Typescript code in JS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rollup-plugin-peer-deps-external&lt;/code&gt; -  To prevent bundling &lt;code&gt;peerDependencies&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rollup-plugin-postcss&lt;/code&gt; - To handle our css&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rollup-plugin-terser&lt;/code&gt; - To minify our bundle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets install these plugins&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm i -D @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-peer-deps-external rollup-plugin-postcss rollup-plugin-terser


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  rollup.config.js
&lt;/h4&gt;

&lt;p&gt;The next step is to add the &lt;code&gt;rollup.config.js&lt;/code&gt; file. This is where all our rollup configs live.&lt;/p&gt;

&lt;p&gt;The entrypoint to our library is the &lt;code&gt;src/index.ts&lt;/code&gt; file and we'll be bundling our library into both &lt;code&gt;commonjs&lt;/code&gt; and &lt;code&gt;es modules&lt;/code&gt; formats. If the app using this library supports esmodules, it will use the &lt;code&gt;esm&lt;/code&gt; build, otherwise &lt;code&gt;cjs&lt;/code&gt; build will be used.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rollup.config.js&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';

const packageJson = require('./package.json');

export default {
    input: 'src/index.ts',
    output: [
        {
            file: packageJson.main,
            format: 'cjs',
            sourcemap: true,
            name: 'react-lib'
        },
        {
            file: packageJson.module,
            format: 'esm',
            sourcemap: true
        }
    ],
    plugins: [
        external(),
        resolve(),
        commonjs(),
        typescript({ tsconfig: './tsconfig.json' }),
        postcss(),
        terser()
    ]
}



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

&lt;/div&gt;

&lt;p&gt;We have defined the &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;output&lt;/code&gt; values for our &lt;code&gt;cjs&lt;/code&gt; and &lt;code&gt;esm&lt;/code&gt; builds. &lt;/p&gt;

&lt;h4&gt;
  
  
  Putting it all together
&lt;/h4&gt;

&lt;p&gt;Notice that we have specified the &lt;code&gt;file&lt;/code&gt; option in &lt;code&gt;output&lt;/code&gt; from &lt;code&gt;package.json&lt;/code&gt;. Let's go ahead and define these two values in &lt;code&gt;package.json&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",



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

&lt;/div&gt;

&lt;p&gt;Now that we've configured Rollup, we can use it in our build script in &lt;code&gt;package.json&lt;/code&gt; instead of the &lt;code&gt;tsc&lt;/code&gt; command before.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"build": "rollup -c"


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

&lt;/div&gt;

&lt;p&gt;If we run &lt;code&gt;npm run build&lt;/code&gt; now, we can see that there is a &lt;code&gt;dist&lt;/code&gt; folder created with our library output.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmm9b1m184q6ibpdwio2j.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmm9b1m184q6ibpdwio2j.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;cjs&lt;/code&gt; folder contains the &lt;code&gt;commonjs&lt;/code&gt; bundle and &lt;code&gt;esm&lt;/code&gt; folder contains modern &lt;code&gt;esmodules&lt;/code&gt; bundle. &lt;/p&gt;

&lt;p&gt;We have our own library which can now be published to the npm registry or used with other applications locally as well.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing it out
&lt;/h4&gt;

&lt;p&gt;We can test out our library locally by using &lt;a href="https://docs.npmjs.com/cli/v7/commands/npm-pack" rel="noopener noreferrer"&gt;npm pack&lt;/a&gt; or &lt;a href="https://docs.npmjs.com/cli/v7/commands/npm-link" rel="noopener noreferrer"&gt;npm link&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bundling types
&lt;/h3&gt;

&lt;p&gt;If you notice in our &lt;code&gt;dist&lt;/code&gt; folder after running &lt;code&gt;npm run build&lt;/code&gt;, we can see that we are not bundling our types. The advantage of using TS here is that code editors can pick up the types and provide Intellisense and static type-checking, which is super useful. It also reduces the need to look at documentation often.&lt;/p&gt;

&lt;p&gt;We need a add a few options in our &lt;code&gt;tsconfig.json&lt;/code&gt; to generate types.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"declaration": true,
"declarationDir": "types",
"emitDeclarationOnly": true


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

&lt;/div&gt;

&lt;p&gt;Adding this would add a types folder in our &lt;code&gt;cjs&lt;/code&gt; and &lt;code&gt;esm&lt;/code&gt; folders in &lt;code&gt;dist&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can further improve this by providing a single file which would contain all the types used in our library. For this, we are going to be using a Rollup plugin called &lt;a href="https://github.com/Swatinem/rollup-plugin-dts" rel="noopener noreferrer"&gt;rollup-plugin-dts&lt;/a&gt; which takes all our &lt;code&gt;.d.ts&lt;/code&gt; files and spits out a single types file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm i -D rollup-plugin-dts


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

&lt;/div&gt;

&lt;p&gt;We can add another entrypoint in our &lt;code&gt;rollup.config.js&lt;/code&gt; to add our types config.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
        input: 'dist/esm/types/index.d.ts',
        output: [{ file: 'dist/index.d.ts', format: "esm" }],
        external: [/\.css$/],
        plugins: [dts()],
},


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

&lt;/div&gt;

&lt;p&gt;What this does is take the &lt;code&gt;index.d.ts&lt;/code&gt; file from our esm bundle, parse through all the types file and generate one types file &lt;code&gt;index.d.ts&lt;/code&gt; inside our &lt;code&gt;dist&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Now we can add a &lt;code&gt;types&lt;/code&gt; entry in our &lt;code&gt;package.json&lt;/code&gt; &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"types": "dist/index.d.ts"


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

&lt;/div&gt;

&lt;p&gt;The entire &lt;code&gt;rollup.config.js&lt;/code&gt; looks like this now&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import dts from 'rollup-plugin-dts';

const packageJson = require('./package.json');

export default [
    {
        input: 'src/index.ts',
        output: [
            {
                file: packageJson.main,
                format: 'cjs',
                sourcemap: true,
                name: 'react-ts-lib'
            },
            {
                file: packageJson.module,
                format: 'esm',
                sourcemap: true
            }
        ],
        plugins: [
            external(),
            resolve(),
            commonjs(),
            typescript({ tsconfig: './tsconfig.json' }),
            postcss(),
            terser()
        ],
    },
    {
        input: 'dist/esm/types/index.d.ts',
        output: [{ file: 'dist/index.d.ts', format: "esm" }],
        external: [/\.css$/],
        plugins: [dts()],
    },
]



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

&lt;/div&gt;

&lt;p&gt;Now, if we use our library in other projects, code editors can pick up the types and provide Intellisense and type checking.&lt;/p&gt;

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

&lt;p&gt;This is by no means a comprehensive or perfect way to setup a component library. This is just a basic setup to get started and learn about bundling in the process. The next step in this process would be to add tests and tooling like &lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt; or &lt;a href="https://react-styleguidist.js.org/" rel="noopener noreferrer"&gt;Styleguidist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The source code can be found here &lt;a href="https://github.com/sidv93/react-ts-lib" rel="noopener noreferrer"&gt;react-ts-lib&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;br&gt;
Cheers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Cleaner data fetching with react-query</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Sat, 14 Aug 2021 20:04:41 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/cleaner-data-fetching-with-react-query-4klg</link>
      <guid>https://dev.to/siddharthvenkatesh/cleaner-data-fetching-with-react-query-4klg</guid>
      <description>&lt;p&gt;Data-fetching is something I feel does not get the importance and limelight as say state management in the React world. People often combine client state and server state into their state management solution. By server state, I mean the data from your backend servers. For example, in a redux setup, client state and data from server are stored in stores and updates are handled through actions and reducers. Client state change &lt;em&gt;usually&lt;/em&gt; causes a change in server state, so combining them makes sense in most cases. But I feel they are two separate entities and if handled properly, we can even get rid of client side state management solutions in some cases.&lt;/p&gt;

&lt;p&gt;I started looking at solutions to separate client and server state management. Coming into the React world from an Angular background, I wanted something simple as storing your server data in a service and inject it into your components, and you're good to go. In React, you'd have to maintain a global state management solution if the data you're fetching is needed in multiple components.&lt;/p&gt;

&lt;h3&gt;
  
  
  react-query to the rescue
&lt;/h3&gt;

&lt;p&gt;I then looked at libraries which performs data fetching and maintain server state. I stumbled up &lt;a href="https://github.com/tannerlinsley/react-query" rel="noopener noreferrer"&gt;react-query&lt;/a&gt; and boom! It had everything I needed and more. It provided a way of maintaining a global context of server state, and also provided an excellent caching solution with minimal configuration. There's also &lt;a href="https://github.com/vercel/swr" rel="noopener noreferrer"&gt;swr&lt;/a&gt; which is equally awesome.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Alright, enough chit-chat. Let's get to the code. Here's how I usually setup my React projects. I have a folder called &lt;code&gt;pages&lt;/code&gt; which has all the top level routes. &lt;code&gt;components&lt;/code&gt; folder houses all the UI components and a folder called &lt;code&gt;api&lt;/code&gt; which has all the server side APIs.&lt;/p&gt;

&lt;p&gt;Let's say we have a product entity. A product has all of CRUD operations attached to it. So, the following API calls need to be there in products&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Fetch all products
2. Fetch a specific product
3. Add a new product
4. Edit a product
5. Delete a product
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;react-query&lt;/code&gt; provides us with a &lt;code&gt;useQuery&lt;/code&gt; hook which we can use for all our queries. This should cover points 1 and 2 in the above list.&lt;br&gt;
We'll create our own data-fetching hooks for product by wrapping &lt;code&gt;useQuery&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our product file in &lt;code&gt;api/product.js&lt;/code&gt; looks something like this&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nhyr3c95kmwoyoswcpk.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nhyr3c95kmwoyoswcpk.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's go over how we setup functions for data-fetching with &lt;code&gt;react-query&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fetch products
&lt;/h4&gt;

&lt;p&gt;Let's start with fetching products. The barebones implementation with &lt;code&gt;useQuery&lt;/code&gt; would look like&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdgpz0tc7iaeda3bgfwn.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdgpz0tc7iaeda3bgfwn.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
 Not much going on here. We pass a unique id &lt;code&gt;key&lt;/code&gt; as the first argument to &lt;code&gt;useQuery&lt;/code&gt; and a fetch function to actually make the API call.&lt;/p&gt;

&lt;p&gt;If we want to use this in a component, we can do so like&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ramt3omdp5g38bnfbpz.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ramt3omdp5g38bnfbpz.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It gives us loading and error states which I think is neat&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have a working data-fetching setup, but the feature set doesn't end here. Any listing page would have additional features like &lt;code&gt;search&lt;/code&gt;, &lt;code&gt;filters&lt;/code&gt;, &lt;code&gt;pagination&lt;/code&gt; etc. This is where react-query makes it really simple to add these. Let's set these things up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pagination
&lt;/h3&gt;

&lt;p&gt;In our &lt;code&gt;Products&lt;/code&gt; component, we can have &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; values as state. &lt;code&gt;page&lt;/code&gt; denotes the current page number and &lt;code&gt;limit&lt;/code&gt; denotes the number of products to be displayed on the page.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcl89utwht8nxwxjj2w7.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcl89utwht8nxwxjj2w7.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step would be to hook this up with our &lt;code&gt;useFetchProducts&lt;/code&gt; hook. Lets make our &lt;code&gt;useFetchProducts&lt;/code&gt; hook take in &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; as arguments.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxql7s88mrwqlnfr9t0os.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxql7s88mrwqlnfr9t0os.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's unpack what's going on here. The &lt;code&gt;useFetchProducts&lt;/code&gt; hook now takes in &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; as arguments. It also adds these two to the &lt;code&gt;key&lt;/code&gt; and adds them to the fetch URL. &lt;/p&gt;

&lt;p&gt;Great! That's it. We now have our pagination implemented. Now, whenever the value of &lt;code&gt;page&lt;/code&gt; and &lt;code&gt;limit&lt;/code&gt; changes in our &lt;code&gt;Products&lt;/code&gt; component, react-query would automatically fire the API request and update the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search
&lt;/h3&gt;

&lt;p&gt;Another important common feature is search. So, lets add a search on &lt;code&gt;name&lt;/code&gt; field in products. As you might have guessed already, it is the exact same process as pagination. We'll have a &lt;code&gt;name&lt;/code&gt; field in state, and this state value would be passed to our &lt;code&gt;useFetchProducts&lt;/code&gt; hook.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbg6m84tcg3ui68664ntb.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbg6m84tcg3ui68664ntb.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;useFetchProducts&lt;/code&gt; will look something like this.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyo6l17agcz6gpedon4b.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyo6l17agcz6gpedon4b.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, we can hook any number of filtering/search parameters to out &lt;code&gt;useFetchProducts&lt;/code&gt; hook. This hook can be used across multiple components without any global state management system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;Caching is hands-down my favourite feature of react-query. With minimal code, we can setup a powerful caching system. In the case of our products example, let's say we want our products to be cached for 10 seconds. We can do this by adding the &lt;code&gt;staleTime&lt;/code&gt; option.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9bdpqobky2euglhbu7u.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9bdpqobky2euglhbu7u.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
This would use the data from the cache whenever this hook is called with the same &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An important thing to understand here is the &lt;code&gt;key&lt;/code&gt; option. The &lt;code&gt;key&lt;/code&gt; uniquely identifies a request. So, a request with &lt;code&gt;page&lt;/code&gt; as 1 is not the same as a request with &lt;code&gt;page&lt;/code&gt; as 2. The value from the cache will only be used all three key values are same.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Updating internal cache
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;react-query&lt;/code&gt; also give us access to its internal cache. We can update this cache whenever we want. What this means is, we can set internal cache values of individual products.&lt;/p&gt;

&lt;p&gt;Imagine this, we have fetched a list of products and displayed them on screen. The user clicks on a product, we take them to the product screen. In the product screen, we would have to fetch the product's details and display it. But! We already have the product's details in react-query's internal cache. What if we can use that instead of making an API call?&lt;/p&gt;

&lt;p&gt;Let's start with creating a &lt;code&gt;useFetchProduct&lt;/code&gt; hook for fetching individual product.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x5zscd3bce76gkac585.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x5zscd3bce76gkac585.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing crazy going on here. Pretty much the same thing we did before, just that it take &lt;code&gt;id&lt;/code&gt; as an argument. &lt;br&gt;
The important thing to note here is &lt;code&gt;['product', id]&lt;/code&gt; as the key. We are associating a product's &lt;code&gt;id&lt;/code&gt; as its key.&lt;/p&gt;

&lt;p&gt;Now to the interesting part, whenever we fetch the list of products, we set the internal cache with value of each individual product. &lt;code&gt;react-query&lt;/code&gt; exposes a &lt;code&gt;useQueryClient&lt;/code&gt; hook which gives us the internal cache.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsd1xrpi318bujrysyur.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsd1xrpi318bujrysyur.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whenever our list of products is successfully fetched, the &lt;code&gt;onSuccess&lt;/code&gt; function is called which has the API response it. We loop through each product and store it in the cache using the &lt;code&gt;setQueryData&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Now, whenever the user moves to an individual product's page from the products list page, the value from the cache will be used rather than making an API call.&lt;/p&gt;

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

&lt;p&gt;So, I found react-query to be extremely simple and powerful data-fetching solution. After using react-query, I even removed global state management solutions from some of my projects. Go give them some love on their &lt;a href="https://github.com/tannerlinsley/react-query" rel="noopener noreferrer"&gt;repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers! &lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>api</category>
    </item>
    <item>
      <title>Cleaner async-await for asynchronous JavaScript</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Fri, 13 Aug 2021 13:44:43 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/cleaner-async-await-for-asynchronous-javascript-42j0</link>
      <guid>https://dev.to/siddharthvenkatesh/cleaner-async-await-for-asynchronous-javascript-42j0</guid>
      <description>&lt;p&gt;So, there are thousands of articles floating around the internet about why callbacks are bad and you should be using Promises and async/await. As the popular saying goes, the answer to most of the opinions in world of programming is "&lt;strong&gt;It depends&lt;/strong&gt;". There is no one right solution for any problem. &lt;/p&gt;

&lt;p&gt;What I'm addressing here is a very simple problem. I need to run multiple async operations in a function, and I need the code to look clean and readable. I have a handler function of a POST request to create a new product. It is written in Express and does the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const createProduct = (req, res, next) =&amp;gt; {
    // Check if the user is valid
    // Check if the product name already exists
    // Check if the store exists
    // Save product to database
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Promise based approach
&lt;/h3&gt;

&lt;p&gt;A promise based approach would look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const createProduct = (req, res, next) =&amp;gt; {
    const { id: userId } = req.user;
    User.findOne({id: userId})
        .then((user) =&amp;gt; {
            if (!user) {
                console.log('User does not exist');
                return res.status(400).json({
                    status: 'error',
                    message: 'User does not exist',
                });
            }
            const { name, storeId, price } = req.body;
            Product.findOne({name})
                .then((product) =&amp;gt; {
                    if (product) {
                        console.log('Product with the same name already exists');
                        return res.status(400).json({
                            status: 'error',
                            message: 'Product with the same name already exists',
                        });
                    }
                    Store.findOne({id: storeId})
                        .then((store) =&amp;gt; {
                            if (!store) {
                                console.log('Store does not exist');
                                return res.status(400).json({
                                    status: 'error',
                                    message: 'Store does not exist',
                                })
                            }
                            // Valid product. Can be saved to db
                            const newProduct = new Product({
                                name,
                                storeId,
                                price,
                            });
                            newProduct.save()
                                .then(() =&amp;gt; {
                                    console.log('Product saved successfully');
                                    return res.status(200).json({
                                        status: 'success',
                                        message: 'Product saved successfully',
                                    });
                                })
                                .catch((err) =&amp;gt; {
                                    console.log('err');
                                    next(err);
                                })
                        })
                        .catch((err) =&amp;gt; {
                            console.log(err);
                            next(err);
                        })
                })
                .catch((err) =&amp;gt; {
                    console.log(err);
                    next(err);
                })
        })
        .catch((err) =&amp;gt; {
            console.log(err);
            next(err);
        })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Async-await based approach
&lt;/h3&gt;

&lt;p&gt;And if you convert this to a &lt;code&gt;async await&lt;/code&gt; based approach, you would end up with something very similar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const createProduct = async (req, res, next) =&amp;gt; {
    const { id: userId } = req.user;
    try {
        const user = await User.findOne({id: userId});
        if (!user) {
            console.log('User does not exist');
            return res.status(400).json({
                status: 'error',
                message: 'User does not exist',
            });
        }
        const { name, storeId, price } = req.body;
        try {
            const product = await Product.findOne({name});
            if (product) {
                console.log('Product with the same name already exists');
                return res.status(400).json({
                    status: 'error',
                    message: 'Product with the same name already exists',
                });
            }
            try {
                const store = await Store.findOne({id: storeId});
                if (!store) {
                    console.log('Store does not exist');
                    return res.status(400).json({
                        status: 'error',
                        message: 'Store does not exist',
                    })
                }
                try {
                    const newProduct = new Product({
                        name,
                        storeId,
                        price,
                    });
                    await newProduct.save();
                    console.log('Product saved successfully');
                    return res.status(200).json({
                        status: 'success',
                        message: 'Product saved successfully',
                    });
                } catch (err) {
                    console.log('Error when saving product', err);
                    next(err);
                }
            } catch (err) {
                console.log('Error when fetching store', err);
                next(err);
            }
        } catch (err) {
            console.log('Error when fetching product', err);
            next(err);
        }
    } catch (err) {
        console.log('Error when fetching user', err);
        next(err);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is nothing wrong with this approach and works pretty well for small functions. But when the number of async operations increase, the code goes into this pyramid structure which is hard to understand. Usually called the &lt;code&gt;Pyramid of doom&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linear async-await
&lt;/h3&gt;

&lt;p&gt;To overcome this and to give our code a linear structure, we can write a utility function which fires the promise and returns the error and success states.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const firePromise = (promise) =&amp;gt; {
    return promise
        .then((data) =&amp;gt; {
            return [null, data];
        })
        .catch((err) =&amp;gt; {
            return [err, null];
        })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can pass any async operation which returns a promise to this function and it will give us error and success states in an array. Goes something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [error, user] = await firePromise(User.findOne({id: userId}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can refactor our &lt;code&gt;createProduct&lt;/code&gt; handler to use our &lt;code&gt;firePromise&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const createProduct = async (req, res, next) =&amp;gt; {
    let error, user, product, store;
    const { id: userId } = req.user;
    try {
        [error, user] = await firePromise(User.findOne({id: userId}));
        if(error) {
            console.log('Error when fetching user', error);
            next(error);
        }
        if(!user) {
            console.log('User does not exist');
            return res.status(400).json({
                status: 'error',
                message: 'User does not exist',
            });
        }
        const { name, storeId, price } = req.body;
        [error, product] = await firePromise(Product.findOne({name}));
        if(error) {
            console.log('Error when fetching product', error);
            next(error);
        }
        if (product) {
            console.log('Product with the same name already exists');
            return res.status(400).json({
                status: 'error',
                message: 'Product with the same name already exists',
            });
        }
        [error, store] = await firePromise(Store.findOne({id: storeId}));
        if(error) {
            console.log('Error when fetching store', error);
            next(error);
        }
        if (!store) {
            console.log('Store does not exist');
            return res.status(400).json({
                status: 'error',
                message: 'Store does not exist',
            })
        }
        const newProduct = new Product({
            name,
            storeId,
            price,
        });
        [error] = await firePromise(newProduct.save());
        if (error) {
            console.log('Error when saving product', err);
            next(error);
        }
        console.log('Product saved successfully');
        return res.status(200).json({
            status: 'success',
            message: 'Product saved successfully',
        });
    } catch (err) {
        console.log('Unexpected error');
        next(err);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my opinion, this is much more readable because of its linear structure. This function can be used with any JS framework to write readable and maintainable async code.&lt;/p&gt;

&lt;p&gt;This was inspired by &lt;a href="https://github.com/scopsy/await-to-js"&gt;await-to-js&lt;/a&gt; library, and I use it in almost all my JS projects. Go give them a star.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>promises</category>
      <category>node</category>
    </item>
    <item>
      <title>Lazy loading react components with dynamic imports and intersection observer</title>
      <dc:creator>Siddharth Venkatesh</dc:creator>
      <pubDate>Fri, 13 Aug 2021 06:43:38 +0000</pubDate>
      <link>https://dev.to/siddharthvenkatesh/lazy-loading-react-components-with-dynamic-imports-and-intersection-observer-24mh</link>
      <guid>https://dev.to/siddharthvenkatesh/lazy-loading-react-components-with-dynamic-imports-and-intersection-observer-24mh</guid>
      <description>&lt;h3&gt;
  
  
  Lazy Loading
&lt;/h3&gt;

&lt;p&gt;Lazy loading is a way by which we can load content only when they are needed. This is achieved by code-splitting, where we split our app into multiple chunks. The idea here is to serve the user with only the content they can view, and serve the other contents as and when the user visits them. &lt;/p&gt;

&lt;h3&gt;
  
  
  Route based code-splitting
&lt;/h3&gt;

&lt;p&gt;For example, lets say we have a website where we have &lt;code&gt;/home&lt;/code&gt;, &lt;code&gt;/profile&lt;/code&gt; and &lt;code&gt;/about&lt;/code&gt; routes, and &lt;code&gt;/home&lt;/code&gt; is where the user first lands on. If we can compile the three routes into three bundles, we can serve them as and when the user visits the respective pages. The code for &lt;code&gt;home&lt;/code&gt; route would contain only the code in &lt;code&gt;&amp;lt;Home /&amp;gt;&lt;/code&gt; component. And when the user visits &lt;code&gt;/about&lt;/code&gt;, the content for this route will be downloaded and displayed. If we have a fairly large app with lots of routes, this would give us a significant performance gain on initial page load times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component based code-splitting
&lt;/h3&gt;

&lt;p&gt;The above example describes what is a route-based code-splitting strategy. We can take this a step further with a component based code-splitting strategy. Lets say we have a heavy form component buried deep in the app which the user would rarely use. It doesn't make sense for us to add it to our main bundle and it's a perfect recipe for lazy-loading. &lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic Imports
&lt;/h3&gt;

&lt;p&gt;We can achieve this in React using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports" rel="noopener noreferrer"&gt;Dynamic imports&lt;/a&gt;. React provides us way by which we can leverage dynamic imports with &lt;code&gt;React.lazy&lt;/code&gt; and &lt;code&gt;Suspense&lt;/code&gt; &lt;a href="https://reactjs.org/docs/code-splitting.html#reactlazy" rel="noopener noreferrer"&gt;From React docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lets build our example. We have a dummy form component &lt;code&gt;&amp;lt;HeavyForm /&amp;gt;&lt;/code&gt;. It does nothing, but you get the idea.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4n9djramledfcaramgz3.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4n9djramledfcaramgz3.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, if we want to dynamically import it, we would do something like this&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyy1jbv8zzl6p3mi1hjq.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmyy1jbv8zzl6p3mi1hjq.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intersection Observer
&lt;/h3&gt;

&lt;p&gt;If you run the code now, you can see the &lt;code&gt;HeavyForm&lt;/code&gt; is downloaded as a separate js file. This means that &lt;code&gt;HeavyForm&lt;/code&gt; was bundled as a separate chunk and it is not part of our main bundle. &lt;/p&gt;

&lt;p&gt;Great! But still, it is downloaded as soon as the page loads. We want this to be downloaded only when it is in the viewport, i.e when the user actually sees it. &lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver" rel="noopener noreferrer"&gt;Intersection Observer&lt;/a&gt; comes in. IntersectionObserver lets us know if the target element is in the viewport or not. We can safely assume that if the IntersectionObserver fires, the target element is in the viewport. We can leverage this and lazily load any component when it is in the viewport.&lt;/p&gt;

&lt;p&gt;I'm going to be using &lt;a href="https://github.com/thebuilder/react-intersection-observer" rel="noopener noreferrer"&gt;react-intersection-observer&lt;/a&gt; library, which uses native IntersectionObserver underneath and gives us neat hooks for ease of use.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53nc4rqom0c7vbnmz1u5.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53nc4rqom0c7vbnmz1u5.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what the complete implementation would look like with IntersectionObserver. &lt;code&gt;react-intersection-observer&lt;/code&gt; gives us &lt;code&gt;useInView&lt;/code&gt; hook, which gives us a &lt;code&gt;ref&lt;/code&gt; and &lt;code&gt;inView&lt;/code&gt; flag. The &lt;code&gt;ref&lt;/code&gt; should be attached to the target element and &lt;code&gt;inView&lt;/code&gt; lets us know if the target element is in the viewport. The &lt;code&gt;threshold&lt;/code&gt; option is a value between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; indicating the percentage of element that should be visible before triggering.&lt;/p&gt;

&lt;p&gt;Now, &lt;code&gt;&amp;lt;HeavyForm /&amp;gt;&lt;/code&gt; would only be downloaded when it is in the viewport.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This technique can be extended to multiple routes and components for easy gains on initial page load times. Remember to strike a balance between the components you lazy-load and components that are added to the main bundle. There is a penalty of network round trip that needs to be made when requesting lazy-loaded content. &lt;br&gt;
Cheers!&lt;/p&gt;

&lt;p&gt;You can take a look at the entire source code &lt;a href="https://github.com/sidv93/dynamic-import-with-intersection-observer" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
