<?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: Hieu Nguyen</title>
    <description>The latest articles on DEV Community by Hieu Nguyen (@hieussr).</description>
    <link>https://dev.to/hieussr</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%2F486536%2Fa5632ccc-3880-4a0b-b3b3-264f27c22516.jpg</url>
      <title>DEV Community: Hieu Nguyen</title>
      <link>https://dev.to/hieussr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hieussr"/>
    <language>en</language>
    <item>
      <title>How to Setup Monorepos with Git for JavaScript and TypeScript</title>
      <dc:creator>Hieu Nguyen</dc:creator>
      <pubDate>Sun, 15 Aug 2021 09:28:49 +0000</pubDate>
      <link>https://dev.to/hieussr/how-to-setup-monorepos-with-git-for-javascript-and-typescript-2ab5</link>
      <guid>https://dev.to/hieussr/how-to-setup-monorepos-with-git-for-javascript-and-typescript-2ab5</guid>
      <description>&lt;p&gt;When your app gets bigger, managing files within a project gets more complex. You may start to have modules shared between front-end and back-end projects. Often, you also need to manage different versions of those modules.&lt;/p&gt;

&lt;p&gt;A monorepo is a way to structure your projects to manage that kind of complexity all in one place.&lt;/p&gt;

&lt;p&gt;I failed to set up a monorepo with &lt;a href="%7Bhttps://lerna.js.org/%7D"&gt;Lerna&lt;/a&gt; a few times. &lt;a href="%7Bhttps://github.com/wclr/yalc%7D"&gt;Yalc&lt;/a&gt; and &lt;a href="%7Bhttps://classic.yarnpkg.com/en/docs/workspaces/%7D"&gt;Yarn Workspace&lt;/a&gt; can be troublesome when I need to move a project out of the monorepo.&lt;/p&gt;

&lt;p&gt;Finally, I found a way to make it work using &lt;a href="%7Bhttps://git-scm.com/book/en/v2/Git-Tools-Submodules%7D"&gt;git submodules&lt;/a&gt;. Git is great for resolving code conflicts. Git branches can be used for versioning. You can have unlimited private repositories for free when using Github or Gitlab. Besides, with TypeScript or JavaScript (using webpack), you can configure module aliases to create beautiful import paths.&lt;/p&gt;

&lt;p&gt;In this post, I’ll show you how to set up a dependency, structure your project, and configure module aliases for a monorepo. And discuss the disadvantage I’ve encountered of using this setup.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See &lt;a href="%7Bhttps://github.com/hieunc229/git-monorepo-project%7D"&gt;git-monorepo-project&lt;/a&gt;&lt;/em&gt; on Github for the final result*&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setup a dependency
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YMHv8oAP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miami.inverr.com/PiIl%3DLQwO/WtwH0zl2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YMHv8oAP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miami.inverr.com/PiIl%3DLQwO/WtwH0zl2m.png" alt="A dependency is a submodule"&gt;&lt;/a&gt;A dependency is a git repository. It can either contain a complete module (i.e with package.json and bundled/transpired JavaScripts files), or it may only have plain JavaScript or Typescript files.&lt;/p&gt;

&lt;p&gt;Besides, we often need different versions of the dependency, known as versioning. Which allows us to make changes in a specific version, without affecting projects that use other versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a dependency repository
&lt;/h3&gt;

&lt;p&gt;You can create a &lt;a href="%7Bhttps://docs.github.com/en/get-started/quickstart/create-a-repo%7D"&gt;public or private repository&lt;/a&gt; (make sure contributors have access), and push the code there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency versioning
&lt;/h3&gt;

&lt;p&gt;For versioning, you can &lt;a href="%7Bhttps://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/making-changes-in-a-branch/managing-branches%7D"&gt;use branches&lt;/a&gt;. For example, using the &lt;strong&gt;main&lt;/strong&gt; branch for the latest version, &lt;strong&gt;&lt;a href="mailto:stable@v0.0.1"&gt;stable@v0.0.1&lt;/a&gt;&lt;/strong&gt; branch for the stable 0.0.1 version, and so on&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Structure a project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sjVMfnH4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miami.inverr.com/PiIl%3DLQwO/h6U4pR%3DqU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sjVMfnH4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miami.inverr.com/PiIl%3DLQwO/h6U4pR%3DqU.png" alt="How to structure monorepo project with Git"&gt;&lt;/a&gt;The main idea of setting up a monorepo with git is to add dependencies (in step 1) as submodules.&lt;/p&gt;

&lt;p&gt;In the project structure, a submodule is a local directory. Hence, we can easily import and treat them as a local directory. And because it’s a git repository, any committed changes will also apply to the copies in other projects (after pulling the changes)&lt;/p&gt;

&lt;h3&gt;
  
  
  Project structures
&lt;/h3&gt;

&lt;p&gt;One way to structure your project is to have all dependencies under the &lt;strong&gt;src/packages&lt;/strong&gt; directory. Here’s a project directory tree example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-root/
    ├── .gitsubmodules
    ├── package.json
    ├── tsconfig.json
    ├── webpack.config.js
    └── src/
        ├── index.ts
        ├── packages/
        │   ├── module1 (submodule)/ 
        │   │   ├── package.json
        │   │   └── src/
        │   │       └── index.ts
        │   ├── module2 (submodule)/
        │   │   └── index.ts
        │   └── ...
        └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;a href="%7Bhttps://github.com/hieunc229/git-monorepo-project%7D"&gt;git-monorepo-project&lt;/a&gt; for example&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a dependency
&lt;/h3&gt;

&lt;p&gt;After creating a dependency repository, you can add it as a submodule using the &lt;strong&gt;git submodule add&lt;/strong&gt; command, and store it under the &lt;strong&gt;src/packages&lt;/strong&gt; directory. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git submodule add https://github.com/username/module-name.git src/packages/module-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To add a specific version of the dependency, use the &lt;strong&gt;--b&lt;/strong&gt; flag when adding the submodule. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git submodule add -b stable@v0.0.1 https://github.com/username/module-name.git src/packages/module-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can import the new dependency as a local directory. For example, &lt;strong&gt;import Module1 from “../packages/module1”&lt;/strong&gt;;&lt;/p&gt;

&lt;h3&gt;
  
  
  Working from another computer
&lt;/h3&gt;

&lt;p&gt;After setting up the monorepo, it’s easy to install a project or a dependency in another computer. It's useful when you have many workstations (ie. PC, laptop), or if you have someone working with you.&lt;/p&gt;

&lt;p&gt;To set up the monorepo in another computer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the main project with the &lt;strong&gt;--recursive&lt;/strong&gt; flag on the new computer. It will download the repository and all the submodules. For example: &lt;strong&gt;git clone --recursive &lt;a href="https://github.com/username/main-project.git"&gt;https://github.com/username/main-project.git&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Install node modules (if needed) using “&lt;strong&gt;npm install&lt;/strong&gt;”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now the project should be ready to work on!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Configure module aliases
&lt;/h2&gt;

&lt;p&gt;A common problem when setting up a monorepo as above is that it results in ugly import paths. For example, the import path in the &lt;strong&gt;src/pages/dashboard/profile/ProfileMenu.tsx&lt;/strong&gt; file will be "&lt;strong&gt;../../../packages/module1&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;Luckily, you can set up module aliases for shorter import paths. Note: if you're using webpack to transpile Typescript, you'll need to set up module aliases for both JavaScript and Typescript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure module aliases for JavaScript
&lt;/h3&gt;

&lt;p&gt;You can configure the module alias for webpack in the &lt;strong&gt;webpack.config.js&lt;/strong&gt; file, using the resolve.alias configuration. For React apps created with &lt;a href="%7Bhttps://create-react-app.dev/%7D"&gt;CRA&lt;/a&gt;, you can use &lt;a href="%7Bhttps://www.npmjs.com/package/react-app-rewired%7D"&gt;react-app-rewired&lt;/a&gt; to override the webpack configurations.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
    …,
    resolve: {
        alias: {
            // import Module1 from “module1”
            "module1": "path/to/src/packages/module1",

            // this config allow importing any modules 
            // under src/packages directory
            // i.e import Module1 from “packages/module1”
            "packages": "path/to/src/packages",
            ...
        }
    }
}  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;a href="%7Bhttps://github.com/hieunc229/https://github.com/hieunc229/git-monorepo-project/blob/master/webpack.config.js%7D"&gt;webpack.config.js&lt;/a&gt; file for example&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure module aliases for Typescript
&lt;/h3&gt;

&lt;p&gt;You can configure module aliases for Typescript in the &lt;strong&gt;tsconfig.json&lt;/strong&gt; file, using the &lt;strong&gt;compilerOptions.paths&lt;/strong&gt; configuration.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "compilerOptions": {
        …,
        "baseUrl": "./src",
        "paths": {

            // import Module1 from “module1”
            "module1": "packages/module1",
            "module1/*": "packages/module1/*",

            // this config allow importing any modules 
            // under src/packages directory
            // i.e import Module1 from “packages/module1”
            "packages": "packages",
            "packages/*": "packages/*",
            ...
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure the "&lt;strong&gt;baseUrl&lt;/strong&gt;" (as above) is also present. It helps the compiler resolve dependency paths. See the &lt;a href="%7Bhttps://github.com/hieunc229/git-monorepo-project/blob/master/tsconfig.extends.json%7D"&gt;tsconfig.extends.json&lt;/a&gt; file for example&lt;/p&gt;




&lt;p&gt;Once you have set up repositories for dependencies, structured your project as above, and configured your module aliases - your monorepo is ready!&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Disadvantages
&lt;/h2&gt;

&lt;p&gt;I’ve been using this approach for over a year. Here are a few issues you could encounter, and how to deal with them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making dependencies
&lt;/h3&gt;

&lt;p&gt;In case you’re trying to convert an existing project to a monorepo structure, it might take some time to set up. For example, separate some parts of the code and push them into their own repository.&lt;/p&gt;

&lt;p&gt;But afterward, they are should be more independent, make it much easier to work with, or moving around.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with dependencies of a dependency
&lt;/h3&gt;

&lt;p&gt;It’s quite common when you’re using a dependency, which depends on other modules. In this case, I’ll install them in the main project.&lt;/p&gt;

&lt;p&gt;Let’s say Project-1uses Module-A, Module-A uses Module-B, and they all belong to the monorepo. And Module-B was added to Module-A as above. In this case, I’ll need to do the same for Project-1. This means adding Module-B s a submodule, and config the module alias.&lt;/p&gt;

&lt;p&gt;Also, make sure the module aliases should be the same in both Project-1 and Module-A.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;It’s often difficult to manage multiple projects and dependencies in a big app. A monorepo is a way to structure them all in a single repository, making it easier to work with.&lt;/p&gt;

&lt;p&gt;Git provides submodules, branches, and the ability to manage code conflicts, which is useful for setting up a monorepo.&lt;/p&gt;

&lt;p&gt;You can set up monorepo with git by separating each dependency into its own repository, then adding them as submodules. Besides, we get to configure module aliases to attain nice and readable import paths.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="%7Bhttps://twitter.com/poppacalypse%7D"&gt;Carl Poppa&lt;/a&gt; for proofreading and feedback.&lt;/p&gt;

</description>
      <category>monorepo</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Create a Glowing Loader with CSS and HTML</title>
      <dc:creator>Hieu Nguyen</dc:creator>
      <pubDate>Fri, 16 Apr 2021 20:52:57 +0000</pubDate>
      <link>https://dev.to/hieussr/create-a-glowing-loader-in-css-and-html-3omd</link>
      <guid>https://dev.to/hieussr/create-a-glowing-loader-in-css-and-html-3omd</guid>
      <description>&lt;p&gt;We want our products to leave a good impression on first-time users. So whenever we build something, we make it possible first, then ask if it can be a little unique. And the glowing loader is one of those attempts.&lt;/p&gt;

&lt;p&gt;Here is the original version on &lt;a href="https://jsfiddle.net/hieunc229/wyk0qg9L/33/" rel="noopener noreferrer"&gt;jsfiddle (33 changes)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your own loader in HTML and CSS
&lt;/h2&gt;

&lt;p&gt;The loader design has a container and 3 elements: the background button, glowing spinner, and the logo. While the button and the spinner are all overlay layers (use absolute positioning). &lt;/p&gt;

&lt;p&gt;Let's start with a straigt forward HTML markup as following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"logo-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"spinner"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"logo"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"yourlogo.svg"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"28"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1. The container
&lt;/h3&gt;

&lt;p&gt;The main point of the container is to groups the elements together, and positioning its children &lt;code&gt;logo&lt;/code&gt; in the center. Let's use &lt;code&gt;flex&lt;/code&gt; in this example.&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="nc"&gt;.logo-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* align children in the center */&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* a circle with 60x60 pixels */&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&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;Now we have a plain logo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwilson.inverr.com%2Fmedia%2Fglowing-spinner-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwilson.inverr.com%2Fmedia%2Fglowing-spinner-1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before going into the button background and the spinner. We need to make sure they are overlay layers, by applying absolute positioning as following.&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="nc"&gt;.background&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.spinner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&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;
  
  
  2. The button background
&lt;/h3&gt;

&lt;p&gt;The button background is a circle, with a matte-ish gradient and shadows. Here's one way to do it&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;/* The button background layer */&lt;/span&gt;
&lt;span class="nc"&gt;.background&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#0f1013&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#252730&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;-1px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;inset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt; &lt;span class="m"&gt;-2px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;inset&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;It'll look like this now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwilson.inverr.com%2Fmedia%2Fglowing-spinner-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwilson.inverr.com%2Fmedia%2Fglowing-spinner-2.png"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Bring logo to front
&lt;/h3&gt;

&lt;p&gt;In case you can't see your logo, you will need to use &lt;code&gt;z-index&lt;/code&gt; to bring the &lt;code&gt;logo&lt;/code&gt; on top. Simple as the following CSS:&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="nc"&gt;.logo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&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;
  
  
  4. Glowing spinner
&lt;/h3&gt;

&lt;p&gt;Glowing spinner layer use a &lt;code&gt;top-border&lt;/code&gt; attribute, with shadow to add the glowing effect. It can be done as following:&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="nc"&gt;.spinner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ae34db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* glowing with shadow (30% of #ae34db) */&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;-5px&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;#ae34db4&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* add spin animation */&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;spin&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="n"&gt;infinite&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;Then add the animation keyframes. It's basically a keyframe that rotate the light spinner in a 360 degree circle&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;spin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;360deg&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;You should see a glowing spinner with your logo inside. Here is our final result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwilson.inverr.com%2Fmedia%2Fglowing-spinner-3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwilson.inverr.com%2Fmedia%2Fglowing-spinner-3.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can play with the &lt;code&gt;spin&lt;/code&gt; keyframe to change the glowing colors, or spread the shadow more to make it like a siren light.&lt;/p&gt;




&lt;p&gt;Now you know how to create a glowing loader. It contains a matte-ish gradient background, with a glowing spinner. You can check out the final result &lt;a href="https://jsfiddle.net/m52jdu3e/" rel="noopener noreferrer"&gt;on jsfiddle&lt;/a&gt;;&lt;/p&gt;

&lt;p&gt;Let us know what you think and thanks for the support.&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>animate</category>
      <category>loader</category>
    </item>
    <item>
      <title>A simple mistake that leaks 20M users' data</title>
      <dc:creator>Hieu Nguyen</dc:creator>
      <pubDate>Mon, 22 Mar 2021 09:07:27 +0000</pubDate>
      <link>https://dev.to/hieussr/a-simple-mistake-that-leaks-20m-users-data-2pep</link>
      <guid>https://dev.to/hieussr/a-simple-mistake-that-leaks-20m-users-data-2pep</guid>
      <description>&lt;p&gt;It’s one of the silliest mistakes you can make. But who knows. When you get busy, things can happen.&lt;/p&gt;

&lt;p&gt;A few days ago, I was on this page — a social platform, with 20M users. Wandering on a user profile, then I opened his followers page. Must have been switching between debugging my website, out of curiosity, I had a look at a XHR data request on their website.&lt;/p&gt;

&lt;p&gt;And baam, the request contains a list of users, with personal information. Also the access tokens from their social platform accounts.&lt;/p&gt;

&lt;p&gt;With those access tokens, hackers use them to gain control over social accounts. I guess a large portion of those tokens is still valid. Besides, personal data with contact are valuable. Many companies want them for cold calls/emails, risking users’ privacy.&lt;/p&gt;

&lt;p&gt;For 20 millions users, this issue is serious.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mOv0vbox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/bArWrBw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mOv0vbox--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/bArWrBw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is not a good explanation for this mistake. The tokens doesn’t look fake. Maybe, their team built the API for convenience, without being careful enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevent this mistake as a developer
&lt;/h2&gt;

&lt;p&gt;You can prevent this mistake by following Principle of least privilege. Which limiting permission for users to perform any action, only allow what they have to, no more. And besides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store sensitive info in a different table&lt;/li&gt;
&lt;li&gt;Build API with single purpose (i.e don't use 1 endpoint to get user info, and their sensitive data)&lt;/li&gt;
&lt;li&gt;Always validate if request has the right permission&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Especially, double check if you have a page that list users (i.e list of followers). And prevent hackers craw your APIs (i.e using rate-limit, IP blacklist)&lt;/p&gt;

&lt;h2&gt;
  
  
  Some afterthought.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using social platform for authentication (signup, login) is still dangerous. Even though the platform itself is secured, a few apps you connected to are secured. But if one app leaks users’ access token, that’s it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maybe passwordless authorization (login with a magic-link) is a near and brighter future. Unless you use the same password everywhere, and one of your online shopping being hacked. Or worst, that online shopping doesn’t securely store passwords.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sometimes rushing to deliver a project can cause mistake like this :P. Also make it easier for anyone to report incident like this.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;P/s: I've contacted the team to fix the issue mentioned above. A friendly reminder to check double your API with sensitive data. We’re all busy, mistakes can happen.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
    </item>
    <item>
      <title>Not Another Productivity Hack</title>
      <dc:creator>Hieu Nguyen</dc:creator>
      <pubDate>Tue, 13 Oct 2020 12:48:55 +0000</pubDate>
      <link>https://dev.to/hieussr/not-another-productivity-hack-43g5</link>
      <guid>https://dev.to/hieussr/not-another-productivity-hack-43g5</guid>
      <description>&lt;p&gt;When I was younger, most of my time spent writing code, with 5 hours of sleep a day. I often read about productivity hacks, hope to squeeze a little more works. But nothing really works.&lt;/p&gt;

&lt;p&gt;Having an unhealthy lifestyle drained my energy. In the past year, even if I wanted to, I can't do it anymore. I need more breaks, more sleep, and more time away from the projects.&lt;/p&gt;

&lt;p&gt;Turn out, it was for the better.&lt;/p&gt;

&lt;p&gt;Most personal productivity hacks are to kick-start some unused parts of our energy. But when you have already drained them out, there is no more energy to kick-start. You're just working too much.&lt;/p&gt;

&lt;p&gt;My change of habit helps me feel much better.&lt;/p&gt;

&lt;p&gt;Almost every day, I sleep 7:30+ hours, often have a noon nap for a productive afternoon. I started to eat on time, 3 meals a day. For the last year, I exercise after work, at least 5 times a week, 30 mins each.&lt;/p&gt;

&lt;p&gt;In case you're drained out, and looking for productivity hacks. Maybe you don't need one. Maybe all you need is to take care of yourself better. &lt;/p&gt;

&lt;p&gt;Sleep more, eat more, be healthier&lt;/p&gt;

&lt;p&gt;Posted on &lt;a href="https://hieunc.com/posts/2u5@LYnbgX-not-another-productivity-hack"&gt;Not Another Productivity Hack — Hieu's Blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Fixing lazyload images with React Server-Side Render</title>
      <dc:creator>Hieu Nguyen</dc:creator>
      <pubDate>Sat, 10 Oct 2020 03:04:21 +0000</pubDate>
      <link>https://dev.to/hieussr/fixing-lazyload-images-with-react-server-side-render-5c3f</link>
      <guid>https://dev.to/hieussr/fixing-lazyload-images-with-react-server-side-render-5c3f</guid>
      <description>&lt;p&gt;I've recently realized my server render pages has a problem. The offscreen lazyload image don't show up.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In case you don't know, offscreen images won't load until it shows up onscreen. It helps to reduce unecessery loading to improve performance.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As usual, I impletemented image lazy loading by (1) get all img elements, (2) move photo URL to &lt;code&gt;data-src&lt;/code&gt;, and remove &lt;code&gt;src&lt;/code&gt;. Then (3) observe whenever each of them scrolled onscreen, move the photo URL back to &lt;code&gt;src&lt;/code&gt; to load.&lt;/p&gt;

&lt;p&gt;It works when React running on the client-side. But when render on the server-side, the images don't show up.&lt;/p&gt;

&lt;p&gt;Turn out, it's a little different. As usual, the server will return a hydrated React page. Then the client will re-render the page again as a normal React page.&lt;/p&gt;

&lt;p&gt;The problem was, the lazyload function executed twice as well. The first time, &lt;code&gt;src&lt;/code&gt; is a link, and set to &lt;code&gt;empty&lt;/code&gt; after assigning the link to &lt;code&gt;data-src&lt;/code&gt;. Then the second time, &lt;code&gt;src&lt;/code&gt; was empty, and that empty value is again, assigned to &lt;code&gt;data-src&lt;/code&gt;. And when users viewing the page, it will start to load an empty value.&lt;/p&gt;

&lt;p&gt;I fixed this problem by checking if &lt;code&gt;src&lt;/code&gt; is not empty, before moving it to &lt;code&gt;data-src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In case you're looking for the code, here is a basic lazyImages.ts with some favors (add loading class, check for offscreen images)&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You can use the &lt;code&gt;lazyImages&lt;/code&gt; as following:&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="nx"&gt;lazyImages&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// offset before the image is scrolled into view&lt;/span&gt;
&lt;span class="nx"&gt;lazyImages&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;rootMargin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Posted on &lt;a href="https://hieunc.com/posts/6fNu3@O9xp-fixing-lazyload-images-with-react-server-side-render"&gt;Fixing lazyload images with React Server-Side Render&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>ssr</category>
      <category>hydration</category>
      <category>lazyload</category>
    </item>
  </channel>
</rss>
