<?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: Florentin / 珞辰</title>
    <description>The latest articles on DEV Community by Florentin / 珞辰 (@ecklf).</description>
    <link>https://dev.to/ecklf</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%2F627586%2F44854a90-5f11-454e-a6c7-fd174d1749fd.jpeg</url>
      <title>DEV Community: Florentin / 珞辰</title>
      <link>https://dev.to/ecklf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ecklf"/>
    <language>en</language>
    <item>
      <title>React Native + Next.js Monorepo
</title>
      <dc:creator>Florentin / 珞辰</dc:creator>
      <pubDate>Sun, 14 Nov 2021 11:23:22 +0000</pubDate>
      <link>https://dev.to/ecklf/react-native-nextjs-monorepo-19n8</link>
      <guid>https://dev.to/ecklf/react-native-nextjs-monorepo-19n8</guid>
      <description>&lt;h2&gt;
  
  
  Preamble
&lt;/h2&gt;

&lt;p&gt;If you need an introduction to Yarn Workspaces: &lt;a href="https://classic.yarnpkg.com/blog/2017/08/02/introducing-workspaces/"&gt;Yarn Blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you prefer looking at the finished repository: &lt;a href="https://github.com/ecklf/react-native-next-monorepo.git"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup
&lt;/h2&gt;

&lt;p&gt;Our goal for this blog post is to have a basic monorepo setup that contains one bare React Native app and one Next.js project. This will result in a file structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;monorepo-tutorial
├── package.json
└── packages
    ├── app
    └── web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For starters we create our root directory and initialize a fresh project with git repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;monorepo-tutorial &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;monorepo-tutorial &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn init &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;node_modules &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .gitignore &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since both of our packages will depend on &lt;code&gt;react&lt;/code&gt; we will &lt;em&gt;lift up&lt;/em&gt; the dependency to the root level of our monorepo. Note that we also add &lt;code&gt;react-dom&lt;/code&gt; in case we want to create more web packages later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-W&lt;/span&gt; react react-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;package.json&lt;/code&gt; we define a workspace structure. The below &lt;em&gt;glob&lt;/em&gt; defined in &lt;code&gt;workspaces&lt;/code&gt; tells Yarn where our monorepo packages are located.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;{&lt;/span&gt;
+ "private": true,
&lt;span class="gi"&gt;+ "name": "root",
&lt;/span&gt;  "version": "1.0.0",
  "main": "index.js",
  "author": "ecklf",
  "license": "MIT",
&lt;span class="gi"&gt;+ "workspaces": [
+   "packages/*"
+ ]
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now proceed with creating our packages folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;packages &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;packages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  React Native
&lt;/h2&gt;

&lt;p&gt;Let's start by initializing a fresh React Native project from the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx react-native init app &lt;span class="nt"&gt;--template&lt;/span&gt; react-native-template-typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now encouter this error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Failed to install CocoaPods dependencies for iOS project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is perfectly fine since the template's CocoaPods configuration has the wrong path to &lt;code&gt;react-native&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Continue by removing the &lt;code&gt;react&lt;/code&gt; dependency from the template since we will resolve it from the root level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;app
yarn remove react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From my experience Metro plays the nicest in monorepos when launched separately with &lt;code&gt;yarn start&lt;/code&gt;, so we disable the packaging when running &lt;code&gt;ios&lt;/code&gt; / &lt;code&gt;android&lt;/code&gt; scripts. While we are at it we can also update the name in our &lt;code&gt;package.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;{&lt;/span&gt;
+ "private": true,
&lt;span class="gi"&gt;+ "name": "@monorepo/app",
&lt;/span&gt;  "version": "1.0.0",
  "main": "index.js",
  "author": "ecklf",
  "license": "MIT",
  "scripts": {
&lt;span class="gd"&gt;-   "android": "react-native run-android",
&lt;/span&gt;&lt;span class="gi"&gt;+   "android": "react-native run-android --no-packager",
&lt;/span&gt;&lt;span class="gd"&gt;-   "ios": "react-native run-ios",
&lt;/span&gt;&lt;span class="gi"&gt;+   "ios": "react-native run-ios --no-packager",
&lt;/span&gt;  },
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  React Native Configuration
&lt;/h3&gt;

&lt;p&gt;Create the file &lt;code&gt;react-native.config.js&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ module.exports = {
+   reactNativePath: '../../node_modules/react-native',
+ };
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Metro Configuration
&lt;/h3&gt;

&lt;p&gt;Update &lt;code&gt;metro.config.js&lt;/code&gt; to have an additional watch folder at root level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ const path = require('path');
&lt;/span&gt;
module.exports = {
&lt;span class="gi"&gt;+ watchFolders: [path.resolve(__dirname, '../../')],
&lt;/span&gt;  transformer: {
    getTransformOptions: async () =&amp;gt; ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
&lt;span class="err"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Babel Configuration
&lt;/h3&gt;

&lt;p&gt;We need to add aliases to explicitly define where our root-level packages are located in &lt;code&gt;babel.config.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; @babel/runtime babel-plugin-module-resolver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module:metro-react-native-babel-preset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module-resolver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^react-native$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^react-native/(.+)&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;name&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;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`react-native/&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.ios.js&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="s2"&gt;.ios.ts&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="s2"&gt;.ios.tsx&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="s2"&gt;.android.js&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="s2"&gt;.android.ts&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="s2"&gt;.android.tsx&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="s2"&gt;.native.js&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="s2"&gt;.native.ts&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="s2"&gt;.native.tsx&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="s2"&gt;.js&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="s2"&gt;.ts&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="s2"&gt;.tsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  iOS / iPadOS
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Podfile
&lt;/h4&gt;

&lt;p&gt;First, we fix our previous install error by now pointing to our root's &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- require_relative '../node_modules/react-native/scripts/react_native_pods'
&lt;/span&gt;&lt;span class="gi"&gt;+ require_relative '../../../node_modules/react-native/scripts/react_native_pods'
&lt;/span&gt;&lt;span class="gd"&gt;- require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
&lt;/span&gt;&lt;span class="gi"&gt;+ require_relative '../../../node_modules/@react-native-community/cli-platform-ios/native_modules'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can confirm if this worked by installing our pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx pod &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Xcode (workspace) - Signing &amp;amp; Capabilities
&lt;/h4&gt;

&lt;p&gt;Add your development team to build the project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Xcode (workspace) - Build Phases
&lt;/h4&gt;

&lt;p&gt;Nothing special here. We just adjust the paths like in CocoaPods.&lt;/p&gt;

&lt;h5&gt;
  
  
  Start Packager
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" &amp;gt; "${SRCROOT}/../node_modules/react-native/scripts/.packager.env"
&lt;/span&gt;&lt;span class="gi"&gt;+ echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" &amp;gt; "${SRCROOT}/../../../node_modules/react-native/scripts/.packager.env"
&lt;/span&gt;
- open "$SRCROOT/../node_modules/react-native/scripts/launchPackager.command" || echo "Can't start packager automatically"
&lt;span class="gi"&gt;+ open "$SRCROOT/../../../node_modules/react-native/scripts/launchPackager.command" || echo "Can't start packager automatically"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Xcode (workspace) - Bundle React Native code and images
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- ../node_modules/react-native/scripts/react-native-xcode.sh
&lt;/span&gt;&lt;span class="gi"&gt;+ ../../../node_modules/react-native/scripts/react-native-xcode.sh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Build Settings
&lt;/h4&gt;

&lt;h5&gt;
  
  
  User-Defined
&lt;/h5&gt;

&lt;p&gt;Add a user-defined setting (+ sign at the top menu bar) &lt;code&gt;RCT_NO_LAUNCH_PACKAGER&lt;/code&gt; with the value &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Android
&lt;/h3&gt;

&lt;p&gt;Getting things to work on Android is just a matter of adding paths for &lt;code&gt;hermes&lt;/code&gt; + &lt;code&gt;react-native&lt;/code&gt; cli and updating the existing ones.&lt;/p&gt;

&lt;h4&gt;
  
  
  android/build.gradle
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;maven {
&lt;/span&gt;    // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
&lt;span class="gd"&gt;-   url("$rootDir/../node_modules/react-native/android")
&lt;/span&gt;&lt;span class="gi"&gt;+   url("$rootDir/../../../node_modules/react-native/android")
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
maven {
    // Android JSC is installed from npm
&lt;span class="gd"&gt;-   url("$rootDir/../node_modules/jsc-android/dist")
&lt;/span&gt;&lt;span class="gi"&gt;+   url("$rootDir/../../../node_modules/jsc-android/dist")
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  android/settings.gradle
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
&lt;/span&gt;&lt;span class="gi"&gt;+ apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  app/build.gradle
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;project.ext.react = [
&lt;/span&gt;&lt;span class="gd"&gt;-  enableHermes: false,  // clean and rebuild if changing
&lt;/span&gt;&lt;span class="gi"&gt;+  enableHermes: true,  // clean and rebuild if changing
+  hermesCommand: "../../../../node_modules/hermes-engine/%OS-BIN%/hermesc",
+  composeSourceMapsPath: "../../node_modules/react-native/scripts/compose-source-maps.js",
+  cliPath: "../../node_modules/react-native/cli.js"
&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;

&lt;span class="gd"&gt;- apply from: "../../node_modules/react-native/react.gradle"
&lt;/span&gt;&lt;span class="gi"&gt;+ apply from: "../../node_modules/react-native/react.gradle"
&lt;/span&gt;
- def hermesPath = "../../node_modules/hermes-engine/android/";
&lt;span class="gi"&gt;+ def hermesPath = "../../../../node_modules/hermes-engine/android/";
&lt;/span&gt;
- apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
&lt;span class="gi"&gt;+ apply from: file("../../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing the Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn ios
yarn android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next.js
&lt;/h2&gt;

&lt;p&gt;Fortunately adding a Next.js project is more straightforward. All we need to do is delete &lt;code&gt;package-lock.json&lt;/code&gt; (we use yarn not npm) and remove our root dependencies from the template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest &lt;span class="nt"&gt;--ts&lt;/span&gt; web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;package-lock.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn remove react react-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;{&lt;/span&gt;
+ "private": true,
&lt;span class="gi"&gt;+ "name": "@monorepo/web",
+ "version": "1.0.0",
&lt;/span&gt;  "main": "index.js",
  "author": "ecklf",
  "license": "MIT",
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>react</category>
      <category>reactnative</category>
      <category>nextjs</category>
      <category>yarnworkspaces</category>
    </item>
    <item>
      <title>Configuring 2FA on your Linux Server</title>
      <dc:creator>Florentin / 珞辰</dc:creator>
      <pubDate>Wed, 16 Jun 2021 21:36:03 +0000</pubDate>
      <link>https://dev.to/ecklf/configuring-2fa-on-your-linux-server-2fjm</link>
      <guid>https://dev.to/ecklf/configuring-2fa-on-your-linux-server-2fjm</guid>
      <description>&lt;h2&gt;
  
  
  Recommended Setup
&lt;/h2&gt;

&lt;p&gt;Update your server to the latest packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt upgrade &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a non-root user with sudo privileges.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adduser username
usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;username
su username
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Time-based One-time Password Authentication
&lt;/h2&gt;

&lt;p&gt;First, install the Google Authenticator PAM module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;libpam-google-authenticator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the &lt;code&gt;google-authenticator&lt;/code&gt; binary to create a new secret key (keep this one safe).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;google-authenticator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After completing the process, you can scan/enter the code in an Authenticator App.&lt;/p&gt;

&lt;p&gt;I've chosen the following settings for this example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Do you want authentication tokens to be time-based (y/n) y

Do you want me to update your "/home/username/.google_authenticator" file? (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) n

By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) y

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To finish the Setup, we need to add the authentication module to the OpenSSH daemon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/pam.d/sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; Standard Un*x password updating.
&lt;span class="err"&gt;@include&lt;/span&gt; common-password
&lt;span class="gi"&gt;+ auth required pam_google_authenticator.so
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt; We created a non-root user with sudo privileges earlier, so we can disable
&lt;span class="gd"&gt;- PermitRootLogin yes
&lt;/span&gt;&lt;span class="gi"&gt;+ PermitRootLogin no
&lt;/span&gt;
- ChallengeResponseAuthentication no
&lt;span class="gi"&gt;+ ChallengeResponseAuthentication yes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// Restart to pick up changes
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hardware-based Authentication with YubiKey
&lt;/h2&gt;

&lt;p&gt;Check if OpenSSH 8.2 or higher is installed on your server for FIDO (Fast Identity Online) U2F support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsb_release &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ssh &lt;span class="nt"&gt;-V&lt;/span&gt;
Description:    Ubuntu 20.04.2 LTS
OpenSSH_8.2p1 Ubuntu-4ubuntu0.2, OpenSSL 1.1.1f  31 Mar 2020
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new SSH key pair with &lt;code&gt;ecdsa-sk&lt;/code&gt; or &lt;code&gt;ed25519-sk&lt;/code&gt;. Please note that &lt;code&gt;ed25519-sk&lt;/code&gt; only works if your YubiKey runs on Firmware &lt;code&gt;&amp;gt;=5.2.3&lt;/code&gt;.&lt;br&gt;
To determine your firmware, you can use &lt;code&gt;ykman&lt;/code&gt; or &lt;code&gt;YubiKey manager&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;ykman
ykman list
Firmware version: 5.2.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// Using ed25519-sk since Firmware of my YubiKey &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;5.2.3
// macOS may throw &lt;span class="s2"&gt;"unknown key type ed25519-sk"&lt;/span&gt;, run &lt;span class="s2"&gt;"brew install openssh"&lt;/span&gt; to fix this issue
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519-sk &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-yk_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you didn't set up a FIDO PIN, the first thing you should see is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You may need to touch your authenticator to authorize key generation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Touch the YubiKey disk and finish the Setup.&lt;/p&gt;

&lt;p&gt;For the last step, you'll add the generated public key to your server's &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/hostname-yk_id username@IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logging into the server will now ask you for a password (if defined) and a physical touch on your YubiKey.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/hostname-yk_id username@IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can repeat the above steps for multiple keys.&lt;/p&gt;

</description>
      <category>server</category>
      <category>linux</category>
      <category>yubikey</category>
      <category>security</category>
    </item>
    <item>
      <title>Why Tailwind? A long term user perspective</title>
      <dc:creator>Florentin / 珞辰</dc:creator>
      <pubDate>Mon, 10 May 2021 07:37:11 +0000</pubDate>
      <link>https://dev.to/ecklf/why-tailwind-a-long-term-user-perspective-20o5</link>
      <guid>https://dev.to/ecklf/why-tailwind-a-long-term-user-perspective-20o5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; I don't want to engage in the drama. I just think it's important to share experiences to help people make a choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prelude
&lt;/h3&gt;

&lt;p&gt;I've been using &lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt; for roughly 3 years now, and it is currently my go-to solution for starting a new project.&lt;/p&gt;

&lt;p&gt;As for any of my favorite tools, I love recommending them to other fellow developers. Thus over the years, I've engaged in many conversations discussing the pros and cons of using it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The following is a collection of statements I've encountered and my stance on why I think they are not justified.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  “Tailwind is for people who don't want / are too lazy to learn CSS”
&lt;/h3&gt;

&lt;p&gt;Let's start with my backstory of how I learned about Tailwind. In 2018 I decided to volunteer for a project to gain more experience building things for the web. My CSS knowledge back then was mediocre at best, and therefore I didn't enjoy participating in styling the frontend. Soon after, the project's management wanted a fresh look. One guy suggested: "Let's use Tailwind for the refresh. We use it at &lt;em&gt;(name of one of the biggest companies in Germany)&lt;/em&gt; and we love it". So I took this opportunity to learn to style for real this time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So what was the experience of learning CSS with Tailwind?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Spoiler:&lt;/strong&gt; It made me understand CSS more&lt;/p&gt;

&lt;p&gt;The reason why I didn't know much about CSS was that it just felt uncomfortable to learn. I would consider myself as a person who has a lot of grit when it comes to learning new things, but I kept getting frustrated about figuring out where to put which property to make things work. Utility classes just eliminated this issue for me because you directly see what styling affects which element. When it comes to learning new things, the initial step is the hardest, and I think utility-first played the leading role in making it &lt;em&gt;click&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Another point I would like to bring up is to figure out how to learn to make things look pretty. Most of the modern web has styling hidden behind hashed class names which may be more performant but are super intimidating for a beginner.&lt;/p&gt;

&lt;p&gt;Especially for Tailwind, there are tons of resources to learn from. High-quality open-source projects like &lt;a href="https://github.com/vercel/commerce"&gt;Vercel's commerce&lt;/a&gt;, component platforms like &lt;a href="https://tailwindcomponents.com"&gt;tailwindcomponents&lt;/a&gt; or if you prefer videos the &lt;a href="https://www.youtube.com/tailwindlabs"&gt;Tailwind Labs YouTube&lt;/a&gt; channel. There are also tools like &lt;a href="https://usewindy.com"&gt;Windy&lt;/a&gt;, which allows you to transform page content into Tailwind-styled markup to get a better understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  “You need to learn the syntax and neglect vanilla CSS”
&lt;/h3&gt;

&lt;p&gt;Personally, readability has never been an issue for me to begin with, but starting from &lt;code&gt;v1&lt;/code&gt;, Tailwind has seen massive improvements in this regard. At this point, it feels like writing CSS properties in a &lt;code&gt;kebab-case&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;Some people have also raised concerns that switching to utility classes for too long will make their vanilla CSS suffer. From personal experience, I can say that I worked on projects that only use &lt;code&gt;CSS-in-JS&lt;/code&gt; libraries and I didn't face any issues transitioning over to writing regular syntax.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CSS-in-JS&lt;/code&gt; is often praised as the solution when I ask people what they are using instead. Whether or not to use &lt;code&gt;CSS-in-JS&lt;/code&gt; boils down to preference, but be aware you can use both with libraries like &lt;a href="https://github.com/ben-rogerson/twin.macro"&gt;twin.macro&lt;/a&gt; (see Tailwind adds no value and does not scale on why you would still want it). As for my part, I find it easier to modify my styling without jumping around my component's file.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Utility classes make the HTML look ugly and refactoring harder”
&lt;/h3&gt;

&lt;p&gt;Bloating the HTML by eliminating the separation of concerns is a trade-off. But what are the alternatives that work better? All I can say that I just &lt;em&gt;love&lt;/em&gt; to get an idea of how something looks by just looking at utility-flavored markup. Using components also makes this issue and the hassle of refactoring way less relevant than people make it out to be. It just takes a different approach that admittedly takes some time getting used to.&lt;/p&gt;

&lt;p&gt;I can suggest taking a look at &lt;a href="https://www.youtube.com/watch?v=J_7_mnFSLDg"&gt;Tailwind CSS Best Practice Patterns&lt;/a&gt; and Robin Malfait's &lt;a href="https://gist.github.com/RobinMalfait/490a0560a7cfde985d435ad93f8094c5"&gt;Good Example&lt;/a&gt; to get an idea of how that would look like. For consistency, I can also recommend having a convention for class ordering or using &lt;a href="https://github.com/ryanhhhh/headwind"&gt;headwind&lt;/a&gt; as an opinionated class sorter.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Building pixel-perfect designs takes longer”
&lt;/h3&gt;

&lt;p&gt;Fortunately, this won't be an issue anymore, and you can already opt-in today! Tailwind Labs has released a &lt;a href="https://tailwindcss.com/docs/just-in-time-mode"&gt;Just-in-Time (JIT)&lt;/a&gt; preview, which allows you to inline arbitrary spacing and color values for "single-use-values". Don't overdo it though.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Tailwind builds are slow and require extra tooling”
&lt;/h3&gt;

&lt;p&gt;I mention this since JIT mode is still in preview but will eventually eliminate this issue as well. Tailwind generates lots of classes, so you need to strip out the unused ones for production builds. Back in older versions, this required setting up &lt;a href="https://github.com/FullHuman/purgecss"&gt;purgecss&lt;/a&gt;. Credit where credit's due it later was merged into Tailwind to make it even easier for people to get optimized production builds.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Tailwind does not provide XYZ”
&lt;/h3&gt;

&lt;p&gt;Tailwind was designed with customization in mind, and if you don't want to make a Plugin for it, nothing will stop you from adding a bit of custom CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Tailwind adds no value and does not scale”
&lt;/h3&gt;

&lt;p&gt;I often hear this from people who presumably never gave the framework a proper try.&lt;/p&gt;

&lt;p&gt;The most value Tailwind adds to my projects can be summarized in two words: &lt;strong&gt;Design constraints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using the ecosystem as a team will make you appreciate Tailwind in a scaling project as it's just so convenient to work with it. I think many people just get the wrong idea by looking at Tailwind's defaults. You can narrow down choices, or you can ditch them altogether. You don't need to keep names like &lt;code&gt;text-red-500&lt;/code&gt; and go with more generic terms. What &lt;em&gt;does&lt;/em&gt; matter is that developers can adhere to style choices from one single &lt;em&gt;Source of Truth&lt;/em&gt;. All that's left is to install autocompletion for the supported editors, and you will find yourself creating layouts at a huge pace.&lt;/p&gt;

&lt;h2&gt;
  
  
  So should I use it?
&lt;/h2&gt;

&lt;p&gt;Everything is a trade-off, and I won't force you to use Tailwind if you don't like it.&lt;/p&gt;

&lt;p&gt;Will / is there a better solution? Maybe, but I think the resources and tooling are great (if not the best) right now.&lt;/p&gt;

&lt;p&gt;Just give it a try. You can join the official Discord &lt;a href="https://discord.gg/BdzC2dWY"&gt;here&lt;/a&gt; if you have any questions.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>html</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
