<?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: Max Rozen</title>
    <description>The latest articles on DEV Community by Max Rozen (@rozenmd).</description>
    <link>https://dev.to/rozenmd</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%2F16888%2F7b28cd27-7753-43c9-9816-059055520bfb.jpg</url>
      <title>DEV Community: Max Rozen</title>
      <link>https://dev.to/rozenmd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rozenmd"/>
    <language>en</language>
    <item>
      <title>8 Months of OnlineOrNot: From MVP to Stable Product</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Wed, 03 Nov 2021 15:56:02 +0000</pubDate>
      <link>https://dev.to/rozenmd/8-months-of-onlineornot-from-mvp-to-stable-product-20ki</link>
      <guid>https://dev.to/rozenmd/8-months-of-onlineornot-from-mvp-to-stable-product-20ki</guid>
      <description>&lt;p&gt;Previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://onlineornot.com/building-saas-in-one-week-how-built-onlineornot"&gt;Building a SaaS in one week: How I built OnlineOrNot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onlineornot.com/two-months-on-how-onlineornot-is-going"&gt;Two months in: How the SaaS that was built in 7 days is going&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onlineornot.com/six-months-on-how-onlineornot-is-going"&gt;Six months in: How the SaaS that was built in 7 days is going&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;September and October were relatively quiet, so I thought I would write a single article for both months.&lt;/p&gt;

&lt;p&gt;While I'd normally try to write at least one useful article per month for OnlineOrNot's audience (as well as an update on how the business is going), I wrote no articles, and no code, actually. Instead, I packed up my life in Sydney, Australia, escaped lockdown, and relocated to France with my wife, and just enjoyed living for a while.&lt;/p&gt;

&lt;p&gt;It took just over a month to get over my jetlag, mild burnout from spending months in lockdown, and get back in the groove of working full-time and building OnlineOrNot in the hours before work.&lt;/p&gt;

&lt;p&gt;Massive bonus of living in Europe: most of my users are either in this timezone, or nearby (Hi North American friends!) so I'm no longer emailing them at 1am their time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;In October we experienced our first month of negative revenue growth, as of the start of November 2021, we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;419 (+68) users&lt;/li&gt;
&lt;li&gt;7 (-1) paying customers&lt;/li&gt;
&lt;li&gt;$127 (-$149) USD MRR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(numbers in brackets are relative to the &lt;a href="https://onlineornot.com/six-months-on-how-onlineornot-is-going"&gt;last update&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;You'll notice that a single customer represented more than half of OnlineOrNot's MRR. While it hurt to lose them as a customer, they didn't leave due to missing functionality. Their business helped OnlineOrNot scale rapidly, and I don't lose those gains. For example: from the efforts of keeping them as a customer, our backend scaled from only just handling a few dozen simultaneous Uptime Checks to almost fifty thousand simultaneous Uptime Checks.&lt;/p&gt;

&lt;p&gt;While I wish I could say it was beautiful to know I had a business running in the background that requires almost no input from me, it's still extremely early days, and OnlineOrNot's growth engine doesn't work if I'm not routinely writing articles, and interviewing potential customers to improve the product's roadmap.&lt;/p&gt;

&lt;p&gt;OnlineOrNot &lt;strong&gt;did&lt;/strong&gt; however manage to continue attracting users without my input thanks to previous marketing efforts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--djOTMX51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/eight-months-of-onlineornot/onlineornot-user-growth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--djOTMX51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/eight-months-of-onlineornot/onlineornot-user-growth.png" alt="OnlineOrNot User Growth" width="616" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's now a matter of converting free users to paid customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Since coming back to work, here's what I've released:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bumped a bunch of npm packages (gotta love the JavaScript ecosystem), reducing average frontend build time from 12 minutes to 2 (mainly thanks to the Next.js 12 release)&lt;/li&gt;
&lt;li&gt;Added a &lt;a href="https://onlineornot.com/changelog#discord-alerts"&gt;Discord integration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Migrated from x86 to ARM-based servers (decent performance boost)&lt;/li&gt;
&lt;li&gt;Made it possible for customers to select which of our &lt;a href="https://onlineornot.com/changelog#uptime-browser-checks-around-the-world"&gt;18 locations to monitor from&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Started tracking uptime check usage to allow for future pricing options&lt;/li&gt;
&lt;li&gt;Started tracking alerts sent for a future alert "cooldown" feature&lt;/li&gt;
&lt;li&gt;Made it possible for customers to remove their own Slack/Discord integrations (before this customers used to email me, and I would do this manually in the backend)&lt;/li&gt;
&lt;li&gt;Began to remove the "UI debt" that came with shipping OnlineOrNot in just seven days:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For context, OnlineOrNot is currently three services in one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uptime Checks for marketing websites (and soon APIs)&lt;/li&gt;
&lt;li&gt;Browser Checks for web apps (also known as synthetic checks)&lt;/li&gt;
&lt;li&gt;PageSpeed Checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I initially built the Uptime Check functionality for regular websites, and just kept adding fields:&lt;/p&gt;


&lt;br&gt;
  &lt;img alt="OnlineOrNot's UI Debt" src="https://res.cloudinary.com/practicaldev/image/fetch/s--4kluX1wd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/eight-months-of-onlineornot/ui-debt.jpeg" width="472" height="753"&gt;&lt;br&gt;
  

&lt;p&gt;I wound up in a situation similar to tech debt, that I call "UI debt". I would get requests for more features that I'd need to add to this form, while also handling support tickets from customers confused about what certain fields do.&lt;/p&gt;

&lt;p&gt;After a fair bit of customer feedback, I decided to redesign the Uptime Checks form to allow me to keep providing functionality for power users, while keeping the average use case simple:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LcrlKBU5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/eight-months-of-onlineornot/onlineornot-new-ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LcrlKBU5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/eight-months-of-onlineornot/onlineornot-new-ui.png" alt="OnlineOrNot new uptime check ui" width="880" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Losing half my MRR from a single customer made me realise I need to talk to more customers and potential customers to understand how I can better serve them with OnlineOrNot. I'm currently reading Michele Hansen's &lt;a href="https://deployempathy.com/"&gt;Deploy Empathy&lt;/a&gt; and have re-read Rob Fitzpatrick's &lt;a href="http://momtestbook.com/"&gt;The Mom Test&lt;/a&gt; to get better at this.&lt;/p&gt;

&lt;p&gt;In terms of product, I'm planning on extending the Uptime Checks feature to support monitoring APIs better (right now it just supports GET requests and simple body checks), and adding more chat integrations (like Microsoft Teams, and Telegram).&lt;/p&gt;

&lt;p&gt;In terms of marketing, I'm getting back in the groove of writing an article every two weeks on DevOps/SRE topics, which I'll also be releasing on this blog.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>showdev</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a SaaS in one week: How I built OnlineOrNot</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Sat, 06 Mar 2021 00:52:00 +0000</pubDate>
      <link>https://dev.to/rozenmd/building-a-saas-in-one-week-how-i-built-onlineornot-506d</link>
      <guid>https://dev.to/rozenmd/building-a-saas-in-one-week-how-i-built-onlineornot-506d</guid>
      <description>&lt;p&gt;(This is an article from OnlineOrNot.com. You can read the original by &lt;a href="https://onlineornot.com/building-saas-in-one-week-how-built-onlineornot"&gt;clicking here&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;When I first started building SaaS apps for side projects, it would take me a solid weekend solely to build the Stripe integration. These days, It's possible to build the whole SaaS app in one week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background Info&lt;/li&gt;
&lt;li&gt;Tech Stack&lt;/li&gt;
&lt;li&gt;Approach&lt;/li&gt;
&lt;li&gt;
Building the SaaS

&lt;ul&gt;
&lt;li&gt;First, a blog&lt;/li&gt;
&lt;li&gt;Uptime Checker Test&lt;/li&gt;
&lt;li&gt;On Auth&lt;/li&gt;
&lt;li&gt;Using Tailwind for UI&lt;/li&gt;
&lt;li&gt;On SSR and Emotion&lt;/li&gt;
&lt;li&gt;Forms!&lt;/li&gt;
&lt;li&gt;Latency troubles&lt;/li&gt;
&lt;li&gt;Docs&lt;/li&gt;
&lt;li&gt;Stripe integration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Shipped!&lt;/li&gt;
&lt;li&gt;Where to next?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Background Info
&lt;/h2&gt;

&lt;p&gt;Let's start with some background about the app: OnlineOrNot is a pretty standard uptime checker - it does have some fancy logic to ensure the page is &lt;em&gt;actually&lt;/em&gt; down before notifying you, but essentially it's a login/sign-up page, a dashboard, a "new page" form, a checker service, a transactional email integration, and a payment integration. I built a similar tool in 2018 (under the same name) to snapshot test GraphQL queries, but that project never took off.&lt;/p&gt;

&lt;p&gt;I recently &lt;a href="https://maxrozen.com/walkthrough-migrating-maxrozen-com-gatsby-to-nextjs"&gt;migrated my blog from Gatsby to Next.js&lt;/a&gt;, and that got me thinking - How much effort could it possibly be to use Next.js as the core of a SaaS?&lt;/p&gt;

&lt;p&gt;I decided to do a quick test, and built a webpage to manually check if any URL is available, in a single afternoon (it's now part of the landing page at &lt;a href="https://onlineornot.com/"&gt;OnlineOrNot.com&lt;/a&gt;). I was impressed by how seamlessly Next.js blended together React, server-side rendering, and API routes - compared to my regular approach of using Gatsby.js as a landing page, create-react-app as the SaaS app, and a ton of AWS Lambda functions to handle the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;p&gt;Keeping in mind that I prefer to &lt;a href="https://mcfunley.com/choose-boring-technology"&gt;choose boring technology&lt;/a&gt; - I could have used DynamoDB/MongoDB to keep running costs as low as possible, but I opted for a relational data model.&lt;/p&gt;

&lt;p&gt;Anyway, here's the tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core app: Next.js, hosted on &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;UI/CSS framework: &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Auth: &lt;a href="https://next-auth.js.org/"&gt;NextAuth.js&lt;/a&gt; - a Next.js library&lt;/li&gt;
&lt;li&gt;Backend: GraphQL + Node.js, written in TypeScript, sitting on a &lt;a href="https://nextjs.org/docs/api-routes/introduction"&gt;Next.js API route&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Database: Postgres&lt;/li&gt;
&lt;li&gt;Worker functions: Node.js/TypeScript, hosted on AWS Lambda&lt;/li&gt;
&lt;li&gt;Payment integration: &lt;a href="https://stripe.com"&gt;Stripe&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Transactional Emails: &lt;a href="https://www.mailgun.com/"&gt;Mailgun&lt;/a&gt; - I'd normally prefer to use &lt;a href="https://postmarkapp.com/"&gt;Postmark&lt;/a&gt;, but didn't want to blocked from shipping by having to wait around to get verified&lt;/li&gt;
&lt;li&gt;Newsletter Emails: &lt;a href="https://app.convertkit.com/referrals/l/4f6b4767-e189-42b0-b0eb-d171435c247d"&gt;Convertkit&lt;/a&gt; (referral link)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;p&gt;My work hours for my employer are 9am to 5pm, I tend to wake up at 7am, and go to bed around 11pm. If I gave 100% of my own time to this build, I would have had around 40 hours to work with.&lt;/p&gt;

&lt;p&gt;In this particular week, I still either went to the gym, or ran each day, watched some Netflix, had dinner, and went out one night, so I had roughly 20 hours to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the SaaS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  First, a blog
&lt;/h3&gt;

&lt;p&gt;To begin with, OnlineOrNot started its life as a blog. In particular, I cloned this example: &lt;a href="https://github.com/vercel/next.js/tree/canary/examples/blog-starter-typescript"&gt;blog-starter-typescript&lt;/a&gt; from the Next.js repo.&lt;/p&gt;

&lt;p&gt;To get most of the blog functionality I wanted (sitemap, RSS feed, and code snippets in particular), I followed &lt;a href="https://maxrozen.com/walkthrough-migrating-maxrozen-com-gatsby-to-nextjs#first-steps-with-nextjs"&gt;these steps&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Uptime Checker Test
&lt;/h3&gt;

&lt;p&gt;With the blog running smoothly, I built an additional page to manually check uptime (the form you see at &lt;a href="https://onlineornot.com"&gt;OnlineOrNot.com&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;To get technical with you, the form submits data to a page living under &lt;code&gt;status/[...url].tsx&lt;/code&gt; in my Next.js pages folder. Once it gets a request, it sends off a request to an &lt;a href="https://nextjs.org/docs/api-routes/introduction"&gt;API route&lt;/a&gt;, which then sends the request to one of ten serverless functions deployed around the world (I had to use AWS Lambda for this part).&lt;/p&gt;

&lt;p&gt;Seeing app-like functionality work so seamlessly on a blog is what inspired me to see how long it would take me to build a whole SaaS around the AWS Lambda function I wrote.&lt;/p&gt;

&lt;h3&gt;
  
  
  On Auth
&lt;/h3&gt;

&lt;p&gt;For auth I initially wanted to use Passport, and try out Max Stoiber's recently released &lt;a href="https://github.com/mxstbr/passport-magic-login"&gt;passport-magic-login&lt;/a&gt;, but I didn't enjoy the user experience of logging into an app with your email only, checking your email, clicking a link, then having to find the original tab to be able to use the app.&lt;/p&gt;

&lt;p&gt;After a bit of searching, I came across &lt;a href="https://next-auth.js.org/"&gt;NextAuth.js&lt;/a&gt;, which boasted similar functionality (and heaps more Auth Providers supported), with a bit more polish (to be fair, it's been around longer). It only took me a couple of hours to have both email-only login, and Google Auth0 login working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Tailwind for UI
&lt;/h3&gt;

&lt;p&gt;My typical approach to building UI rapidly would still be to use Bootstrap. In React, I opt for the &lt;a href="https://reactstrap.github.io/"&gt;Reactstrap library&lt;/a&gt;. It's never going to win me any design awards, but it helps me get the job done fast.&lt;/p&gt;

&lt;p&gt;Since rewriting my own blog from Gatsby to Next.js, I've started writing my CSS using Tailwind utility classes. I find that whereas with Reactstrap I would use the default Bootstrap styles, then manually override each component, Tailwind has me thinking "How can I make this a component I can re-use?" more often, and it makes the code feel cleaner as a result.&lt;/p&gt;

&lt;p&gt;To make my use of Tailwind CSS even faster, I paid for &lt;a href="https://tailwindui.com/"&gt;Tailwind UI&lt;/a&gt;, which has removed the "oh crap, where do I even start?" step for me when working on the frontend. It's kind of like paying for a designer to tell you "here's what screens like this tend to look like" - I highly recommend it.&lt;/p&gt;

&lt;h3&gt;
  
  
  On SSR and Emotion
&lt;/h3&gt;

&lt;p&gt;One pitfall I noticed with server-side rendering (SSR) and Emotion, is that some of the Tailwind classes use the &lt;code&gt;:first-child&lt;/code&gt; selector, and Emotion's SSR support (a CSS in JS library) extracts its CSS to be the first sibling of your component.&lt;/p&gt;

&lt;p&gt;As a result, your pages tend to flicker on first load. The fix is simple enough - in Next.js you need to extract the critical styles into the head of the page (I followed the instructions &lt;a href="https://github.com/ben-rogerson/twin.examples/tree/master/next-emotion#extract-styling-on-server-optional"&gt;here&lt;/a&gt; since I used &lt;a href="https://github.com/ben-rogerson/twin.macro"&gt;twin.macro&lt;/a&gt; to use Tailwind with Emotion).&lt;/p&gt;

&lt;h3&gt;
  
  
  Forms!
&lt;/h3&gt;

&lt;p&gt;I used to hate building forms in React until I came across &lt;a href="https://react-hook-form.com/"&gt;React Hook Form&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Importing the hook, passing &lt;code&gt;ref={register({ required: true })}&lt;/code&gt; to a few of my inputs, and implementing a submit handler is all it takes to build awesome forms in minutes these days (though it helps that I've spent a couple of years building forms in React).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PbLzykkK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/building-saas-in-one-week-how-built-onlineornot/onlineornot-add-page.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PbLzykkK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/building-saas-in-one-week-how-built-onlineornot/onlineornot-add-page.jpeg" alt="OnlineOrNot Add Page Form" width="880" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the user submits the form, they get to see their page being monitored:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--85OGqTuZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/building-saas-in-one-week-how-built-onlineornot/onlineornot-your-pages.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--85OGqTuZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/building-saas-in-one-week-how-built-onlineornot/onlineornot-your-pages.jpg" alt="OnlineOrNot Your Pages List" width="880" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Latency troubles
&lt;/h3&gt;

&lt;p&gt;Two days into building the SaaS with login/signup working, and it being possible to add pages to monitor, I decided to ship a v0.0.1 for friends to check out. I'm very glad I did, because I immediately noticed doing almost anything on the production build of the site took 3-6 seconds per click.&lt;/p&gt;

&lt;p&gt;I eventually realised that the issue was caused by where I host my database. I typically host my Postgres database in AWS's ap-southeast-2 (Sydney, Australia) region, and Vercel hosts their functions somewhere on the US East Coast - probably in AWS's us-east-1 region.&lt;/p&gt;

&lt;p&gt;So each request would hit their CDN, travel half-way across the world to the US, travel half-way across the world back to Sydney, Australia, and back to the US to finish the request.&lt;/p&gt;

&lt;p&gt;Shutting down my Sydney database and spinning up a new one in AWS's us-east-1 region greatly reduced the latency issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docs
&lt;/h3&gt;

&lt;p&gt;Before launching, I knew I needed documentation - even though initially I was just building OnlineOrNot as a test to see how much effort is involved in building a SaaS in 2021, as a developer, nothing pisses me off more than crap/incomplete/missing documentation.&lt;/p&gt;

&lt;p&gt;I followed the instructions at &lt;a href="https://v2.docusaurus.io/"&gt;Docusaurus' homepage&lt;/a&gt;, spun up a quick site in Vercel, and mounted it onto the /docs path of OnlineOrNot - a pretty nifty &lt;a href="https://vercel.com/docs/configuration#routes"&gt;Vercel feature&lt;/a&gt; that I loved from AWS CloudFront.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stripe integration
&lt;/h3&gt;

&lt;p&gt;Integrating Stripe's Checkout requires adding products in the Stripe dashboard, and following the &lt;a href="https://stripe.com/docs/payments/checkout"&gt;docs&lt;/a&gt; - there are also examples to follow.&lt;/p&gt;

&lt;p&gt;Since I implemented my last Stripe integration, Stripe launched &lt;a href="https://stripe.com/en-au/billing"&gt;Billing&lt;/a&gt; - greatly reducing the amount of effort required to let users upgrade, change, or cancel their subscriptions.&lt;/p&gt;

&lt;p&gt;One of my employer's core values is "Don't f**k the customer", and I &lt;em&gt;strongly&lt;/em&gt; identify with it, carrying it with me even when building side-projects. So I knew I had to have Stripe Billing to let my customers cancel at any time.&lt;/p&gt;

&lt;p&gt;Rather than build the whole integration myself, I paid for &lt;a href="https://divjoy.com/?via=oon"&gt;Divjoy&lt;/a&gt; (referral link), which conveniently includes a Stripe integration (with Checkout, Billing, and a webhook endpoint). While I only wanted the Billing portion of the codebase, it was money well-spent - it gave me ideas to refactor the integration I built by following the Stripe docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipped!
&lt;/h2&gt;

&lt;p&gt;With the Stripe integration built and tested, I deployed everything, and here we are - one week after I started toying around with a simple manual uptime checker in Next.js, I have a SaaS with login/sign-up, an app that tracks page uptime, and a way to pay for it (there's also a &lt;a href="https://onlineornot.com/docs/free-tier"&gt;free tier&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to next?
&lt;/h2&gt;

&lt;p&gt;Glad you asked!&lt;/p&gt;

&lt;p&gt;I keep a &lt;a href="https://onlineornot.com/changelog"&gt;changelog&lt;/a&gt; and a &lt;a href="https://onlineornot.com/docs/roadmap"&gt;product roadmap&lt;/a&gt; tracking what's on the radar, what's coming soon, and what's released - I intend to continue shipping features for OnlineOrNot, and continue running it as part of my side-business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to give OnlineOrNot a go?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Get started on the free tier, with 10 reliable monitors, and email notifications - no credit card required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://onlineornot.com/get-started"&gt;Get started for free&lt;/a&gt;&lt;/p&gt;

</description>
      <category>saas</category>
      <category>react</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Guidelines to help you avoid losing your domain</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Sat, 30 Jan 2021 00:52:00 +0000</pubDate>
      <link>https://dev.to/rozenmd/guidelines-to-help-you-avoid-losing-your-domain-466k</link>
      <guid>https://dev.to/rozenmd/guidelines-to-help-you-avoid-losing-your-domain-466k</guid>
      <description>&lt;p&gt;(This is an article from OnlineOrNot.com. You can read the original by &lt;a href="https://onlineornot.com/articles/guidelines-to-help-avoid-losing-your-domain"&gt;clicking here&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Imagine you're sitting in your office, and you start noticing emails coming in asking if you'd like to buy your domain.&lt;/p&gt;

&lt;p&gt;"Huh, that's weird, I already own that domain" you think to yourself.&lt;/p&gt;

&lt;p&gt;A few more emails come in, and they're getting past the spam filter, so you decide to double check your domain manager. Doubt starts creeping into your mind, you start &lt;em&gt;panicking&lt;/em&gt;, and you frantically scroll down to where the domain should be, and...&lt;/p&gt;

&lt;p&gt;It's gone.&lt;/p&gt;

&lt;p&gt;The only option you have is to pay the person that grabbed your domain $3000 USD.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hold up, rewind...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This sort of scenario can be avoided, yet an entire industry of domain squatters exists due to how commonly it occurs.&lt;/p&gt;

&lt;p&gt;In this article, I'll provide advice you can do &lt;strong&gt;today&lt;/strong&gt; to keep your domain secure in the long run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable 2FA&lt;/li&gt;
&lt;li&gt;Check that your domain is set to auto renew&lt;/li&gt;
&lt;li&gt;Lock your domain from transfer&lt;/li&gt;
&lt;li&gt;Check your payment details&lt;/li&gt;
&lt;li&gt;Use a reputable domain registrar&lt;/li&gt;
&lt;li&gt;Be sure you own your domain&lt;/li&gt;
&lt;li&gt;Extend your domain registration&lt;/li&gt;
&lt;li&gt;Be aware of any TLD-specific rules around renewals&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enable 2FA
&lt;/h2&gt;

&lt;p&gt;If your domain registrar supports it, enable 2FA (two-factor authentication, also known as MFA/multi-factor authentication). It'll send you an email/SMS/push notification when logging into your domain manager.&lt;/p&gt;

&lt;p&gt;While not a foolproof way of stopping hackers, it'll slow them down &lt;strong&gt;and&lt;/strong&gt; alert you if your account has been compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check that your domain is set to auto renew
&lt;/h2&gt;

&lt;p&gt;Some domain registrars don't enable auto renew by default, particularly when transferring domains.&lt;/p&gt;

&lt;p&gt;Check your domain manager to see that it's enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lock your domain from transfer
&lt;/h2&gt;

&lt;p&gt;While you're checking your domain manager has auto renewal enabled, also double check that your domain's "transfer lock" is also enabled.&lt;/p&gt;

&lt;p&gt;Enabling transfer lock for your domain is effectively like a car alarm for your domain. If someone manages to get into your domain manager account, and tries to transfer the domain, you'll receive quite a few emails about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your payment details
&lt;/h2&gt;

&lt;p&gt;I know this one sounds obvious, but if the payment fails, your domain doesn't get renewed.&lt;/p&gt;

&lt;p&gt;The most common mistake is that your credit card expires, and you forget to update the payment details your domain registrar has on file.&lt;/p&gt;

&lt;p&gt;On the off-chance your domain registrar accepts Paypal (or similar), also double check that the payment details that they have are also up to date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a reputable domain registrar
&lt;/h2&gt;

&lt;p&gt;There are a few ways to interpret "reputable" - I mean large companies trust them with their services, and the business itself is trustworthy. Certain domain registrars also own companies that &lt;a href="https://en.wikipedia.org/wiki/Domain_drop_catching"&gt;"drop catch"&lt;/a&gt; domains that expire from their services. Would &lt;strong&gt;you&lt;/strong&gt; want to use a domain registrar that's financially incentivised to let your domain expire?&lt;/p&gt;

&lt;p&gt;Here are some reputable domain registrars that immediately come to mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS

&lt;ul&gt;
&lt;li&gt;Some of the largest internet companies trust AWS to host their services&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;NameCheap

&lt;ul&gt;
&lt;li&gt;Decent reputation, has been around a very long time, used to make you pay for privacy, now doesn't&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Gandi

&lt;ul&gt;
&lt;li&gt;Decent reputation, has been around a very long time&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Google Domains

&lt;ul&gt;
&lt;li&gt;I hesitated to put this one on the list. Google has a reputation for &lt;a href="https://killedbygoogle.com/"&gt;killing products&lt;/a&gt; once it realises it won't be a billion dollar business. Despite this, I've heard Google's Support has been slowly improving over time.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;I've personally had negative experiences with GoDaddy and CrazyDomains (in Australia), and would strongly recommend to anyone reading this: transfer your domain ASAP to somewhere like AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be sure you own your domain
&lt;/h2&gt;

&lt;p&gt;If you purchased your domain through a third-party, like Wix, WordPress, or maybe the agency or contractor that helped build your site, chances are you're not fully in control of your domain.&lt;/p&gt;

&lt;p&gt;Sure, it might be easier for you to have them manage the domain for you, and pass the bill along each year, however this sort of arrangement can become problematic when you want to cancel the service, or move to another provider.&lt;/p&gt;

&lt;p&gt;If you're having an agency or contractors build your site for you, and they become unresponsive, you risk losing the domain if you also let them manage it for you.&lt;/p&gt;

&lt;p&gt;By keeping the login to your domain manager to yourself, you can cut ties with rogue third-parties and move to a different provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extend your domain registration
&lt;/h2&gt;

&lt;p&gt;Most domain registrars will let you extend your registration for around $12 USD per year for .com domains. This lets you remove the risk of your automatic renewal not going through by manually renewing.&lt;/p&gt;

&lt;p&gt;For example, AWS offers the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--23sQe1Tt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/guidelines-to-help-avoid-losing-your-domain/extend-domain-registration.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--23sQe1Tt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onlineornot.com/assets/guidelines-to-help-avoid-losing-your-domain/extend-domain-registration.png" alt="AWS Extend Domain Registration" width="611" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Considering the amount of money domain squatters will try to get from you if you let your domain expire, it's a pretty good deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be aware of any TLD-specific rules around renewals
&lt;/h2&gt;

&lt;p&gt;While it's great fun to grab a domain from a country half way across the world from you so you can spell out your brand, different countries have different rules around domain renewals.&lt;/p&gt;

&lt;p&gt;As an example, Spain (.es) charges a renewal fee on top of an annual fee. As well as that, if you let the domain expire, there's another renewal fee that ranges from 30 USD to hundreds of dollars (depending on your registrar).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Got any tips I'm missing?&lt;/strong&gt; Feel free to &lt;a href="https://twitter.com/RozenMD"&gt;tweet them at me&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Indiehacking: a review of my 3rd year</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Tue, 22 Dec 2020 09:48:48 +0000</pubDate>
      <link>https://dev.to/rozenmd/indiehacking-a-review-of-my-3rd-year-1njp</link>
      <guid>https://dev.to/rozenmd/indiehacking-a-review-of-my-3rd-year-1njp</guid>
      <description>&lt;p&gt;Each year I take this time of year to reflect on how I progressed towards my goal of being self-sufficient as a web developer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://maxrozen.com/2018/12/31/2018-review-starting-an-internet-business/"&gt;2018: Reflections on trying to start an internet business&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maxrozen.com/2019/12/29/2019-further-reflections-trying-to-start-an-internet-business/"&gt;2019: Further reflections on trying to start an internet business&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This year is no different, even if it &lt;em&gt;was&lt;/em&gt; a total mess.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Table of contents&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How I Indiehack&lt;/li&gt;
&lt;li&gt;Investing in tooling&lt;/li&gt;
&lt;li&gt;Books I Read&lt;/li&gt;
&lt;li&gt;
Year in review

&lt;ul&gt;
&lt;li&gt;Mercenary for hire&lt;/li&gt;
&lt;li&gt;Day-rate contracting&lt;/li&gt;
&lt;li&gt;Startup Tech Stack&lt;/li&gt;
&lt;li&gt;PerfBeacon on the side&lt;/li&gt;
&lt;li&gt;Back into stable full-time work&lt;/li&gt;
&lt;li&gt;Trade-offs, trade-offs everywhere&lt;/li&gt;
&lt;li&gt;Writing weekly&lt;/li&gt;
&lt;li&gt;Taking notes&lt;/li&gt;
&lt;li&gt;Writing a book&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Financials&lt;/li&gt;
&lt;li&gt;Learnings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How I Indiehack
&lt;/h2&gt;

&lt;p&gt;To save you reading through the previous year's summaries, here's how I indiehack.&lt;/p&gt;

&lt;p&gt;I try to keep either a full-time job that doesn't expect me to work 18 hour days to meet tough deadlines, or I take contract/day-rate jobs where I can negotiate to work 4 days a week.&lt;/p&gt;

&lt;p&gt;Before and after business hours, and occasionally on weekends, I work on side-projects.&lt;/p&gt;

&lt;p&gt;I tend to work in 1-2 hour increments, partly because that's how long I can focus at a time, and partly because I prefer to chip away at a problem, rather than making one big push.&lt;/p&gt;

&lt;p&gt;I started the year with the firm belief that running your own one-person SaaS was the only means of starting your own business as a developer. I now see that SaaS is just one type of fix, in a spectrum of fixes - from books/videos/workshops to consulting, to SaaS and other products.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investing in tooling
&lt;/h2&gt;

&lt;p&gt;I'm not a big fan of re-inventing the wheel when I build my side-projects, so in previous years I invested time in building my own personal framework to spin up SaaS-like solutions.&lt;/p&gt;

&lt;p&gt;This time around, instead of investing time, I spent a few hundred dollars on licenses for &lt;a href="https://tailwindui.com/"&gt;Tailwind UI&lt;/a&gt; and &lt;a href="https://divjoy.com/"&gt;Divjoy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I still maintain a terraform config for spinning up the required infrastructure for my projects, but now I don't need to think about styling my side-projects, or fiddly integrations like Auth0 and Stripe.&lt;/p&gt;

&lt;p&gt;I was able to productionise a web app for one of my projects this year within a couple of hours thanks to these investments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Books I Read
&lt;/h2&gt;

&lt;p&gt;Until this year, I rarely sat down to read books. Instead, I'd use my commute time to work to listen to audiobooks. As a result, when 2020 happened and I stopped commuting, I started having to make time to read, and as a result of &lt;em&gt;that&lt;/em&gt;, I was much more impatient with boring or long-winded books.&lt;/p&gt;

&lt;p&gt;Here are the ones that made the cut:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is Marketing - Seth Godin&lt;/li&gt;
&lt;li&gt;How I Built This - Guy Raz&lt;/li&gt;
&lt;li&gt;How to Take Smart Notes - Sönke Ahrens&lt;/li&gt;
&lt;li&gt;Originals - Adam Grant&lt;/li&gt;
&lt;li&gt;Tribes - Seth Godin&lt;/li&gt;
&lt;li&gt;Obviously Awesome - April Dunford&lt;/li&gt;
&lt;li&gt;The Tipping Point - Malcolm Gladwell&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Year in review
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mercenary for hire
&lt;/h3&gt;

&lt;p&gt;I started the year completely burnt out from working for companies that claimed to have certain values, while exhibiting behaviour that ran counter to those values.&lt;/p&gt;

&lt;p&gt;So I started looking for React gigs where I could jump in, either fix or build a React app, and jump out as the client needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Day-rate contracting
&lt;/h3&gt;

&lt;p&gt;At the end of 2019 I complained across my network long enough that I managed to find myself working as a contractor for a scrappy startup building an entire IoT platform (from devices up to a frontend to display data). Thankfully they had hired a consulting company to handle the IoT device and data ingestion, so I only needed to worry about the web platform.&lt;/p&gt;

&lt;p&gt;What started off as a couple of weeks to help the company get to demo day ended up being a few months helping build the company's entire tech stack.&lt;/p&gt;

&lt;p&gt;As a result, overnight I unofficially became the:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product Manager&lt;/li&gt;
&lt;li&gt;Frontend Dev&lt;/li&gt;
&lt;li&gt;Backend Dev&lt;/li&gt;
&lt;li&gt;Data Engineer&lt;/li&gt;
&lt;li&gt;SRE&lt;/li&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and it was surprisingly doable, with the right tech stack (Spoiler: I'm a huge fan of &lt;a href="http://boringtechnology.club/"&gt;Choose Boring Technology&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Startup Tech Stack
&lt;/h3&gt;

&lt;p&gt;I opted for the same tech stack I know and love for my side projects, and it worked quite well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For managing tasks/documentation I used &lt;a href="https://www.atlassian.com/software/jira"&gt;Jira&lt;/a&gt; and &lt;a href="https://www.atlassian.com/software/confluence"&gt;Confluence&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;for the frontend I used React via &lt;a href="https://create-react-app.dev/"&gt;create-react-app&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;for the backend I used postgres with a node GraphQL server,&lt;/li&gt;
&lt;li&gt;for Infrastructure as Code (IaC) I used &lt;a href="https://www.terraform.io/"&gt;terraform&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;and for CI/CD I used &lt;a href="https://circleci.com/"&gt;CircleCI&lt;/a&gt; (though these days I'd probably look at using GitHub Actions).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the data engineering side of things, we would regularly need to transform ad-hoc user-provided data into a format that our system could understand. Essentially, clients would already have IoT devices from competitors, and we would make it possible for them to join our platform via our data engineering efforts.&lt;/p&gt;

&lt;p&gt;I was able to automate most of this using Python and AWS S3's event system, so we wouldn't need to wrangle the client's custom datasets more than once.&lt;/p&gt;

&lt;p&gt;Side note: it blew my mind how incumbent companies can still charge $10k+ for systems built more than 20 years ago, that these days could be replaced by an ESP32, wifi/3g modem and a python script. Though being able to compete with these companies requires a large professional network and willingness to run high-touch sales that I don't have.&lt;/p&gt;

&lt;h3&gt;
  
  
  PerfBeacon on the side
&lt;/h3&gt;

&lt;p&gt;While contracting four days a week, I would also work on &lt;a href="https://perfbeacon.com/"&gt;PerfBeacon&lt;/a&gt; before and after work, as well as on Wednesdays. For those that don't know, PerfBeacon helps you keep your app/site fast by automating &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Google Lighthouse&lt;/a&gt; checks.&lt;/p&gt;

&lt;p&gt;I managed to finish building the minimum viable product early this year, and started marketing and sales.&lt;/p&gt;

&lt;p&gt;After launching PerfBeacon to the world, I built a quick CRM in Airtable, and started selling it to people in my network. I estimate I spent around 40 hours trying to sell PerfBeacon to my network directly, netting approximately zero in sales.&lt;/p&gt;

&lt;p&gt;It didn't take long for me to realise that most of running a side business is marketing, rather than product building. Realising I was terrible at marketing, and didn't really know what I was doing lead me to pay for &lt;a href="https://30x500.com/academy/"&gt;30x500&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I still run PerfBeacon, it only takes a couple of hours a month on average to keep it running.&lt;/p&gt;

&lt;p&gt;Funnily enough, while job hunting this year I ran into some resistance from startups "wanting me to be 100% focused, even outside of work hours on their project". In other words, I would have to drop my side-projects to be able to work for them. Often within minutes of telling me my "vast experience with React" made my profile interesting (I wouldn't have a vast experience in React without side-projects).&lt;/p&gt;

&lt;p&gt;Total non-starter, so I kept looking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Back into stable full-time work
&lt;/h3&gt;

&lt;p&gt;Effectively doing any one of six jobs each day grew old surprisingly quickly, and by February I started to look for long-term contracts at more established companies.&lt;/p&gt;

&lt;p&gt;By chance I decided to reach out to a recruiter from Atlassian that ran me through the interview process a year prior, and I managed to get another run through the interview process to be a frontend developer on the Growth team at Atlassian.&lt;/p&gt;

&lt;p&gt;I ended up getting the job, and it's been fantastic.&lt;/p&gt;

&lt;h4&gt;
  
  
  Trade-offs, trade-offs everywhere
&lt;/h4&gt;

&lt;p&gt;Working at a huge corporation like Atlassian has made me appreciate trade-offs much more than any small company ever did.&lt;/p&gt;

&lt;p&gt;We regularly run through &lt;a href="https://www.atlassian.com/team-playbook/plays/daci"&gt;DACIs&lt;/a&gt; (basically a fancy RFC) when making technical decisions. Where before I'd run a technical decision past an architect/CTO, I'm now able to tap into our entire department's expertise to get more context, and see any gaps in what I'm trying to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing weekly
&lt;/h3&gt;

&lt;p&gt;I started the year only writing whenever I got inspired (basically, after something made me angry enough to write about it). While cathartic, this didn't do much for people who read my blog.&lt;/p&gt;

&lt;p&gt;Instead of focusing on things that annoyed me, in July I decided to focus on regularly helping people. I knew quite a bit about React from using it in production for several years (both on side-projects and at work), so it'd be a shame not to share the knowledge.&lt;/p&gt;

&lt;p&gt;Since starting to blog more regularly, I went from an average of 20 readers on my blog per day, to about 140 readers per day on days when I don't post my articles anywhere, up to 2500 a day when I do share my articles.&lt;/p&gt;

&lt;h4&gt;
  
  
  Taking notes
&lt;/h4&gt;

&lt;p&gt;Writing weekly has become easier as I started to treat my articles like "semi-permanent notes" to myself about certain topics in React.&lt;/p&gt;

&lt;p&gt;I tend to take notes (whether in a notebook, on my phone, or on scrap pieces of paper) from wherever I see people discussing React (Twitter threads, reddit posts, etc) and eventually type them up into an article. Eventually, I revise the article as I find new information. A good example of this is &lt;a href="https://maxrozen.com/keeping-up-with-react-libraries/"&gt;Keeping up with React Libraries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I learned this process after reading &lt;a href="https://www.amazon.com/gp/product/1542866502/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1542866502&amp;amp;linkCode=as2&amp;amp;tag=rozenmd-20&amp;amp;linkId=a766abac80f4e8d5f39e5d87804ec00c"&gt;How to Take Smart Notes&lt;/a&gt; by Sönke Ahrens, which I'd highly recommend to aspiring writers out there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing a book
&lt;/h3&gt;

&lt;p&gt;After writing consistently for a few months, I started to notice the articles that would get significantly more traffic and links than others.&lt;/p&gt;

&lt;p&gt;In the end, I decided to write a book to try help people more than a few blog posts ever could. It's called &lt;a href="http://useeffectbyexample.com/"&gt;useEffect By Example&lt;/a&gt;, and I'll be launching it in January 2021.&lt;/p&gt;

&lt;h2&gt;
  
  
  Financials
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://perfbeacon.com/"&gt;PerfBeacon&lt;/a&gt; is now break-even (in terms of running costs), averaging between $1-200 MRR.&lt;/p&gt;

&lt;p&gt;Judging by pre-orders alone, &lt;a href="http://useeffectbyexample.com/"&gt;useEffect By Example&lt;/a&gt; looks like it'll beat PerfBeacon in terms of revenue, even in the long term, and, even though PerfBeacon runs on a recurring subscription model.&lt;/p&gt;

&lt;p&gt;I'm still &lt;strong&gt;very&lt;/strong&gt; far from being able to employ myself, but at least now I have a repeatable process that works - writing consistently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;

&lt;p&gt;SaaS is only one way of solving problems for your customers. Explore all possible means (books, videos, workshops, consulting, code snippets, plugins) of solving a problem before diving into building a SaaS. SaaS is often the most difficult, and time consuming way to run a business.&lt;/p&gt;

&lt;p&gt;Pre-sales are an incredibly powerful tool to gauge buyer interest. Indiehackers can easily start a gumroad pre-sale to take valid credit card details, and only process the payment when they ship.&lt;/p&gt;

&lt;p&gt;Write regularly. Similar to running/working out, the more you work out the muscle, the easier it becomes. There are unintentional benefits to this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing documentation, emails, tickets, etc at work becomes easier&lt;/li&gt;
&lt;li&gt;SEO: the more you write, the more long-tail keywords your articles will pick up&lt;/li&gt;
&lt;li&gt;You grow an audience that wants to see what you're regularly creating&lt;/li&gt;
&lt;li&gt;You get to practice shipping in a way you wouldn't get if you only focused on building product.

&lt;ul&gt;
&lt;li&gt;Building OnlineOrNot and PerfBeacon, I got to experience launch anxiety perhaps once every six months, while with my writing on &lt;a href="https://MaxRozen.com"&gt;MaxRozen.com&lt;/a&gt; I experience it weekly.&lt;/li&gt;
&lt;li&gt;This in turn has taught me lessons on editing, marketing, running a newsletter, engaging with the community, and more that I probably don't even realise yet.&lt;/li&gt;
&lt;/ul&gt;


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

</description>
      <category>career</category>
      <category>startup</category>
    </item>
    <item>
      <title>Keeping up with React Libraries</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Mon, 26 Oct 2020 09:48:48 +0000</pubDate>
      <link>https://dev.to/rozenmd/keeping-up-with-react-libraries-5ceb</link>
      <guid>https://dev.to/rozenmd/keeping-up-with-react-libraries-5ceb</guid>
      <description>&lt;p&gt;This is an article from MaxRozen.com. You can read the original  &lt;a href="https://maxrozen.com/keeping-up-with-react-libraries/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s no secret React has a library discoverability problem.&lt;/p&gt;

&lt;p&gt;While the number of stars in GitHub and weekly downloads in npm might be a good starting point for finding quality libraries, normally you have to wade through a lot of reddit, hacker news, dev.to and individual blog posts to find the best ones.&lt;/p&gt;

&lt;p&gt;In this (continually updated) article, I’ll be adding libraries (excluding component libraries, I track those &lt;a href="https://maxrozen.com/guide-to-component-ui-libraries-react/"&gt;here&lt;/a&gt;) worth talking about on a single page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Accessibility&lt;/li&gt;
&lt;li&gt;Animation&lt;/li&gt;
&lt;li&gt;Browser Features&lt;/li&gt;
&lt;li&gt;Data Fetching Libraries&lt;/li&gt;
&lt;li&gt;Data Visualisation&lt;/li&gt;
&lt;li&gt;Forms&lt;/li&gt;
&lt;li&gt;State Management&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://react-spectrum.adobe.com/react-aria/getting-started.html#installation"&gt;React Aria&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Aria provides you with Hooks that provide accessibility for your components, so all you need to worry about is design and styling. Particularly useful for those building design systems.&lt;/p&gt;

&lt;p&gt;Example usage - &lt;a href="https://react-spectrum.adobe.com/react-aria/useButton.html"&gt;useButton&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@react-aria/button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buttonProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;buttonProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Button pressed!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Press me&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Animation
&lt;/h3&gt;

&lt;p&gt;Animation adds soul to otherwise boring things. These libraries let you build the web app equivalent of &lt;a href="https://www.youtube.com/watch?v=PGKmexNTHNE"&gt;Pixar’s Intro Animation&lt;/a&gt;, but in React.&lt;/p&gt;

&lt;p&gt;Both libraries have similar APIs and support spring physics over time-based animation, though Framer Motion seems to be used more often on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.framer.com/motion/"&gt;Framer Motion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Framer Motion is an animation and gesture library built by &lt;a href="https://www.framer.com/"&gt;Framer&lt;/a&gt;. The added benefit of Framer Motion is that your designers can build animations in Framer, then hand-off designs to be accurately implemented using Framer’s own library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.react-spring.io/"&gt;React Spring&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Spring uses spring physics rather than time-based animation to animate your components. Relative to Framer Motion, React Spring has been under development for longer, with a greater number of open-source contributors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Features
&lt;/h3&gt;

&lt;p&gt;Ever been asked to implement random features that someone on the product team saw on another website and thought was cool? These libraries save you time on building those features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/CharlesStover/use-clippy"&gt;useClippy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;useClippy is a React hook that lets you read and write to your user’s clipboard. Particularly useful for improving UX, letting you save your users from double clicking on your data fields, by providing them a button to copy values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/react-player"&gt;ReactPlayer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ReactPlayer is an awesome library that lets you embed video from major sources (YouTube, Facebook, Twitch, SoundCloud, and more), and define your own controls to the video.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/fkhadra/react-toastify"&gt;React Toastify&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Toastify allows you to add fancy in-app notifications (like the “Message Sent” notification in Gmail) to your React app with only four additional lines of code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Fetching Libraries
&lt;/h3&gt;

&lt;p&gt;You might be wondering why you’d even need a data fetching library, when you could use &lt;code&gt;useEffect&lt;/code&gt; and &lt;code&gt;fetch()&lt;/code&gt;. The short answer is that these libraries also handle caching, loading and error states, avoiding redundant network requests, and much more.&lt;/p&gt;

&lt;p&gt;You could spend hundreds of hours implementing these features in a Redux-like state manager, or just install one of these libraries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tannerlinsley/react-query"&gt;React Query&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Query lets you request data from the same API endpoint (for example &lt;code&gt;api/users/1&lt;/code&gt;) across multiple components, without resulting in multiple network requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vercel/swr"&gt;SWR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to React Query (in fact, based on the same premise, see this &lt;a href="https://github.com/vercel/swr/issues/127"&gt;issue&lt;/a&gt; for more info), SWR is another data fetching library worth checking out. SWR has the added security of being used by Vercel in production as part of their platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Visualisation
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Everyone wants to have beautiful charts, but nobody wants to learn no complicated-ass D3&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Ronnie Coleman, probably&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/airbnb/visx"&gt;visx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’ve ever used a popular charting library such as Recharts or Charts.js, you’ll know it’s surprisingly easy to reach the limits of what a charting library can do for you.&lt;/p&gt;

&lt;p&gt;visx is different, in that it provides you with lower-level React components that are much closer to D3 than other charting libraries. This makes it easier to build your own re-usable charting library, or hyper-customised charts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forms
&lt;/h3&gt;

&lt;p&gt;Forms suck. Take it from someone who once had to build a “smart” form with 26 possible fields to fill out - you want to pass off &lt;strong&gt;as much as possible&lt;/strong&gt; to your form library, leaving you with only quick field names to enter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://react-hook-form.com/"&gt;React Hook Form&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Hook Form is different to other form libraries, in that by default, you’re not building controlled components and watching their state. This means your app’s performance won’t get slower as you add more fields to your form.&lt;/p&gt;

&lt;p&gt;On top of that, it’s probably one of the best documented libraries out there - every example has a CodeSandbox, making it easy to fork and try out your particular use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  State Management
&lt;/h3&gt;

&lt;p&gt;There’s been a fair bit of innovation in state management since the early days of Redux, it’s worth taking a look again if you’re interested in using global state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://recoiljs.org/docs/introduction/motivation"&gt;Recoil&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recoil is a state management library - think Redux meets React Hooks, minus the boilerplate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redux-toolkit.js.org/"&gt;Redux Toolkit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Redux Toolkit (or RTK), is the official, opinionated way to manage your state using Redux.&lt;/p&gt;

&lt;p&gt;RTK greatly reduces the amount of boilerplate necessary for using Redux, provides sensible defaults, and keeps the same immutable update logic that we know and love.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidkpiano/xstate"&gt;xstate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;XState is a library that lets you formalise your React app as a &lt;a href="https://en.wikipedia.org/wiki/Finite-state_machine"&gt;finite state machine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;State machines aren’t a particularly new concept, but developers have only recently started to realise that maybe our apps could be less buggy if we explicitly define the states they can be in, and the inputs required to transition between states.&lt;/p&gt;

&lt;p&gt;XState also generates &lt;a href="https://xstate.js.org/viz/?gist=bbcb4379b36edea0458f597e5eec2f91"&gt;charts&lt;/a&gt; for you based on your app’s xstate configuration, meaning your documentation will stay up to date as you code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/react-testing-library/intro"&gt;React Testing Library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you haven’t started a new create-react-app project in a while, you can be forgiven for not having heard of React Testing Library (RTL) yet.&lt;/p&gt;

&lt;p&gt;RTL replaces Enzyme in your testing stack. While both libraries provide methods for rendering React components in tests, RTL exposes functions that nudge developers away from &lt;a href="https://maxrozen.com/dont-test-implementation-details-react/"&gt;testing implementation details, and towards testing functionality&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Understanding when to use useMemo</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Tue, 20 Oct 2020 07:08:35 +0000</pubDate>
      <link>https://dev.to/rozenmd/understanding-when-to-use-usememo-226</link>
      <guid>https://dev.to/rozenmd/understanding-when-to-use-usememo-226</guid>
      <description>&lt;p&gt;This is an article from MaxRozen.com. You can read the original by &lt;a href="https://maxrozen.com/understanding-when-use-usememo/"&gt;clicking here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's pretty common for people to say&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't use useCallback/useMemo everywhere!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without &lt;em&gt;actually&lt;/em&gt; explaining cases where you &lt;strong&gt;would&lt;/strong&gt; want to use useCallback/useMemo.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://maxrozen.com/stop-useeffect-running-every-render-with-usecallback/"&gt;last article&lt;/a&gt; we learned that useCallback is useful for passing stable references to functions down to the children of a React component, particularly when using those functions in a child's useEffect.&lt;/p&gt;

&lt;p&gt;If you're scratching your head wondering "...but then, what the hell is useMemo for?", you're not alone!&lt;/p&gt;

&lt;p&gt;One hint that the React docs give is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;useCallback(fn, deps) is equivalent to useMemo(() =&amp;gt; fn, deps).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which is great if you're well versed on the significance of passing a function reference versus passing an arrow function that calls the function (while quickly scanning docs for an answer), but for the rest of us, hopefully this article will help.&lt;/p&gt;

&lt;h2&gt;
  
  
  What useMemo does
&lt;/h2&gt;

&lt;p&gt;In short, &lt;strong&gt;useMemo&lt;/strong&gt; calls a function when dependencies change, and memoizes (remembers) the &lt;strong&gt;result of the function&lt;/strong&gt; between renders.&lt;/p&gt;

&lt;p&gt;This is in contrast with &lt;strong&gt;useCallback&lt;/strong&gt; which remembers an &lt;strong&gt;existing value&lt;/strong&gt; (typically a function's definition), between renders.&lt;/p&gt;

&lt;p&gt;You want to use useMemo to save yourself from rerunning an expensive calculation to generate a new value, and you want to use useCallback to store an existing value.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use useMemo
&lt;/h2&gt;

&lt;p&gt;This part is where it's easy to get frustrated. There are a lot of blog posts out there describing useMemo, and then presenting examples of when &lt;strong&gt;not&lt;/strong&gt; to use it.&lt;/p&gt;

&lt;p&gt;Unfortunately, it needs repeating: don't use useMemo until you notice parts of your app are frustratingly slow. "Premature optimisation is the root of all evil", and throwing useMemo everywhere is premature optimisation.&lt;/p&gt;

&lt;p&gt;Here are a couple of cases when you &lt;strong&gt;should&lt;/strong&gt; consider using useMemo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're noticing a component's render is frustratingly slow, and you're passing a calculation to an unknowable number of children, such as when rendering children using &lt;code&gt;Array.map()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Your app often becomes unresponsive because you're fetching a large amount of data, and having to transform it into a usable format&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key is to focus on the problem.&lt;/p&gt;

&lt;p&gt;"My app is slow, and calculation-heavy" is a problem that useMemo helps to solve. Run your app through React DevTools Profiler, as well as &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Google Lighthouse&lt;/a&gt; or &lt;a href="https://webpagetest.org/"&gt;WebPageTest&lt;/a&gt; to understand its performance metrics, wrap your calculation in useMemo, then measure again.&lt;/p&gt;

&lt;p&gt;"I just learned useMemo, and want to use it everywhere" is focusing on the solution, and will lead you to premature optimisation, and a potentially slower app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not use useMemo everywhere then?
&lt;/h2&gt;

&lt;p&gt;In short, it's not a free performance optimisation.&lt;/p&gt;

&lt;p&gt;There's an additional cost (memory usage, for one) incurred when setting up useMemo, that can very quickly outweigh the performance benefit of remembering every single function's possible value.&lt;/p&gt;

&lt;p&gt;When we use useMemo, we're taking up more memory in order to free up CPU time. If your app is hammering the CPU with a lot of calculations, that's when you might consider taking up some memory and use useMemo instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about stable references?
&lt;/h2&gt;

&lt;p&gt;If you want to keep a stable reference to an object/array &lt;strong&gt;that doesn't require recalculation&lt;/strong&gt;, consider using useRef.&lt;/p&gt;

&lt;p&gt;On the other hand if you need to recalculate the value when dependencies change, useMemo is the hook for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Potential mistakes when using useMemo
&lt;/h2&gt;

&lt;p&gt;Using useMemo isn't free of pitfalls as well - one of the big ones is that the cache isn't guaranteed to keep all of its values between renders. Taken from the docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You may rely on useMemo as a performance optimization, not as a semantic guarantee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Translation: the cache isn't stable!&lt;/p&gt;

&lt;p&gt;Meaning if you &lt;strong&gt;absolutely&lt;/strong&gt; want to avoid recalculations with your useMemo call, it's not guaranteed. For a version of useMemo with a stable cache, see &lt;a href="https://github.com/alexreardon/use-memo-one"&gt;useMemoOne&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Guide to Styling your React App</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Tue, 18 Aug 2020 08:42:05 +0000</pubDate>
      <link>https://dev.to/rozenmd/a-guide-to-styling-your-react-app-10lm</link>
      <guid>https://dev.to/rozenmd/a-guide-to-styling-your-react-app-10lm</guid>
      <description>&lt;p&gt;If you’re new to React, you might be wondering why there are so many different tutorials that teach different ways to style your React app. The truth is, we’re all still figuring out the best way to do things.&lt;/p&gt;

&lt;p&gt;Styles in React were more-or-less worked out in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Global CSS&lt;/li&gt;
&lt;li&gt;CSS Modules&lt;/li&gt;
&lt;li&gt;CSS in JS (&lt;a href="https://styled-components.com/"&gt;styled-components&lt;/a&gt;, &lt;a href="http://emotion.sh/"&gt;Emotion&lt;/a&gt;, etc)

&lt;ul&gt;
&lt;li&gt;Utility-first CSS&lt;/li&gt;
&lt;li&gt;Styled System&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Statically extracted CSS in JS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These days, I recommend starting with CSS in JS. If you’d like to know why, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick note
&lt;/h2&gt;

&lt;p&gt;When I say styling, I mean writing your CSS styles more-or-less from scratch. If you’re looking for pre-built components, I wrote a &lt;a href="https://maxrozen.com/guide-to-component-ui-libraries-react/"&gt;guide to commonly used React component libraries&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Global CSS
&lt;/h2&gt;

&lt;p&gt;Global CSS is likely the way you’re used to styling webpages. You have a giant &lt;code&gt;styles.css&lt;/code&gt; file, and try to write &lt;a href="http://getbem.com/naming/"&gt;BEM&lt;/a&gt; or &lt;a href="http://smacss.com/"&gt;SMACSS&lt;/a&gt; names for all of your classes. Alternatively, you have a ton of tiny files, and don’t always know where each class lives.&lt;/p&gt;

&lt;p&gt;We as frontend developers quickly realised that global CSS doesn’t really scale. The more teams you have editing a single file, the more likely you are to have CSS that doesn’t do anything (people become too afraid to delete anything in case it breaks).&lt;/p&gt;

&lt;p&gt;If you still want to use Global CSS in your React app, all you need to do is import the CSS file at the top level of your React app (assuming you’ve configured webpack to do so, or are using create-react-app).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//App.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"some-class"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;some other stuff in here&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  CSS Modules
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/css-modules/css-modules"&gt;CSS Modules&lt;/a&gt; &lt;em&gt;look&lt;/em&gt; a lot like Global CSS, in the sense that you’re importing a CSS file into your React component, but under the hood it’s quite different.&lt;/p&gt;

&lt;p&gt;A lot of the problems we used to have with Global CSS are gone with CSS Modules.&lt;/p&gt;

&lt;p&gt;Your CSS looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* style.css */&lt;/span&gt;
&lt;span class="nc"&gt;.makeItGreen&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and your React Component looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;makeItGreen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Green!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key difference here is that only files that import &lt;code&gt;style.css&lt;/code&gt; will be able to access the class names that it defines, and the class names that get generated during the build process will be unique.&lt;/p&gt;

&lt;p&gt;No more conflicts, no more “too afraid to delete things in case it breaks”, just locally scoped CSS. You can also set-up SCSS/LESS support, if you need it.&lt;/p&gt;

&lt;p&gt;The really cool thing about this is that you can start to play around with JavaScript to change a component’s styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;makeItRed&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;makeItGreen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;myStyle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although, that starts to get a bit messy if you’re using several props to change the style and behaviour of your components. What if your styles could just be components?&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS in JS
&lt;/h2&gt;

&lt;p&gt;That’s where CSS in JS comes in.&lt;/p&gt;

&lt;p&gt;Libraries like &lt;a href="https://styled-components.com/"&gt;styled-components&lt;/a&gt; and &lt;a href="https://emotion.sh/docs/introduction"&gt;Emotion&lt;/a&gt; make it possible to wrap components (including divs, spans, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags, &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags) with styles, and use them as React components.&lt;/p&gt;

&lt;p&gt;The best part is, you can use all of the standard CSS features you’re used to, such as media queries, and &lt;code&gt;:hover&lt;/code&gt; and &lt;code&gt;:focus&lt;/code&gt; selectors.&lt;/p&gt;

&lt;p&gt;Our example from above now becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@emotion/styled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// OR import styled from 'styled-components'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StyledGreenThing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="s2"&gt;`
  color: &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StyledGreenThing&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;StyledGreenThing&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As of 2020, Emotion and styled-components are evenly matched performance-wise. Contributors to styled-components worked hard to bring their performance up to Emotion’s level, so deciding which one to use isn’t as much of a big deal any more.&lt;/p&gt;

&lt;p&gt;Emotion &lt;em&gt;does&lt;/em&gt; provide some extra options for styling, such as the &lt;a href="https://emotion.sh/docs/css-prop"&gt;css prop&lt;/a&gt;, while styled-components tries to keep a single, standard way of doing things via the &lt;code&gt;styled&lt;/code&gt; API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utility-first CSS
&lt;/h2&gt;

&lt;p&gt;A guide to styling React apps wouldn’t be complete without mentioning utility-first CSS frameworks such as &lt;a href="https://tailwindcss.com/"&gt;Tailwind&lt;/a&gt;. You don’t actually need React to use utility-first CSS, but in my opinion, it makes for a better developer experience when you add React and CSS in JS.&lt;/p&gt;

&lt;p&gt;In short, Tailwind lets you style your components one class at a time. Here’s what it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"md:flex bg-white rounded-lg p-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-16 w-16 md:h-24 md:w-24 rounded-full mx-auto md:mx-0 md:mr-6"&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"avatar.jpg"&lt;/span&gt;
  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-center md:text-left"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-lg"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Erin Lindford&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-purple-500"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Product Engineer&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-600"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;erinlindford@example.com&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-600"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;(555) 765-4321&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which creates a component that looks like this:&lt;a href="https://maxrozen.com/static/0e68f5fb70d237a6e162757e1bcb84db/2fd48/tailwind-css-example.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gWxk_0py--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/0e68f5fb70d237a6e162757e1bcb84db/2fd48/tailwind-css-example.png" alt="Tailwind CSS Example Component" title="Tailwind CSS Example Component" width="508" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might be thinking that it’s not a particularly re-usable solution, however it’s possible to use Tailwind class names with your favourite CSS in JS library using &lt;a href="https://github.com/ben-rogerson/twin.macro"&gt;twin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can then have styled Tailwind components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twin.macro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="s2"&gt;`
  color: purple;
  &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tw&lt;/span&gt;&lt;span class="s2"&gt;`border rounded`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyStyledInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Styled System
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://styled-system.com/"&gt;Styled System&lt;/a&gt; takes the &lt;code&gt;styled&lt;/code&gt; API supplied by styled-components or Emotion, and adds utilities as props, rather than class names.&lt;/p&gt;

&lt;p&gt;The Styled System approach is particularly powerful when it comes to theming/white labelling, as changing the entire appearance of your app can be done by replacing the &lt;code&gt;theme.js&lt;/code&gt; file you provide.&lt;/p&gt;

&lt;p&gt;Your components end up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@emotion/styled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;space&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styled-system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;space&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UsedBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Box&lt;/span&gt;
      &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bold"&lt;/span&gt;
      &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"white"&lt;/span&gt;
      &lt;span class="na"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Hello
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Statically extracted CSS in JS
&lt;/h2&gt;

&lt;p&gt;The trouble with CSS in JS is that it takes JavaScript to load your CSS. This slows things down big time, so people started looking for ways to extract the CSS from CSS-in-JS during build time.&lt;/p&gt;

&lt;p&gt;There are a few libraries that can do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://compiledcssinjs.com/"&gt;Compiled&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/callstack/linaria"&gt;linaria&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/seek-oss/treat"&gt;treat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compiled and linaria let you use the &lt;code&gt;styled&lt;/code&gt; API that you know and love, while giving you the performance benefit of not having CSS in your bundle.&lt;/p&gt;

&lt;p&gt;(This is an article from MaxRozen.com. You can read the original by &lt;a href="https://maxrozen.com/guide-to-styling-react-app/"&gt;clicking here&lt;/a&gt;.)&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Guide to Commonly Used React Component Libraries</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Thu, 13 Aug 2020 21:37:42 +0000</pubDate>
      <link>https://dev.to/rozenmd/a-guide-to-commonly-used-react-component-libraries-1m61</link>
      <guid>https://dev.to/rozenmd/a-guide-to-commonly-used-react-component-libraries-1m61</guid>
      <description>&lt;h2&gt;
  
  
  Ant Design
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://maxrozen.com/static/709b9b5610cc3f9b8b0044432c27984f/4b39a/antdesignpro.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tZ_JsfLq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/709b9b5610cc3f9b8b0044432c27984f/fcda8/antdesignpro.png" alt="Ant Design Pro" title="Ant Design Pro" width="590" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Project link: &lt;a href="https://ant.design/"&gt;Ant Design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=antd@4.5.4"&gt;BundlePhobia&lt;/a&gt;): 1.2 mB minified, 349.2kB minified + gzipped, less with treeshaking&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ant Design comes with a &lt;em&gt;huge&lt;/em&gt; amount of supporting documentation, community, including a separate project (Ant Design Pro) with pre-made templates&lt;/li&gt;
&lt;li&gt;The kind of UI library you’d use to quickly throw up as a back-office/internal app design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessibility is lacking&lt;/li&gt;
&lt;li&gt;It’s huge. Expect a sizable performance impact when using&lt;/li&gt;
&lt;li&gt;Pollutes your CSS (expect to add &lt;code&gt;!important&lt;/code&gt; to prevent it styling your non-Ant components)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bootstrap
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://maxrozen.com/static/05a9570527cc0535ab75570be35185eb/e8814/bootstrap.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XWFHQWdg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/05a9570527cc0535ab75570be35185eb/fcda8/bootstrap.png" alt="Bootstrap" title="Bootstrap" width="590" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I actually rate Bootstrap relatively highly as a UI library. It’s not going to win you any design awards, but it gets the job done for side projects and minimum viable products.&lt;/p&gt;

&lt;p&gt;It depends what you want to use it for, though. If you’re new to React, it’s a great library to use to get started. As a more experienced developer, chances are you’ll want to look into styled-components/Emotion.&lt;/p&gt;

&lt;p&gt;There are two popular libraries with React bindings for Bootstrap, I’ve personally only used Reactstrap.&lt;/p&gt;

&lt;p&gt;Project links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://react-bootstrap.github.io/"&gt;React Bootstrap&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=react-bootstrap@1.3.0"&gt;BundlePhobia&lt;/a&gt;): 111kB minified, 34.4kB minified + gzipped, less with treeshaking&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;&lt;a href="https://reactstrap.github.io/"&gt;Reactstrap&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=reactstrap@8.5.1"&gt;BundlePhobia&lt;/a&gt;): 152.1kB minified, 39.4kB minified + gzipped, less with treeshaking&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Bootstrap library that you know and love, with React bindings&lt;/li&gt;
&lt;li&gt;Easily customised via CSS-in-JS&lt;/li&gt;
&lt;li&gt;It’s been around long enough with widespread usage that bugs/issues aren’t a worry&lt;/li&gt;
&lt;li&gt;Quick to get started&lt;/li&gt;
&lt;li&gt;No jQuery dependency as it’s been reimplemented entirely in React&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s Bootstrap: your site will look like everyone elses if you don’t customise it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bulma
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://maxrozen.com/static/8bf5a508641f3fd64b12facf6d628784/e8814/bulma.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J44Kso1---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/8bf5a508641f3fd64b12facf6d628784/fcda8/bulma.png" alt="Bulma" title="Bulma" width="590" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bulma is different to most libraries presented here because it’s purely a CSS framework, no JS required. You can choose to either use the classes from Bulma directly, or use a wrapper library such as &lt;code&gt;react-bulma-components&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Project links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bulma.io/"&gt;Bulma&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/couds/react-bulma-components"&gt;react-bulma-components&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=react-bulma-components@3.4.0"&gt;BundlePhobia&lt;/a&gt;): 179kB minified, 20.1kB minified + gzipped&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doesn’t have the Bootstrap look and feel&lt;/li&gt;
&lt;li&gt;Good for getting something up and running quickly&lt;/li&gt;
&lt;li&gt;Modern features (Flexbox/Grid under the hood)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessibility: there’s some, but doesn’t follow WCAG guidelines as strongly as other libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Chakra UI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://maxrozen.com/static/8077f438a7cf9aa9ef7da76b2e7c27ee/e8814/chakra-ui.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f6M3osH---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/8077f438a7cf9aa9ef7da76b2e7c27ee/fcda8/chakra-ui.png" alt="Chakra UI" title="Chakra UI" width="590" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Project link: &lt;a href="https://chakra-ui.com/"&gt;Chakra UI&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=@chakra-ui/core@0.8.0"&gt;BundlePhobia&lt;/a&gt;): 326.2kB minified, 101.2kB minified + gzipped, less with treeshaking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessibility: follows WAI-ARIA guidelines and components use aria tags&lt;/li&gt;
&lt;li&gt;Discord server for support&lt;/li&gt;
&lt;li&gt;Easily customisable (with theming support)&lt;/li&gt;
&lt;li&gt;Highly modular, so treeshaking actually removes code you don’t use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quite new.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very close to v1 release, so be aware of breaking changes coming from v0.8.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Material UI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://maxrozen.com/static/30a1948174853df72b6b3bbbfd4975c4/e8814/material-ui.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GzmanH73--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/30a1948174853df72b6b3bbbfd4975c4/fcda8/material-ui.png" alt="Material UI" title="Material UI" width="590" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Material UI is one of those libraries that I love to hate. It has helped me get through some extremely tight deadlines for clients in the past, but I always end up removing it in favour of almost anything else as soon as possible.&lt;/p&gt;

&lt;p&gt;In the past you could only customise Material UI’s styles by writing &lt;a href="https://cssinjs.org/?v=v10.3.0#jss-example"&gt;JSS&lt;/a&gt;, but thankfully it’s now &lt;a href="https://material-ui.com/guides/interoperability/#styled-components"&gt;possible to override styles&lt;/a&gt; with styled-components and Emotion.&lt;/p&gt;

&lt;p&gt;Project link: &lt;a href="https://material-ui.com/"&gt;Material UI&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=@material-ui/core@4.11.0"&gt;BundlePhobia&lt;/a&gt;): 325.7kB minified, 92kB minified + gzipped, less with treeshaking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comprehensive documentation&lt;/li&gt;
&lt;li&gt;Icon library is &lt;a href="https://material-ui.com/components/material-icons/"&gt;massive&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Simple to use (at first)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customisation is difficult, and painful, yet necessary (to improve visuals)&lt;/li&gt;
&lt;li&gt;Performance: known to render excessive DOM nodes&lt;/li&gt;
&lt;li&gt;Your app will look like Google made it (which could be a pro, for some people)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Semantic UI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://maxrozen.com/static/07600227238f39b2d1b2fc5eb8f42874/e8814/semantic-ui.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o1JD6dQ8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/07600227238f39b2d1b2fc5eb8f42874/fcda8/semantic-ui.png" alt="Semantic UI" title="Semantic UI" width="590" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Project links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://semantic-ui.com/"&gt;Semantic UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/Semantic-Org/Semantic-UI-React"&gt;Semantic-UI-React&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=semantic-ui-react@1.2.0"&gt;BundlePhobia&lt;/a&gt;): 300.8kB minified, 80.9kB minified + gzipped, less with treeshaking&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Pros&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Composable (using the &lt;code&gt;as&lt;/code&gt; prop to pass components)&lt;/li&gt;
&lt;li&gt;Easily customisable&lt;/li&gt;
&lt;li&gt;Helpful docs&lt;/li&gt;
&lt;li&gt;High profile users (Netflix internally, Amazon publishing)&lt;/li&gt;
&lt;li&gt;TypeScript support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Potential uncertainty about the open source project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See issues: &lt;a href="https://github.com/Semantic-Org/Semantic-UI/issues/6109"&gt;https://github.com/Semantic-Org/Semantic-UI/issues/6109&lt;/a&gt; &lt;a href="https://github.com/Semantic-Org/Semantic-UI/issues/6413"&gt;https://github.com/Semantic-Org/Semantic-UI/issues/6413&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Community-run fork exists: &lt;a href="https://github.com/fomantic/Fomantic-UI"&gt;https://github.com/fomantic/Fomantic-UI&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Honourable mentions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reach UI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://reach.tech/"&gt;Reach UI&lt;/a&gt; is a low-level component library, focusing on allowing developers to build accessible React components in their design system.&lt;/p&gt;

&lt;p&gt;There’s no bundle size available as each component is exported individually as its own npm package.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reakit
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://reakit.io/"&gt;Reakit&lt;/a&gt; is another low-level component library. It’s technically a UI library, but doesn’t come with CSS. So you still need to come up with a styling solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=reakit@1.2.1"&gt;BundlePhobia&lt;/a&gt;): 119.9kB minified, 32.1kB minified + gzipped, less with treeshaking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rebass
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://maxrozen.com/static/abbff9f085f8ef377e3250ec1b2710d9/e8814/rebass.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EVYPLEYw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://maxrozen.com/static/abbff9f085f8ef377e3250ec1b2710d9/fcda8/rebass.png" alt="Rebass" title="Rebass" width="590" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rebass has been on my radar for some time. It’s an extremely powerful component library that doesn’t come with a theme, but can be themed easily. For an example of how this works in practice, see their &lt;a href="https://rebassjs.org/demo"&gt;demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Project link: &lt;a href="https://rebassjs.org/"&gt;Rebass&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundle Size (from &lt;a href="https://bundlephobia.com/result?p=rebass@4.0.7"&gt;BundlePhobia&lt;/a&gt;): 43kB minified, 14.4kB minified + gzipped, less with treeshaking&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;p&gt;In making this list, I’ve attempted to avoid corporate design systems, however some (Material UI) have achieved such widespread adoption that this list would be incomplete without them.&lt;/p&gt;

&lt;p&gt;I’ve also intentionally left out CSS-in-JS such as &lt;a href="https://styled-components.com/"&gt;styled-components&lt;/a&gt; and &lt;a href="https://emotion.sh/docs/introduction"&gt;Emotion&lt;/a&gt;, and utility CSS systems such as &lt;a href="https://tailwindcss.com/"&gt;Tailwind&lt;/a&gt;, as they are not explicitly “React Component libraries”, but rather tools with which to make your components.&lt;/p&gt;

&lt;p&gt;Am I missing any? Tell me on &lt;a href="https://twitter.com/RozenMD"&gt;Twitter!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(This is an article from MaxRozen.com. You can read the original by &lt;a href="https://maxrozen.com/guide-to-component-ui-libraries-react/"&gt;clicking here&lt;/a&gt;.)&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to use SVGs in your React App</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Mon, 10 Aug 2020 21:37:42 +0000</pubDate>
      <link>https://dev.to/rozenmd/how-to-use-svgs-in-your-react-app-2c0j</link>
      <guid>https://dev.to/rozenmd/how-to-use-svgs-in-your-react-app-2c0j</guid>
      <description>&lt;p&gt;Trying to render an SVG in your React app, and getting errors? You’re not alone - it’s a relatively common issue.&lt;/p&gt;

&lt;p&gt;There are two ways to do it, and both have tradeoffs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags, and passing your SVG’s URL
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import logoSrc from './logo.svg';

const MyLogo = () =&amp;gt; {
  return &amp;lt;img src={logoSrc} /&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The benefit of this approach is that your logo will not end up in your bundle, but rather exported as a static file when you run &lt;code&gt;yarn build&lt;/code&gt; (assuming you’re using a standard webpack config, such as the one found in create-react-app).&lt;/p&gt;

&lt;p&gt;This then gives you the option of aggressively caching icons that you know won’t change.&lt;/p&gt;

&lt;p&gt;You would typically use this approach for larger company logos on your marketing site, or for illustrations in your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a React component, and passing props
&lt;/h2&gt;

&lt;p&gt;The other option is to create a React component containing your SVG. Also known as “inlining” your SVG.&lt;/p&gt;

&lt;p&gt;This done by pasting your raw &lt;code&gt;svg&lt;/code&gt; markup into a new React component.&lt;/p&gt;

&lt;p&gt;There are a few ways to achieve this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually, byremoving/replacing all HTML props with the React equivalent, and adding &lt;code&gt;{...props}&lt;/code&gt; to the main &lt;code&gt;svg&lt;/code&gt; element),&lt;/li&gt;
&lt;li&gt;CLI via &lt;a href="https://github.com/gregberge/svgr"&gt;SVGR&lt;/a&gt; - a utility to automate this process&lt;/li&gt;
&lt;li&gt;Webpack config via &lt;a href="https://react-svgr.com/docs/webpack/"&gt;SVGR&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re using create-react-app, it already has SVGR’s webpack config built-in, so you can already use your SVGs like React components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Star from './star.svg';
const App = () =&amp;gt; (
  &amp;lt;div&amp;gt;
    &amp;lt;Star /&amp;gt;
  &amp;lt;/div&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what a manually created SVG-based React component looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';

export const DeleteIcon = (props) =&amp;gt; (
  &amp;lt;svg
    xmlns="http://www.w3.org/2000/svg"
    height="24px"
    viewBox="0 0 24 24"
    {...props}
  &amp;gt;
    &amp;lt;path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" /&amp;gt;
    &amp;lt;path d="M0 0h24v24H0z" fill="none" /&amp;gt;
  &amp;lt;/svg&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach lets you easily access props on your SVG icon. For example, changing the fill color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;DeleteIcon fill="#fff" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downside being that your icons won’t be as easily cached, so I would use this approach for smaller icons, such as the &lt;a href="https://material.io/resources/icons/?style=baseline"&gt;Material Design Icons&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;(This is an article posted to my blog at maxrozen.com. You can read it online by &lt;a href="https://maxrozen.com/how-to-use-svg-react-app/"&gt;clicking here&lt;/a&gt;.)&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Should you use functional components + Hooks over class components?</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Fri, 07 Aug 2020 23:51:22 +0000</pubDate>
      <link>https://dev.to/rozenmd/should-you-use-functional-components-hooks-over-class-components-5829</link>
      <guid>https://dev.to/rozenmd/should-you-use-functional-components-hooks-over-class-components-5829</guid>
      <description>&lt;p&gt;If you're new to React, and you've been working through tutorials, chances are you've run into examples of both functional components with Hooks, and class components, with no strong indication of which one you should be using. Even as a seasoned developer, you might still be using class components, wondering if its worth the rewrite.&lt;/p&gt;

&lt;p&gt;Heck, you might even be thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I've been digging for answers, but I just can't find a clear answer for this&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's fair enough, even the official documentation didn't have a strong recommendation until the middle of 2020.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one should you use?
&lt;/h2&gt;

&lt;p&gt;The official React team stance (according to the &lt;a href="https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both"&gt;docs&lt;/a&gt;), is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you’re ready, we’d encourage you to start trying Hooks in new components you write. [...] We don’t recommend rewriting your existing classes to Hooks unless you planned to rewrite them anyway (e.g. to fix bugs).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To summarise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New code should use functional components with Hooks, when you're ready&lt;/li&gt;
&lt;li&gt;Old code can keep using class components, unless you want to rewrite&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Should I just focus on hooks then?
&lt;/h2&gt;

&lt;p&gt;It's not that simple.&lt;/p&gt;

&lt;p&gt;You still need class components to build &lt;a href="https://reactjs.org/docs/error-boundaries.html"&gt;Error Boundaries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On top of that, most code written before 2019 will likely still use class components, as there is no immediate need to rewrite them to functional components with Hooks. If you want to understand existing code in a codebase, you'll need to also learn class components.&lt;/p&gt;

&lt;p&gt;You'll also find that companies that ask React questions during their interviews will still ask you about classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should we rewrite our old class-based code to use Hooks?
&lt;/h2&gt;

&lt;p&gt;As with all good things, there are tradeoffs to consider here.&lt;/p&gt;

&lt;p&gt;Hooks result in much cleaner, easier to understand components compared to class components of a similar complexity.&lt;/p&gt;

&lt;p&gt;To illustrate my point, compare this component that fetches some data from &lt;a href="https://swapi.dev/"&gt;The Star Wars API&lt;/a&gt;, written first as a class, then as a functional component with hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;DataDisplayer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`https://swapi.dev/api/people/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newData&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;componentWillUnmount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// if this was a subscription, we'd need clean-up here&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CWU&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A pretty standard class component.&lt;/p&gt;

&lt;p&gt;As your app grows, the lifecycle methods grow larger, and the context switching involved just from scrolling through the file increases.&lt;/p&gt;

&lt;p&gt;I don't know about you, but my thought process when scrolling through classes is like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Okay so I'm in &lt;code&gt;componentDidMount&lt;/code&gt;, so we'll be fetching data here&lt;/p&gt;

&lt;p&gt;Okay so here we're rendering, so this code runs every single time&lt;/p&gt;

&lt;p&gt;I need to add some extra functionality... hmm which lifecycle method does &lt;em&gt;that&lt;/em&gt; go in again?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the other hand, you have Hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;DataDisplayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://swapi.dev/api/people/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// if this was a subscription, we'd need clean-up here&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Hooks, writing code that follows sequentially is much easier, and I find reading functional components with Hooks requires less context switching, as you're not jumping around the file to find which lifecycle method you think something happened in.&lt;/p&gt;

&lt;p&gt;That's the main benefit of rewriting to Hooks - your codebase's developer experience improves as it takes less time to understand what each component does.&lt;/p&gt;

&lt;p&gt;The main drawback is time - time spent rewriting is time you could have spent building new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to from here?
&lt;/h2&gt;

&lt;p&gt;When introducing Hooks to my team in the past I recommended the following approach, and it worked quite well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All new code should be written as functional components with Hooks&lt;/li&gt;
&lt;li&gt;Existing code should only be rewritten if it gets touched (for example, if you're fixing a bug or adding functionality, take the time to swap the component over to Hooks)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(This is an article posted to my blog at maxrozen.com. You can read it online by &lt;a href="https://maxrozen.com/react-components-hooks-functions-vs-classes/"&gt;clicking here&lt;/a&gt;.)&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>It's not always greener on the other side</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Wed, 05 Aug 2020 22:50:56 +0000</pubDate>
      <link>https://dev.to/rozenmd/it-s-not-always-greener-on-the-other-side-3of6</link>
      <guid>https://dev.to/rozenmd/it-s-not-always-greener-on-the-other-side-3of6</guid>
      <description>&lt;p&gt;I started my career having finished a double Bachelor’s degree in Software Engineering and Commerce, thinking I could &lt;em&gt;just&lt;/em&gt; get a finance job, and automate the boring parts with my Python skills.&lt;/p&gt;

&lt;p&gt;Then reality hit.&lt;/p&gt;

&lt;p&gt;Roughly a year of job hunting later (including one call back, in which the recruiter asked me why I didn’t also have a background in accounting), I realised I should probably rewrite my resume to be more Software Engineering focused.&lt;/p&gt;

&lt;p&gt;Eventually a friend from university suggested I pitch his hedge fund a dashboard built in &lt;a href="https://d3js.org/"&gt;D3.js&lt;/a&gt; - this cool new technology (at the time) that made their Excel dashboards look ancient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lfKUbPC_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oqggncpn3u2be5o5rohd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lfKUbPC_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oqggncpn3u2be5o5rohd.png" alt="D3 Dashboard" width="880" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So I started learning JavaScript
&lt;/h2&gt;

&lt;p&gt;I spent a week learning just enough JavaScript to make a convincing dashboard of all stocks trading on the ASX 200, with data fetching from Yahoo Finance. I didn’t get the gig, but I ended up getting the attention of the agency that worked for the hedge fund.&lt;/p&gt;

&lt;p&gt;I ended up working for the agency, building data pipelines, learning more Python, building sites in Django and (some) JavaScript. After about a year, I started longing for more work directly with clients, so I started interviewing with the Big Four (EY, PwC, Deloitte, KPMG), and found myself with an offer for a role at Ernst and Young (EY).&lt;/p&gt;

&lt;p&gt;I was so keen for a “big name” on my resume, I was willing to take a &lt;strong&gt;10% pay cut&lt;/strong&gt; to work there.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I became a consultant
&lt;/h2&gt;

&lt;p&gt;The consulting job didn’t quite turn out as planned.&lt;/p&gt;

&lt;p&gt;You had to wear a suit, most clients already hated you before meeting you (presumably due to the cost of hiring the firm). To top it off, it felt strange being told to perform manual tasks in ancient drag-and-drop software after having spent a year writing Python to automate that sort of work.&lt;/p&gt;

&lt;p&gt;I started hatching a plan to escape after three months. I wanted to go back to being a software engineer, but jobs in Python were rare in Sydney.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I started learning React
&lt;/h2&gt;

&lt;p&gt;I had written enough JavaScript to know React was probably going to be a big deal, so I hit the tutorials.&lt;/p&gt;

&lt;p&gt;I found the official documentation at the time to be quite dense, it almost felt like you needed to already know React to be able to finish the tutorial.&lt;/p&gt;

&lt;p&gt;A couple of tutorials really stood out, (and are amazingly still online):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kay-is/react-from-zero"&gt;React From Zero&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/the-road-to-learn-react/the-road-to-learn-react"&gt;The Road to React&lt;/a&gt; - I used the pre-hooks version (since they didn’t exist at the time), but I’m told the &lt;a href="https://www.roadtoreact.com/"&gt;new version&lt;/a&gt; is still good&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the tutorials, I started building &lt;a href="https://indiehackers.com/"&gt;IndieHacker&lt;/a&gt; style side projects in React and GraphQL while job hunting for a React job.&lt;/p&gt;

&lt;p&gt;It took about four months to find a job, but what I found almost &lt;strong&gt;doubled&lt;/strong&gt; my salary.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I became an IndieHacker
&lt;/h2&gt;

&lt;p&gt;I never really stopped building side projects while employed, mainly out of imposter syndrome, as I felt I really needed to catch up to my colleagues to be a useful member of the team.&lt;/p&gt;

&lt;p&gt;I personally wouldn’t recommend it, unless you’re &lt;em&gt;really good&lt;/em&gt; at managing burn-out.&lt;/p&gt;

&lt;p&gt;Over the years, I’ve built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a job board&lt;/li&gt;
&lt;li&gt;an appointment scheduler&lt;/li&gt;
&lt;li&gt;a room booking system&lt;/li&gt;
&lt;li&gt;a GraphQL snapshot monitoring service&lt;/li&gt;
&lt;li&gt;a REST API monitoring service&lt;/li&gt;
&lt;li&gt;a frontend performance monitoring service (&lt;a href="https://perfbeacon.com/"&gt;PerfBeacon&lt;/a&gt; - I’m currently working on this one)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a few years of being a React developer I finally got a job at Atlassian, as a frontend developer on the Growth team.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;I still think React (or something like it) is going to be the next big thing. The numbers tend to agree with me, &lt;a href="https://www.hntrends.com/2019/dec-another-year-on-top-for-react.html"&gt;React was #1 for hiring on Hacker News in both 2018 and 2019&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It has greatly improved my quality of life and my job prospects, so I want to help others do what I did.&lt;/p&gt;

&lt;p&gt;I plan on writing and releasing lessons to help people to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn enough JavaScript to get going with React&lt;/li&gt;
&lt;li&gt;Learn enough React to build useful software&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use Twitter to share what I’m working on, so you can &lt;a href="https://twitter.com/RozenMD"&gt;follow me there&lt;/a&gt; if you’d like to see it first.&lt;/p&gt;

</description>
      <category>career</category>
      <category>react</category>
    </item>
    <item>
      <title>Keeping your sites fast with regular performance tests</title>
      <dc:creator>Max Rozen</dc:creator>
      <pubDate>Tue, 04 Aug 2020 04:40:02 +0000</pubDate>
      <link>https://dev.to/rozenmd/keeping-your-sites-fast-with-regular-performance-tests-1mm</link>
      <guid>https://dev.to/rozenmd/keeping-your-sites-fast-with-regular-performance-tests-1mm</guid>
      <description>&lt;p&gt;Do you have clients, or stakeholders asking about improving their site performance, or wondering why their site seems to have gotten slower over time?&lt;/p&gt;

&lt;p&gt;It happens to everyone: you deliver a fast website from the start, but then over time you get feature request after feature request, deadlines get rushed in, and the next thing you know you’ve got a client asking why some pages are taking up to ten seconds to load.&lt;/p&gt;

&lt;p&gt;It doesn’t have to be this way.&lt;/p&gt;

&lt;p&gt;It’s actually a simple fix: regularly check your site speed.&lt;/p&gt;

&lt;p&gt;There are several manual tools you can use for free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webpagetest.org/"&gt;WebPageTest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.google.com/speed/pagespeed/insights/"&gt;Google PageSpeed Insights&lt;/a&gt; or &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Google Lighthouse&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Export the numbers, and use Excel or Google Sheets to graph your performance over time.&lt;/p&gt;

&lt;p&gt;Once you start checking your site speed daily, it becomes easier to notice your performance slipping over time.&lt;/p&gt;

&lt;p&gt;(This is an article posted to my blog at maxrozen.com. You can read it online by &lt;a href="https://maxrozen.com/keep-site-fast-regular-performance-tests/"&gt;clicking here&lt;/a&gt;.)&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
