<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Gonçalo Alves</title>
    <description>The latest articles on Forem by Gonçalo Alves (@iamgoncaloalves).</description>
    <link>https://forem.com/iamgoncaloalves</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%2F1407925%2F91b55a66-8527-4ea9-994d-b13d244422a3.jpg</url>
      <title>Forem: Gonçalo Alves</title>
      <link>https://forem.com/iamgoncaloalves</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/iamgoncaloalves"/>
    <language>en</language>
    <item>
      <title>How I Developed My First Neovim Plugin: A Step-by-Step Guide</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Thu, 19 Sep 2024 14:08:05 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/how-i-developed-my-first-neovim-plugin-a-step-by-step-guide-1lcb</link>
      <guid>https://forem.com/iamgoncaloalves/how-i-developed-my-first-neovim-plugin-a-step-by-step-guide-1lcb</guid>
      <description>&lt;p&gt;Are you a Neovim enthusiast looking to extend its functionality? Ever wondered how to create your own plugin and publish it up on Github? Look no further! In this post, I'll walk you through my journey of developing a simple "Hello World" plugin for Neovim. &lt;/p&gt;

&lt;p&gt;For those who want to see the finished product or follow along with the complete source code, you can check out the full repository here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/goncaloalves/neovim-plugin-hello-world" rel="noopener noreferrer"&gt;neovim-plugin-hello-world GitHub Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to star the repository if you find it helpful!&lt;/p&gt;

&lt;p&gt;Btw, I'm building a real useful Neovim plugin and I will share how I built this plugin here, so make sure you follow me if you don't wanna miss that.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Neovim, a hyper-extensible Vim-based text editor, allows users to create custom plugins to enhance their editing experience. In this tutorial, we'll create a simple plugin that adds a &lt;code&gt;:HelloWorld&lt;/code&gt; command and a keymapping to Neovim, which prints "Hello from Neovim!" when executed.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Setting Up the Development Environment
&lt;/h2&gt;

&lt;p&gt;Before we dive into coding, let's set up our development environment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ensure you have Neovim installed (version 0.5 or later recommended).&lt;/li&gt;
&lt;li&gt;Choose a directory for your plugin development. I used &lt;code&gt;~/development/neovim/neovim-plugins/&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Creating the Plugin Structure
&lt;/h2&gt;

&lt;p&gt;Before we dive into coding our specific plugin, it's important to understand the typical structure of a Neovim plugin. This structure follows conventions that determine how and when different parts of the plugin are loaded.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Anatomy of a Neovim Plugin
&lt;/h3&gt;

&lt;p&gt;A generic Neovim plugin usually has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── LICENSE
├── README.md
├── plugin/
│   └── plugin-file.lua
└── lua/
    └── plugin-name.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down each component:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Root Directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LICENSE&lt;/code&gt;: The license file for your plugin.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;README.md&lt;/code&gt;: Documentation and usage instructions for your plugin.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;plugin/&lt;/code&gt; Directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files in this directory are automatically executed when Neovim starts.&lt;/li&gt;
&lt;li&gt;This is useful for setting up global commands, autocommands, or keymaps that should be available immediately.&lt;/li&gt;
&lt;li&gt;Example: &lt;code&gt;plugin/plugin-file.lua&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;lua/&lt;/code&gt; Directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is where the main plugin code resides.&lt;/li&gt;
&lt;li&gt;Code here is only executed when explicitly required by the user or other parts of the plugin.&lt;/li&gt;
&lt;li&gt;There are two common ways to structure the main file:
a. Single file: &lt;code&gt;lua/plugin-name.lua&lt;/code&gt;
b. Directory with init file: &lt;code&gt;lua/plugin-name/init.lua&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Key Points About Plugin Structure
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;plugin/&lt;/code&gt; directory is for code that needs to run immediately when Neovim starts, regardless of whether the user explicitly requires the plugin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;lua/&lt;/code&gt; directory contains the main plugin code, which is only executed when required. This is where most of your plugin's functionality should be implemented.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Naming is important. The main file in the &lt;code&gt;lua/&lt;/code&gt; directory should typically match your plugin's name (e.g., &lt;code&gt;hello-world.lua&lt;/code&gt; for a plugin named "hello-world").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For simple plugins, a single file in the &lt;code&gt;lua/&lt;/code&gt; directory is often sufficient. For more complex plugins, you might use a directory with an &lt;code&gt;init.lua&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The main entry point of your plugin (in the &lt;code&gt;lua/&lt;/code&gt; directory) is what users will require to use your plugin. For example, users would use &lt;code&gt;require('hello-world')&lt;/code&gt; to load a plugin with the file &lt;code&gt;lua/hello-world.lua&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This structure allows for efficient loading of plugin code, with immediate execution of necessary setup code (in &lt;code&gt;plugin/&lt;/code&gt;) and on-demand loading of the main functionality (in &lt;code&gt;lua/&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Structure for Our Hello World Plugin
&lt;/h3&gt;

&lt;p&gt;For our simple "Hello World" plugin, we'll use the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;neovim-plugin-hello-world/
├── LICENSE
├── README.md
└── lua/
    └── neovim-plugin-hello-world.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our plugin doesn't need any immediate setup, we'll omit the &lt;code&gt;plugin/&lt;/code&gt; directory and focus on the main functionality in the &lt;code&gt;lua/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Let's create this structure in your chosen development directory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new directory for your plugin:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   mkdir -p neovim-plugin-hello-world/lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Navigate to the plugin directory:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   cd neovim-plugin-hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the main Lua file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   touch lua/neovim-plugin-hello-world.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a README.md file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   touch README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add a LICENSE file (choose an appropriate license for your project).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we have our plugin structure set up, we're ready to start writing the actual plugin code.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Writing the Plugin Code
&lt;/h2&gt;

&lt;p&gt;Let's create the main plugin file. Open &lt;code&gt;lua/neovim-plugin-hello-world.lua&lt;/code&gt; in your favorite text editor and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Main module for the Hello World plugin&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;-- Function to print the hello message&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;M&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello from Neovim!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;-- Function to set up the plugin (Most package managers expect the plugin to have a setup function)&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;M&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;-- Merge user options with defaults&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="c1"&gt;-- Create the user command&lt;/span&gt;
  &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nvim_create_user_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"HelloWorld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

  &lt;span class="c1"&gt;-- Set up a key mapping&lt;/span&gt;
  &lt;span class="c1"&gt;-- Use opts.keymap if provided, otherwise default to '&amp;lt;leader&amp;gt;hw'&lt;/span&gt;
  &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;keymap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;leader&amp;gt;hw'&lt;/span&gt;

  &lt;span class="c1"&gt;-- Create the keymap&lt;/span&gt;
  &lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;say_hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Say hello from our plugin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;silent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;-- Prevents the command from being echoed in the command line&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;-- Return the module&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down this code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We define a module &lt;code&gt;M&lt;/code&gt; that will contain all our plugin's functionality.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;say_hello()&lt;/code&gt; function is the core of our plugin. It simply prints a greeting message.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;setup()&lt;/code&gt; function is used to initialize our plugin with user-provided options:

&lt;ul&gt;
&lt;li&gt;It creates a user command &lt;code&gt;:HelloWorld&lt;/code&gt; that calls our &lt;code&gt;say_hello()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;It sets up a keymapping (default &lt;code&gt;&amp;lt;leader&amp;gt;hw&lt;/code&gt;) that also calls &lt;code&gt;say_hello()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We return the module at the end, making its functions available to users of our plugin.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Debugging Your Plugin
&lt;/h2&gt;

&lt;p&gt;To debug your plugin locally using &lt;code&gt;lazy.nvim&lt;/code&gt; with the &lt;code&gt;dir&lt;/code&gt; option:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Ensure you have &lt;code&gt;lazy.nvim&lt;/code&gt; installed in your Neovim configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modify your Neovim configuration to load your plugin locally. Add the following to your &lt;code&gt;init.lua&lt;/code&gt; or wherever you configure your plugins:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lazy"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"neovim-plugin-hello-world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~/development/neovim/neovim-plugins/neovim-plugin-hello-world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"neovim-plugin-hello-world"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="n"&gt;keymap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;leader&amp;gt;hello"&lt;/span&gt;  &lt;span class="c1"&gt;-- optional: override the default keymap&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;-- ... your other plugins ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Restart Neovim or run &lt;code&gt;:Lazy sync&lt;/code&gt; to load your plugin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test your plugin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try running the command &lt;code&gt;:HelloWorld&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use the keymapping (default &lt;code&gt;&amp;lt;leader&amp;gt;hw&lt;/code&gt; or your custom mapping) in normal mode&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should see "Hello from Neovim!" printed in the command line for both methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Iterative Development
&lt;/h3&gt;

&lt;p&gt;With this setup, you can easily iterate on your plugin:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make changes to your plugin code.&lt;/li&gt;
&lt;li&gt;Save the files.&lt;/li&gt;
&lt;li&gt;In Neovim, run &lt;code&gt;:Lazy reload neovim-plugin-hello-world&lt;/code&gt; to reload your plugin.&lt;/li&gt;
&lt;li&gt;Test the changes by running &lt;code&gt;:HelloWorld&lt;/code&gt; or using the keymapping.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow allows for rapid development and testing without constantly restarting Neovim or manually sourcing files.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Installing the Plugin Remotely
&lt;/h2&gt;

&lt;p&gt;When you're ready to share your plugin with the world, you'll need to push it to a GitHub repository. Here's how to make it installable via &lt;code&gt;lazy.nvim&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a GitHub repository for your plugin.&lt;/li&gt;
&lt;li&gt;Push your plugin code to the repository.&lt;/li&gt;
&lt;li&gt;Update your &lt;code&gt;README.md&lt;/code&gt; file with installation and usage instructions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a sample &lt;code&gt;README.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# neovim-plugin-hello-world&lt;/span&gt;

A simple Neovim plugin that adds a &lt;span class="sb"&gt;`:HelloWorld`&lt;/span&gt; command and keymapping.

&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class="gu"&gt;## Installation&lt;/span&gt;

Using &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;lazy.nvim&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github.com/folke/lazy.nvim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;:

{
  "yourusername/neovim-plugin-hello-world",
  opts = {
      keymap = "&lt;span class="nt"&gt;&amp;lt;leader&amp;gt;&lt;/span&gt;hello"  -- optional: override the default keymap
    }
}

&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
&lt;span class="gu"&gt;## Usage&lt;/span&gt;

After installation:
&lt;span class="p"&gt;1.&lt;/span&gt; Run &lt;span class="sb"&gt;`:HelloWorld`&lt;/span&gt; command in Neovim
&lt;span class="p"&gt;2.&lt;/span&gt; Or use the keymapping (default &lt;span class="sb"&gt;`&amp;lt;leader&amp;gt;hw`&lt;/span&gt; or your custom mapping) in normal mode

Both will print "Hello from Neovim!" in the command line.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Don't forget to replace &lt;code&gt;yourusername&lt;/code&gt; with your actual GitHub username.)&lt;/p&gt;

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

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

&lt;p&gt;Congratulations! You've just created your first Neovim plugin. This simple example demonstrates the basics of plugin development, including structuring your code, creating user commands, setting up keymappings, and making your plugin installable.&lt;/p&gt;

&lt;p&gt;As you become more comfortable with plugin development, you can explore more advanced features like autocommands, integrating with Neovim's API, and creating more complex functionality. The possibilities are endless!&lt;/p&gt;

&lt;p&gt;Remember, the key to successful plugin development is iterative improvement and testing. With the setup we've created, you can quickly make changes, reload your plugin, and see the results in real-time.&lt;/p&gt;

&lt;p&gt;Happy coding, and may your Neovim experience be ever more customized and efficient!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you reached this far and liked this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>development</category>
      <category>vim</category>
      <category>extensions</category>
    </item>
    <item>
      <title>Tailwind CSS vs. Vanilla CSS: When to Use Each for Your Web Development Projects</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Mon, 09 Sep 2024 21:35:44 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/tailwind-css-vs-vanilla-css-when-to-use-each-for-your-web-development-projects-425c</link>
      <guid>https://forem.com/iamgoncaloalves/tailwind-css-vs-vanilla-css-when-to-use-each-for-your-web-development-projects-425c</guid>
      <description>&lt;p&gt;When building a website or web application, the decision between using &lt;strong&gt;Tailwind CSS&lt;/strong&gt; and &lt;strong&gt;vanilla CSS&lt;/strong&gt; can significantly impact your workflow, design consistency, and project scalability. Both options offer unique benefits, but the right choice depends on your specific project requirements and goals. &lt;/p&gt;

&lt;p&gt;In this article, we'll dive into the strengths of Tailwind CSS and vanilla CSS, helping you decide which is the best fit for your next web development project.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Tailwind CSS?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Tailwind CSS is a &lt;strong&gt;utility-first CSS framework&lt;/strong&gt; that provides a vast array of utility classes directly in your HTML. This approach allows for rapid development and design consistency across your project. Instead of writing custom CSS for each component, you apply pre-defined classes like &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;text-center&lt;/code&gt;, and &lt;code&gt;mt-4&lt;/code&gt; to style elements on the fly.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;What is Vanilla CSS?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Vanilla CSS refers to writing custom CSS without the use of frameworks. This traditional approach gives developers complete control over styling, allowing for highly customized designs. While it requires more effort to maintain consistency, it offers unparalleled flexibility.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;Advantages of Tailwind CSS&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster Development&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tailwind CSS shines when you need to &lt;strong&gt;speed up development&lt;/strong&gt;. By using utility classes, you can style components quickly without writing additional CSS. This can be particularly beneficial in agile environments where quick iterations are key.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Design Consistency&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
With Tailwind, every developer on your team uses the same set of utility classes, ensuring &lt;strong&gt;consistent design&lt;/strong&gt; across your project. This is especially useful in large-scale projects where maintaining uniformity is challenging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Component-Based Design&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tailwind CSS is ideal for component-based architectures, commonly used in frameworks like &lt;strong&gt;React&lt;/strong&gt;, &lt;strong&gt;Vue.js&lt;/strong&gt;, and &lt;strong&gt;Angular&lt;/strong&gt;. It allows you to encapsulate styles within components, reducing the need for global CSS and preventing style conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Highly Customizable&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Despite its utility-first nature, Tailwind is highly customizable. You can extend the default theme, create custom utility classes, and modify existing ones to fit your specific design system. This flexibility makes it easy to align Tailwind with your brand's visual identity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimized Naming Conflicts&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
By using utility classes directly in your HTML, Tailwind helps you avoid the common pitfalls of CSS class name conflicts and specificity issues, which can be a major headache in vanilla CSS.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;Advantages of Vanilla CSS&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complete Control Over Styling&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Vanilla CSS gives you &lt;strong&gt;full control&lt;/strong&gt; over your styles, allowing you to write any CSS rule needed to achieve the exact look and feel you want. This level of control is crucial when working on projects with unique or complex design requirements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ideal for Small Projects&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
For smaller projects or simple websites, vanilla CSS might be the better option. It eliminates the need to learn and configure a framework, allowing you to focus on writing custom styles tailored to your project's specific needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplicity and Learning&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you or your team are new to web development, vanilla CSS is universally understood and has a lower learning curve. It’s a great starting point for beginners or teams that prefer a straightforward approach.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Specific Design Requirements&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
When your project has very specific design needs that aren’t easily met with utility classes, vanilla CSS offers the flexibility to create custom styles without limitations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Build Process Required&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Vanilla CSS can be written directly in your HTML or linked in a separate stylesheet without requiring a build process, which is necessary when using Tailwind CSS. This can be a simpler approach for smaller projects or static websites.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;Tailwind CSS vs. Vanilla CSS: Making the Right Choice&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt; is ideal for developers who prefer an integrated workflow, where code and styles coexist within the same component. It offers speed, consistency, and ease of use, particularly in large or team-based projects. The framework’s ability to automatically purge unused classes, along with its high customizability and standardized configuration, makes it a powerful tool for maintaining scalable and maintainable codebases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vanilla CSS&lt;/strong&gt; is best when you need full control over your styling, are working on smaller projects, or have specific design requirements that Tailwind’s utility classes can’t easily accommodate. It’s a great option for those who prefer simplicity and don’t want to deal with the overhead of learning a new framework.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;p&gt;Ultimately, the choice between &lt;strong&gt;Tailwind CSS and vanilla CSS&lt;/strong&gt; comes down to what works best for you and your team. If you value having your code and styles in one place, prefer standardized configurations, and want to minimize the cognitive load of managing class names, Tailwind CSS is likely the better choice. However, if you need full control over your styles or are working on a smaller project where simplicity is key, vanilla CSS might be the way to go.&lt;/p&gt;

&lt;p&gt;Both approaches have their merits, and by understanding the strengths and limitations of each, you can make an informed decision that aligns with your development workflow and project goals.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;P.S. Why I like Tailwind&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For developers who prefer to keep their code and styles tightly integrated within components, Tailwind CSS is a clear winner. Tailwind's utility-first approach allows you to write styles directly in your HTML, eliminating the need to switch back and forth between HTML and CSS files. This alone can streamline your workflow, making it easier to focus on building components without the distraction of managing separate stylesheets.&lt;/p&gt;

&lt;p&gt;Here are some key reasons why I like Tailwind CSS so much:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic Documentation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tailwind’s utility classes serve as &lt;strong&gt;self-documenting code&lt;/strong&gt;. The class names describe what they do, making your code more readable and easier to understand. This reduces the need for extensive documentation since the purpose of each class is clear from the start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic Purging of Unused Classes&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
One of the standout features of Tailwind is its ability to automatically purge unused classes from your final CSS bundle. This ensures that your CSS file remains lean and optimized, improving load times and performance without any extra effort on your part.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Works Great Out of the Box and Highly Customizable&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tailwind CSS is designed to work out of the box, offering a comprehensive set of utility classes that cover most design needs. However, it’s also highly customizable—whether you need to extend the default theme, add new utilities, or modify existing ones, Tailwind makes it easy to tailor the framework to your specific project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standardized Configuration for Easier Maintainability&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tailwind provides a &lt;strong&gt;standardized configuration&lt;/strong&gt; that helps ensure consistency across your project. This is especially beneficial in teams where multiple developers are working on the same codebase. By adhering to a shared configuration, you can maintain uniformity in styles and reduce the overhead of managing class names and styles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Solving Cross-Browser Compatibility&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tailwind helps mitigate cross-browser inconsistencies, ensuring that your designs behave the same across different browsers. This can save significant time and frustration in debugging and maintaining your styles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reducing Cognitive Load&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tailwind removes the burden of coming up with and maintaining unique class names, which can often become a cumbersome task in larger projects. With Tailwind, you use utility classes that are both descriptive and reusable, reducing the cognitive load associated with managing styles.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you reached this far and liked this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>frontend</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Animating with TailwindCSS</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Sun, 28 Jul 2024 16:10:23 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/animating-with-tailwindcss-2gi9</link>
      <guid>https://forem.com/iamgoncaloalves/animating-with-tailwindcss-2gi9</guid>
      <description>&lt;p&gt;When it comes to enhancing user experience in web applications, animations play a pivotal role. TailwindCSS simplifies the process of adding animations, but what if you want more than the basic options? In this article, I will guide you through extending TailwindCSS animations, allowing you to create custom, dynamic animations without relying solely on custom CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding TailwindCSS Animations
&lt;/h2&gt;

&lt;p&gt;TailwindCSS provides four primary animations: spin, ping, bounce, and pulse. These animations are straightforward to implement but often lack the granularity and control that developers desire. While these default options are convenient, you may find yourself needing more sophisticated animations tailored to your application’s unique requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Need for Customization
&lt;/h3&gt;

&lt;p&gt;In many cases, developers want to tweak animations, such as changing speed or applying unique effects like “wiggle.” The great news is that TailwindCSS allows for customization through the configuration file, enabling you to add new animations and define their characteristics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Extended Animations
&lt;/h2&gt;

&lt;p&gt;To get started, you’ll want to locate your TailwindCSS configuration file (typically &lt;code&gt;tailwind.config.js&lt;/code&gt;). Here’s a step-by-step process to extend the basic animations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Adding a New Animation
&lt;/h3&gt;

&lt;p&gt;Let’s say you want to create a slower version of the spin animation, which we’ll call &lt;code&gt;spin-slow&lt;/code&gt;. You’ll begin by accessing your Tailwind config file:&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;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;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;spin-slow&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;spin 1s linear infinite&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we've referenced the existing spin animation and adjusted its duration to one second while maintaining a linear easing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Creating Unique Animations
&lt;/h3&gt;

&lt;p&gt;Beyond modifying existing animations, you can create entirely new ones, such as a "wiggle" effect. To do this, you'll define keyframes in your Tailwind config:&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;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;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;wiggle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wiggle 1s ease-in-out infinite&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;keyframes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;wiggle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0%, 100%&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;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rotate(-9deg)&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;50%&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;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rotate(9deg)&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;p&gt;This configuration introduces a wiggling animation that rotates an element back and forth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing Animations with Plugins
&lt;/h2&gt;

&lt;p&gt;While the default TailwindCSS animations are helpful, they may not cover all your needs, such as adjusting animation timing, delays, or even controlling play states. Here’s where plugins come into play.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the TailwindCSS Animate Plugin
&lt;/h3&gt;

&lt;p&gt;To gain more control over animations, you can use the TailwindCSS Animate plugin. To install this plugin, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the plugin via npm:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npm &lt;span class="nb"&gt;install &lt;/span&gt;tailwindcss-animate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add the plugin to your Tailwind configuration:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss-animate&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This plugin extends the functionality of TailwindCSS, allowing you to define animation durations, delays, and play states easily.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implementing Advanced Animation Features&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the plugin is installed, you can use it to define attributes like duration and delay directly within your HTML:&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;"animate-wiggle duration-75 delay-1000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example applies the wiggle animation with a duration of 75 milliseconds and a delay of one second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing Animation States
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features of the animate plugin is the ability to control whether an animation is running or paused. By toggling classes, you can pause animations based on user interactions, enhancing the interactive experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Toggling Animation States
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;toggleAnimation&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="nx"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isRunning&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.animate-wiggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paused&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isRunning&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.animate-wiggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isRunning&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;This code snippet allows an animation to be paused or resumed with a click, providing a dynamic user interface element.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>css</category>
      <category>animation</category>
    </item>
    <item>
      <title>Why TailwindCSS Should Be Your Go-To CSS Framework</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 19 Jul 2024 22:26:11 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/why-tailwindcss-should-be-your-go-to-css-framework-5eci</link>
      <guid>https://forem.com/iamgoncaloalves/why-tailwindcss-should-be-your-go-to-css-framework-5eci</guid>
      <description>&lt;p&gt;As a developer who's spent countless hours battling with CSS, I want to share a tool that's made my life so much easier: &lt;strong&gt;TailwindCSS&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This isn't just another trendy framework - it's a game-changer that's transformed how I approach web design.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's TailwindCSS all about?
&lt;/h2&gt;

&lt;p&gt;At its core, TailwindCSS is about simplicity. Instead of writing custom CSS for every element, you use pre-defined utility classes right in your HTML. Need some padding? Just add 'p-4'. Want a margin? Throw in 'm-2'. It might look messy at first, but trust me, once you get used to it, you'll wonder how you ever lived without it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I switched to TailwindCSS (and why you should too)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;It's fast. Really fast. No more jumping between HTML and CSS files - everything's right there in your markup.&lt;/li&gt;
&lt;li&gt;It's flexible. Every project is different, and TailwindCSS gets that. You can customize it to fit your needs without fighting against pre-existing styles.&lt;/li&gt;
&lt;li&gt;It's efficient. TailwindCSS only includes the styles you actually use, which means smaller file sizes and faster loading times.&lt;/li&gt;
&lt;li&gt;It makes responsive design a breeze. Creating mobile-friendly layouts is as simple as adding a few extra classes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Some tips from the trenches
&lt;/h2&gt;

&lt;p&gt;After using TailwindCSS for a while, I've picked up a few tricks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get yourself a good code editor extension. It'll make finding the right classes so much easier.&lt;/li&gt;
&lt;li&gt;Don't rush to change everything. TailwindCSS comes with some smart defaults - use them as a starting point.&lt;/li&gt;
&lt;li&gt;Check out the plugin ecosystem. There's a whole world of extra functionality out there waiting to be tapped.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I won't lie - TailwindCSS takes some getting used to. But once you've got the hang of it, you'll wonder how you ever managed without it. It's made my development process faster, my code cleaner, and my designs more consistent.&lt;/p&gt;

&lt;p&gt;If you're on the fence, I'd say give it a shot on your next project. Start small, play around with it, and see how it fits into your workflow. You might just find it's the tool you've been looking for all along.&lt;/p&gt;

&lt;p&gt;Remember, every new tool has a learning curve. But in my experience, the time you invest in learning TailwindCSS pays off tenfold in the long run. Happy coding, and let me know how you get on with TailwindCSS!&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>frontend</category>
    </item>
    <item>
      <title>What's coming in Next.js 15</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 24 May 2024 13:40:33 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/whats-coming-in-nextjs-15-2lbo</link>
      <guid>https://forem.com/iamgoncaloalves/whats-coming-in-nextjs-15-2lbo</guid>
      <description>&lt;p&gt;Today, I'm excited to dive into some of the latest technological advancements that have caught my eye, particularly around Next.js 15. Although these updates might seem nuanced, they are pivotal for developers looking to enhance web application performance and experience.&lt;/p&gt;

&lt;p&gt;While it's still in the RC phase mainly due to dependencies on the upcoming React 19, the anticipation is palpable. This update promises to streamline development processes significantly and improve application performance across the board.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhanced React Support
&lt;/h2&gt;

&lt;p&gt;One of the standout features in this release is the integration with React 19, enhancing both client and server-side capabilities. Developers can now experiment with new features like the React compiler, which optimizes the application even further. If you're keen to try it out, running a simple &lt;code&gt;npm install next@rc&lt;/code&gt;  could get you started on this exciting journey.&lt;/p&gt;

&lt;p&gt;With React 19 on the horizon, Next.js 15 is poised to integrate seamlessly, promising improvements in both client and server-side operations. This means faster, more efficient builds, and a smoother developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiler Upgrades
&lt;/h2&gt;

&lt;p&gt;The integration of the React 19 compiler is a game-changer. It simplifies the setup process, potentially improving performance without the need for extensive code modifications. This upgrade is particularly promising for those looking to enhance application speed and responsiveness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improved Error Handling for Hydration
&lt;/h2&gt;

&lt;p&gt;Next.js 15 aims to solve the hydration errors more transparently by providing clearer debugging messages. This means developers can quickly pinpoint discrepancies between server and client renders, significantly easing the debugging process. This is a huge relief for developers who have struggled with these issues in the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancements in Caching and Routing
&lt;/h2&gt;

&lt;p&gt;Vercel has also overhauled its caching strategies. In previous versions, certain default caching behaviors led to confusion and inefficiencies. The new approach requires explicit actions to enable caching, thereby reducing unintended consequences and giving developers more control.&lt;/p&gt;

&lt;p&gt;Routing has received attention too, with improvements ensuring that navigation fetches the most current page data, enhancing the user experience by maintaining the speed and responsiveness that Next.js applications are known for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incremental Adoption of Partial Pre-rendering
&lt;/h2&gt;

&lt;p&gt;This feature allows developers to implement pre-rendering gradually, making it easier to optimize performance without overhauling the entire application. It’s especially useful for enhancing user experiences on static pages like homepages or blog entries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next After: Post-Request Operations
&lt;/h2&gt;

&lt;p&gt;An exciting addition is the &lt;code&gt;unstable_after&lt;/code&gt; feature, empowering developers to execute background tasks after a response is sent to the user. This is ideal for non-critical operations like logging or analytics, ensuring they don’t delay the initial response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streamlined Developer Experience
&lt;/h2&gt;

&lt;p&gt;Lastly, the improvements extend to the developer experience. The revised &lt;code&gt;create-next-app&lt;/code&gt; template is cleaner and more intuitive, helping new users start projects more efficiently. Moreover, the introduction of Turbo pack invites developers to opt-in during setup, promoting a faster and more responsive development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Forward
&lt;/h2&gt;

&lt;p&gt;As we look towards the general availability of Next.js 15, it's clear that Vercel is committed to pushing the boundaries of what's possible in web development. These updates not only address pain points but also introduce forward-thinking capabilities that promise to set new standards in the industry.&lt;/p&gt;

&lt;p&gt;I'm particularly excited about the potential these updates have to simplify the development process while simultaneously boosting performance and security. Whether you're a seasoned developer or just starting out, these changes are worth exploring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Understanding React Server Components</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 17 May 2024 16:05:17 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/understanding-react-server-components-29o2</link>
      <guid>https://forem.com/iamgoncaloalves/understanding-react-server-components-29o2</guid>
      <description>&lt;p&gt;In the ever-evolving world of web development, React continues to be at the forefront, introducing innovative solutions to enhance performance and user experience. One of the most exciting advancements in recent times is React Server Components, a feature that promises to change how developers build and optimize applications. &lt;/p&gt;

&lt;p&gt;Let’s dive deep into what React Server Components are, how they work, and why they're a game-changer for developers and businesses alike.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are React Server Components?
&lt;/h2&gt;

&lt;p&gt;React Server Components are a new addition to React's capabilities, allowing developers to render components on the server instead of the client. This approach not only reduces the amount of code shipped to the client but also significantly improves performance by offloading processing to the server. &lt;/p&gt;

&lt;p&gt;React Server Components are designed to render ahead of time, during the build process or dynamically per request, without involving the client-side environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits of React Server Components
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Bundle Size:&lt;/strong&gt; By rendering components on the server, the client downloads less JavaScript. This is particularly beneficial for improving load times and enhancing the user experience on mobile devices with slower connections.   &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficient Data Fetching:&lt;/strong&gt; Server Components fetch data directly from the backend without going through client-side APIs, reducing latency and eliminating unnecessary client-server communication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streamlined Rendering:&lt;/strong&gt; Components are processed on the server and only the final HTML and minimal JavaScript are sent to the client, ensuring that the page loads quickly and users can interact with content sooner.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How Do React Server Components Work?
&lt;/h2&gt;

&lt;p&gt;React Server Components can operate in environments without a traditional server (at build time) or with a server (during requests). This flexibility allows them to be used in a variety of scenarios, from static site generation to dynamic server-side rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Without a Server:&lt;/strong&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="c1"&gt;// Server Component&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;page&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;content&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;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&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="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.md`&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;dangerouslySetInnerHTML&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="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;marked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&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;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;Page&lt;/code&gt; component reads a Markdown file during the build process, converts it to HTML, and renders it directly. This method ensures that the client only receives fully prepared HTML, with no need for additional libraries or data fetching after the initial load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example With a Server:&lt;/strong&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="c1"&gt;// Server Component&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Note&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;note&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;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;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="nc"&gt;Author&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorId&lt;/span&gt;&lt;span class="si"&gt;}&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;p&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;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&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;p&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;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Client Component&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Author&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="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;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAuthor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&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="nf"&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/authors/&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setAuthor&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;author&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;id&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;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;By: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;span&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;In the &lt;code&gt;Note&lt;/code&gt; component, data is fetched and rendered on the server. The &lt;code&gt;Author&lt;/code&gt; component, however, is a client component that fetches additional data after the page has loaded. This separation allows critical content to be visible immediately while less critical data loads asynchronously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Outlook and Stability
&lt;/h2&gt;

&lt;p&gt;React Server Components are still evolving. As of React 19, the feature is stable, meaning it won't undergo breaking changes between major versions. However, the underlying APIs may experience minor adjustments. Developers are encouraged to pin specific React versions or use Canary releases to experiment with the latest features.&lt;/p&gt;

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

&lt;p&gt;React Server Components represent a significant leap forward in optimizing web applications. By leveraging the server's capabilities, developers can deliver faster, more efficient web applications. As this technology matures, it is set to become an essential tool in the arsenal of modern web developers, pushing the boundaries of what's possible on the web.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Building a Simple React Custom Hook</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 10 May 2024 15:39:47 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/building-a-simple-react-custom-hook-43nm</link>
      <guid>https://forem.com/iamgoncaloalves/building-a-simple-react-custom-hook-43nm</guid>
      <description>&lt;p&gt;Have you ever had the need to persist certain bits of your app’s state, even after a dreaded page refresh? Have you ever wished for an alternative to backend storage for small amounts of data? Don't worry, we've all been there.&lt;/p&gt;

&lt;p&gt;Let's use React Custom Hooks to do that exactly.&lt;/p&gt;

&lt;p&gt;As a React developer, one constant feature I've always wanted is to be able to store and retrieve user data right from my application, without making an extra round trip to the server or managing a database. I've explored different solutions, but found myself frequently circling back to the underrated localStorage API. However, directly using this API comes with figuring out where, when and how to handle its operations. &lt;/p&gt;

&lt;p&gt;Then one day, some kind of magic happened. I stumbled upon React hooks and, boy oh boy, did that make a difference. Suddenly I was able to extract my localStorage handling code into its dedicated area, creating a custom hook that simplified my codebase to unprecedented levels. &lt;/p&gt;

&lt;p&gt;Let me walk you through it.&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;useState&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="k"&gt;from&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useLocalStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;storedValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setStoredValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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;try&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;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&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="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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;try&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;valueToStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;storedValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;setStoredValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueToStore&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueToStore&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;storedValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&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="nx"&gt;useLocalStorage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like that, you have created your custom hook to handle localStorage. What you are seeing here is a beautifully layered piece of code. It takes a key and initial value as parameters. In return, it gets a state variable and setter function to update that value. &lt;/p&gt;

&lt;p&gt;Excited about how to use this freshly baked hook? Let me show you:&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;useLocalStorage&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;./useLocalStorage&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="nf"&gt;App&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLocalStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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="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;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;input&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'text'&lt;/span&gt;
                &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;
        &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;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="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There you go! Now, each time you type in the input box in your App component, it will automatically store your changes to localStorage under the key "name". Not only that, but it won't forget even if you refresh, restart, or come back to it after an hour-long coffee break.&lt;/p&gt;

&lt;p&gt;Life as a React developer just got easier! With hooks, especially custom hooks like this, we now have delightful, reusable pieces of code that preserve the beautiful, declarative nature of React we all cherish.&lt;/p&gt;

&lt;p&gt;So why not dish out your favourite text editor, roll up your sleeves, and start experimenting with this brand new custom hook right now? Just like me, I'm sure you'll discover joy and practical efficiency in this exciting React feature. Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Docker Containers vs. Virtual Machines</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 03 May 2024 15:59:20 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/docker-containers-vs-virtual-machines-3baj</link>
      <guid>https://forem.com/iamgoncaloalves/docker-containers-vs-virtual-machines-3baj</guid>
      <description>&lt;p&gt;In a previous article I talked about how to install a NextJS app on a docker container. Now, I feel the need to write an article about the differences between docker containers and virtual machines.&lt;/p&gt;

&lt;p&gt;As a declaration of interests, I only run containers in my production apps. I believe that having a dedicated virtual machine for just running an app is overkill. Also, I don't like to have all the apps installed in a virtual machine where I can make a mistake and mess up the environments of the different apps.&lt;/p&gt;

&lt;p&gt;For that reason my current setup for production apps is two vms, each of them running docker with a few containers. When I need to update a container I just do a &lt;code&gt;git pull&lt;/code&gt; followed by a &lt;code&gt;docker stop &amp;lt;container name&amp;gt;&lt;/code&gt; and &lt;code&gt;docker build&lt;/code&gt; and &lt;code&gt;docker start &amp;lt;container name&amp;gt;&lt;/code&gt;. So far it has worked without any hiccups.&lt;/p&gt;

&lt;p&gt;With that out of the way let's get into the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Virtual Machine?
&lt;/h2&gt;

&lt;p&gt;A virtual machine is an emulation of a computer system that provides the functionality of a physical computer. It operates based on a hypervisor, which acts as a platform for running multiple VMs. &lt;/p&gt;

&lt;p&gt;Each VM runs its own operating system, completely isolated from the host and other VMs, thereby ensuring thorough compartmentalization of applications.&lt;/p&gt;

&lt;p&gt;There are two types of hypervisors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Type 1 (Bare Metal):&lt;/strong&gt; This hypervisor runs directly on the hardware of the host machine, offering high performance and efficiency because it interacts directly with the physical hardware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type 2 (Hosted):&lt;/strong&gt; This version runs as an application within the host operating system, relying on the host OS to manage hardware resources. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have a Proxmox server, running for 6+ years, that is a type-2 hypervisor and it works beautifully. It is a lot of fun and cannot recommend enough. You will learn so much and go through many rabbit holes :) .&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Docker Containers
&lt;/h2&gt;

&lt;p&gt;Docker containers, on the other hand, offer a more lightweight approach to application deployment. Unlike VMs that virtualize the entire operating system, containers virtualize at the application level, sharing the host system's kernel but running isolated processes. &lt;/p&gt;

&lt;p&gt;This architecture allows Docker containers to be extremely efficient and fast, making them ideal for environments where rapid scaling is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Differences Between VMs and Docker Containers
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolation:&lt;/strong&gt; VMs provide complete OS isolation, which enhances security and allows different operating systems to coexist on the same physical machine. Containers, while isolated, share the host's kernel, making them less isolated compared to VMs but more resource-efficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt; VMs involve additional overhead due to the need to emulate a full hardware environment and an entire OS for each instance. Containers, being lighter, start faster and use fewer resources, as they only need to run the application and its dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Management:&lt;/strong&gt; VMs require a predefined allocation of resources, which can lead to underutilization or resource scarcity if not managed properly. Containers are more flexible, allowing for dynamic resource allocation that maximizes efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Portability:&lt;/strong&gt; Containers excel in portability since they encapsulate the application configuration and dependencies in a single package, making it easy to move across different environments that support Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Cases:&lt;/strong&gt; VMs are better suited for applications requiring full isolation and security, complete control over the operating environment, or where hardware emulation is necessary. Containers are ideal for microservices architecture, application scaling, and rapid development cycles where quick deployment and efficient resource use are priorities.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Applications and Considerations
&lt;/h2&gt;

&lt;p&gt;In practice, the choice between a Docker container and a VM depends on the specific needs of the application and the environment. &lt;/p&gt;

&lt;p&gt;For instance, if you need a fully isolated environment for secure data processing, a VM might be the appropriate choice. &lt;/p&gt;

&lt;p&gt;However, if you are developing microservices that need to scale dynamically, Docker containers can provide the necessary speed and flexibility.&lt;/p&gt;

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

&lt;p&gt;Both Docker containers and virtual machines offer significant advantages but serve different purposes based on application requirements and environmental context.&lt;/p&gt;

&lt;p&gt;I recommend that you start playing with docker because it really is a great tool to use and doesn't consume that many resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vm</category>
      <category>docker</category>
      <category>virtualmachine</category>
    </item>
    <item>
      <title>How to install React 19 Beta</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 26 Apr 2024 16:15:58 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/how-to-install-react-19-beta-46kj</link>
      <guid>https://forem.com/iamgoncaloalves/how-to-install-react-19-beta-46kj</guid>
      <description>&lt;p&gt;I wrote this quick article to help those that are like me and want to test the latest version of React, which is React 19 Beta.&lt;/p&gt;

&lt;p&gt;In that way, we get to play around with the new cool stuff like Server Components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 - Installation
&lt;/h2&gt;

&lt;p&gt;In a terminal go to the folder with all your repos and execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest react-beta-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the prompts be sure to select React and Javascript or Typescript. I'm going to select Typescript as I prefer it. This command will create a new folder called react-beta-test (you can change this if you want in the command above).&lt;/p&gt;

&lt;p&gt;CD into that folder: &lt;code&gt;cd react-beta-test&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and run the following command: &lt;code&gt;npm install react@beta react-dom@beta&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;after the command finishes, run &lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1.a - React Typescript
&lt;/h2&gt;

&lt;p&gt;If you selected the typescript version of React follow these commands. If not, and you have selected Javascript, you can move on to the next step.&lt;/p&gt;

&lt;p&gt;Edit the package.json file and delete the dependencies and remove "@types/react" and "@types/react-dom" from the devDependencies. After that include the following lines in the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"dependencies": {  
"@types/react": "npm:types-react@alpha",  
"@types/react-dom": "npm:types-react-dom@alpha"  

},  
"overrides": {  
"@types/react": "npm:types-react@alpha",  
"@types/react-dom": "npm:types-react-dom@alpha"  
}  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is an example of what it looks like before and after the edit.&lt;/p&gt;

&lt;p&gt;Before (Example package.json File:)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "react-beta-test",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc &amp;amp;&amp;amp; vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.66",
    "@types/react-dom": "^18.2.22",
    "@typescript-eslint/eslint-plugin": "^7.2.0",
    "@typescript-eslint/parser": "^7.2.0",
    "@vitejs/plugin-react": "^4.2.1",
    "eslint": "^8.57.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.6",
    "typescript": "^5.2.2",
    "vite": "^5.2.0"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After (Example package.json File):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "react-beta-test",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --host",
    "build": "tsc &amp;amp;&amp;amp; vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "@types/react": "npm:types-react@alpha",
    "@types/react-dom": "npm:types-react-dom@alpha",
    "react": "^19.0.0-beta-94eed63c49-20240425",
    "react-dom": "^19.0.0-beta-94eed63c49-20240425"
  },
  "overrides": {
    "@types/react": "npm:types-react@alpha",
    "@types/react-dom": "npm:types-react-dom@alpha"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^7.2.0",
    "@typescript-eslint/parser": "^7.2.0",
    "@vitejs/plugin-react": "^4.2.1",
    "eslint": "^8.57.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.6",
    "typescript": "^5.2.2",
    "vite": "^5.2.0"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 - Check the React version installed
&lt;/h2&gt;

&lt;p&gt;To check that we have the right version we can edit the src/App.tsx file and change the first line:&lt;/p&gt;

&lt;p&gt;From:&lt;br&gt;
&lt;code&gt;import { useState } from "react";&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To:&lt;br&gt;
&lt;code&gt;import { useState, version } from "react";&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and edit the line 19, from:&lt;br&gt;
&lt;code&gt;&amp;lt;h1&amp;gt;Vite + React &amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;to:&lt;br&gt;
&lt;code&gt;&amp;lt;h1&amp;gt;Vite + React {version}&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;after in a terminal run: &lt;code&gt;npm run dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Hopefully, you should see something similar to the image on the top of this post. If not, leave me a comment below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>vite</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How to serve a Next.js app in docker</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 19 Apr 2024 13:56:30 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/how-to-serve-a-nextjs-app-in-docker-1e1l</link>
      <guid>https://forem.com/iamgoncaloalves/how-to-serve-a-nextjs-app-in-docker-1e1l</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;I have created a few Next.js containers, but for some reason, when I do it I always go back in forth between my code and few webpages. I haven't found a page with everything that I need to know in order to containerize an app. &lt;/p&gt;

&lt;p&gt;So I'm writing this for my reference and hope that you will also get value from it. If you do, please leave me a comment as way to encourage me to do this more often.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the next.js project
&lt;/h2&gt;

&lt;p&gt;Ideally you would use a current Next.js project that you already created.&lt;/p&gt;

&lt;p&gt;If you currently you don't have a Next.js project and want to tryout this tutorial you can run &lt;code&gt;npx create-next-app@latest&lt;/code&gt; to create a new Next.js project and follow the instruction provided in the terminal by the installer.&lt;/p&gt;

&lt;p&gt;Next make sure to edit the next.config.js file in order to create the "production" build. Your next.config.js file should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const nextConfig = { output: 'standalone', }

module.exports = nextConfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the Dockerfile for x86 and ARM
&lt;/h2&gt;

&lt;p&gt;In order to containerize your app, you must create a file called &lt;code&gt;Dockerfile&lt;/code&gt; in the root directory of your project. You should copy the code below to your &lt;code&gt;Dockerfile&lt;/code&gt;, depending on the Docker host architecture.&lt;/p&gt;

&lt;p&gt;There a few important points to note in the &lt;code&gt;Dockerfile&lt;/code&gt; :&lt;br&gt;
    1. The first line of the &lt;code&gt;Dockerfile&lt;/code&gt; referes to the base image of the container and, in this case, the node version in which your containerized app is going to run. You should always match the version of node you are running in your dev environment with the version running in the container.&lt;br&gt;
    2. Some Next.js have different file structures. For instance, some apps need environment variables to be set in order to run properly. In that case you need to copy your .env file to the container. You can do that by adding the following code: &lt;code&gt;COPY --from=builder --chown=nextjs:nodejs /app/.env ./&lt;/code&gt; . This code should be added before the &lt;code&gt;USER nextjs&lt;/code&gt; line to ensure that it's copied at the correct moment.&lt;br&gt;
    3. You should adapt the code from the previous line to suit your needs. For instance if you have an ORM with a sqlite database, you are going to have to build the database from scratch when you build the container. The same steps that you followed to build the database in your DEV environment, should be followed for the containerized environment. I have left an example in a few comments of how you can achieve that. In my example I'm using sqlite with prisma.&lt;/p&gt;

&lt;h3&gt;
  
  
  For x86 architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:20-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm &amp;amp;&amp;amp; pnpm i --frozen-lockfile; \
  else echo "Lockfile not found." &amp;amp;&amp;amp; exit 1; \
  fi


# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Example: Create a new database
# RUN rm -rf /app/prisma
# RUN npx prisma init --datasource-provider sqlite
# COPY prisma/schema.prisma ./prisma/
# COPY prisma/migrations ./prisma/
# RUN npx prisma migrate dev --name init
# RUN npx prisma generate

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Comment the following line in case you want to enable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1

# RUN yarn build

# If using npm comment out above and use below instead
RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
# Comment the following line in case you want to enable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
# Environment Variable Example: COPY --from=builder --chown=nextjs:nodejs /app/.env ./
# App with Database Example: COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma/
COPY --from=builder --chown=nextjs:nodejs /app/.next/ ./.next/
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static


USER nextjs

EXPOSE 3000

ENV PORT 3000
# set hostname to localhost
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For the ARM architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:20-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk update &amp;amp;&amp;amp; apk add --no-cache libc6-compat python3 make build-base 
WORKDIR /app
# ENV PYTHON /usr/bin/python3
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm &amp;amp;&amp;amp; pnpm i --frozen-lockfile; \
  else echo "Lockfile not found." &amp;amp;&amp;amp; exit 1; \
  fi


# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Example: Create a new database
# RUN rm -rf /app/prisma
# RUN npx prisma init --datasource-provider sqlite
# COPY prisma/schema.prisma ./prisma/
# COPY prisma/migrations ./prisma/
# RUN npx prisma migrate dev --name init
# RUN npx prisma generate

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Comment the following line in case you want to enable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1

# RUN yarn build

# If using npm comment out above and use below instead
RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
# Comment the following line in case you want to enable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
# Environment Variable Example: COPY --from=builder --chown=nextjs:nodejs /app/.env ./
# App with Database Example: COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma/
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static


USER nextjs

EXPOSE 3000

ENV PORT 3000
# set hostname to localhost
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build the Docker Container
&lt;/h2&gt;

&lt;p&gt;To build the Docker Container all you have to do is go into the Next.js root directory and issue the following command: `&lt;code&gt;sudo docker build --no-cache -t &amp;lt;IMAGE_NAME&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Be sure to replace &lt;code&gt;&amp;lt;IMAGE_NAME&amp;gt;&lt;/code&gt; with a name that you can remember. That name is going to be used in the next step.&lt;/p&gt;

&lt;p&gt;Also note that the command assumes that you have a file called &lt;code&gt;Dockerfile&lt;/code&gt;in the root of your project. If, for some reason, you want to use a file with a different name you can use the &lt;code&gt;-f &amp;lt;FILENAME&amp;gt;&lt;/code&gt; flag (e.g.: &lt;code&gt;sudo docker build --no-cache -t &amp;lt;IMAGE_NAME&amp;gt; -f &amp;lt;FILENAME&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Docker Container
&lt;/h2&gt;

&lt;p&gt;After the container is built, you can deploy the container. For that we are going to use the following command: &lt;code&gt;sudo docker run -p 5000:3000 --name &amp;lt;CONTAINER_NAME&amp;gt; &amp;lt;IMAGE_NAME&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-p 5000:3000&lt;/code&gt; - Maps the port 5000 of the host to the port 3000 of the container which is the default Next.js port.&lt;br&gt;
&lt;code&gt;&amp;lt;CONTAINER_NAME&amp;gt;&lt;/code&gt; - The name of the container. This is useful for easy reference of the container when issuing commands or when listing containers (e.g.: &lt;code&gt;docker stop &amp;lt;CONTAINER_NAME&amp;gt;; docker ps)&lt;br&gt;
&lt;/code&gt;` - The name of the image (given in the previous step). This is useful for easy reference of the image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;One of the ideas behind docker is to maximize efficiency. In this regard, you could use a Caddy instance in another container and use it as reverse proxy for your docker apps. For instance, you could have 3 different domains, point to the same machine in DNS and configure Caddy to serve the apps on the different ports to the different URLS. &lt;/p&gt;

&lt;p&gt;If you would me to write about this leave me a comment :).&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my &lt;a href="//www.goncaloalves.com"&gt;personal blog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on &lt;a href="//twitter.com/iamgoncaloalves"&gt;Twitter/X&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on &lt;a href="//bio.link/goncaloalves"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>docker</category>
    </item>
    <item>
      <title>An Introduction to LLM Agents</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 12 Apr 2024 13:46:05 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/an-introduction-to-llm-agents-2h45</link>
      <guid>https://forem.com/iamgoncaloalves/an-introduction-to-llm-agents-2h45</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@possessedphotography?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Possessed Photography&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/white-robot-wallpaper-JjGXjESMxOY?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Large language models (LLMs) have taken the AI world by storm. But what if these powerful language processors could not just talk, but also think and reason? Enter LLM agents, a new wave of AI assistants poised to transform how we interact with computers.&lt;/p&gt;

&lt;p&gt;LLM agents, short for Large Language Model agents, are essentially AI systems built around a large language model (LLM) as their core. This LLM acts like a super-powered understanding and generation engine for human language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasoning Beyond Chatbots
&lt;/h2&gt;

&lt;p&gt;Traditional chatbots are great for answering simple questions. However, LLM agents go a step further. They can process information, analyze situations, and even learn from past interactions. This allows them to engage in more meaningful conversations and tackle complex tasks.&lt;/p&gt;

&lt;p&gt;Imagine a customer service agent that understands your frustration and suggests solutions tailored to your specific problem. Or, picture a financial advisor that analyzes your financial goals and market trends to recommend personalized investment strategies. These are just a few examples of the potential applications of LLM agents with their reasoning capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations of Reasoning
&lt;/h2&gt;

&lt;p&gt;LLM agents are not without their limitations and vary deeply depending on the model that they are based on. Here are some of the limitations that LLM Agents suffer from currently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Common Sense Reliance:&lt;/strong&gt; LLMs often rely on the common sense embedded in their training data. This can lead to nonsensical responses in situations requiring real-world understanding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Causal Reasoning:&lt;/strong&gt; While they can make basic causal inferences, LLMs struggle with complex cause-and-effect relationships. They might struggle to understand the nuances of human actions and motivations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Black Box Reasoning:&lt;/strong&gt; Unlike traditional logic systems, LLM reasoning can be opaque. It's often difficult to understand exactly how an LLM agent arrives at its conclusions, making it challenging to debug errors or ensure reliable decision-making.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Biases:&lt;/strong&gt; LLM agents inherit biases from the data they're trained on. Developers need to be mindful of this and implement measures to mitigate bias in their apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explainability &amp;amp; Trust:&lt;/strong&gt; As with any AI system, ensuring transparency in the reasoning process of LLM agents is crucial for building trust with users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage of LLM Agents in our apps
&lt;/h2&gt;

&lt;p&gt;However, even in their current state, LLM Agents can revolutionize app development. For instance, we can transform in-app search by understanding the user's intent behind their queries. The agents can not only find the relevant information but also explain it in a clear and concise way.&lt;/p&gt;

&lt;p&gt;LLM Agents can enhance the user experience by analyzing user data and preferences and recommending features, content or actions tailored to each user's specific needs. The can also streamline the development process by analyzing code and user interactions to identify potential bugs and suggest improvements, saving time and effort during testing and debugging phases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: A New Era of Intelligent Interaction
&lt;/h2&gt;

&lt;p&gt;LLM agents represent a significant leap forward in human-computer interaction. Their ability to reason and learn paves the way for a future where AI assistants can not just respond to our queries, but also understand them, anticipate our needs, and offer intelligent solutions. With continued research and development, LLM agents have the potential to transform various sectors, from customer service and education to finance and healthcare. The future of intelligent interaction is here, and LLM agents are at the forefront.&lt;/p&gt;

&lt;p&gt;Overall, LLM agents hold immense potential to transform the way we develop and interact with apps. By leveraging their reasoning capabilities and natural language processing prowess, developers can create more intuitive, user-friendly, and intelligent applications across various domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my personal blog.&lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on Twitter/X.&lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on here&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
    </item>
    <item>
      <title>The Shocking Discovery of a Backdoor in XZ Utils: A Threat to Internet Systems</title>
      <dc:creator>Gonçalo Alves</dc:creator>
      <pubDate>Fri, 05 Apr 2024 17:19:43 +0000</pubDate>
      <link>https://forem.com/iamgoncaloalves/the-shocking-discovery-of-a-backdoor-in-xz-utils-a-threat-to-internet-systems-5gfe</link>
      <guid>https://forem.com/iamgoncaloalves/the-shocking-discovery-of-a-backdoor-in-xz-utils-a-threat-to-internet-systems-5gfe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A recent alarming discovery has shaken the cybersecurity community. A backdoor was found in XZ Utils, a widely-used compression tool known for its exceptional compression ratios. This backdoor, if left undetected, could have had catastrophic consequences for numerous Linux distributions, potentially compromising the security of countless systems that rely on XZ compression. In this article, we will delve into the details of this shocking revelation, its implications, and the steps to protect yourself from this threat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Backdoor
&lt;/h2&gt;

&lt;p&gt;The backdoor in XZ Utils was first noticed by a developer at Microsoft who was troubleshooting SSH issues on a Debian Linux system. He observed that SSH logins were taking longer than usual, consuming excessive CPU cycles, and generating errors with a memory debugging tool. Through careful analysis, he traced the problem to recent updates made to XZ Utils, which turned out to be a malicious backdoor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Extent of the Backdoor
&lt;/h2&gt;

&lt;p&gt;Initially, it was believed that the backdoor was limited to Debian's XZ package. However, further investigation revealed that the backdoor was present in the Upstream XZ repository. This meant that if left undetected, it could have made its way downstream into various Linux distributions, potentially impacting a significant portion of the internet. Due to XZ Utils being open-source software, the backdoor had to be meticulously concealed to avoid detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uncovering the Backdoor
&lt;/h2&gt;

&lt;p&gt;The backdoor was cleverly hidden within binary files in the test folder, rather than in the Upstream code or build tools used by XZ in G. Interestingly, the initial part of the backdoor was encrypted using the XZ Library itself, adding an extra layer of complexity to its analysis. The backdoor employed a chunk-based loading mechanism, where chunks of data were loaded and discarded repeatedly until the final stage was reached.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Functionality of the Backdoor
&lt;/h2&gt;

&lt;p&gt;Once the backdoor was fully loaded, it injected a binary object into the build process. This binary object, when reverse engineered, revealed that it installed an audit hook into the dynamic linker to resolve symbols and libraries not yet loaded. It then waited for RSA public decrypt PLT to be resolved, modifying its value to point to its own code. This allowed the backdoor to execute system-level remote code, potentially granting unauthorized access to compromised systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Intricate Operation
&lt;/h2&gt;

&lt;p&gt;The discovery of this backdoor shed light on an elaborate hacking operation that had been in progress for years. Multiple individuals seemed to have collaborated to infiltrate the XZ repository. Suspicious accounts made their presence known during this time, constantly criticizing the slow release schedule of XZ Utils. This operation involved subtle manipulation of the repository and the introduction of compromised versions of XZ into Debian's repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mitigating the Threat
&lt;/h2&gt;

&lt;p&gt;To protect against this backdoor, it is essential to update your system to XZ version 5.6.0 or 5.6.1, if available. If a patched version is not yet accessible, consider downgrading to a version prior to these releases. Even if you are not using a red hat or Debian-based system, it is crucial to remain vigilant, as the full scope of this threat is still being investigated. Additional vulnerabilities may exist, given the extensive manipulation that occurred within the XZ repository.&lt;/p&gt;

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

&lt;p&gt;The discovery of a backdoor in XZ Utils has sent shockwaves through the cybersecurity community. This incident serves as a reminder of the constant vigilance required to maintain the security of open-source software. By promptly updating systems and staying informed about potential threats, we can mitigate the risks posed by such advanced persistent threats. Let us remain proactive in safeguarding our systems and ensuring the integrity of the software we rely on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect with me
&lt;/h2&gt;

&lt;p&gt;If you like this article be sure to leave a comment. That will make my day!&lt;/p&gt;

&lt;p&gt;If you want to read other stuff by me you can check out my personal blog.&lt;/p&gt;

&lt;p&gt;If you want to connect with me you can send me a message on Twitter/X.&lt;/p&gt;

&lt;p&gt;You can also check other stuff that I have going on here&lt;/p&gt;

</description>
      <category>news</category>
      <category>security</category>
      <category>linux</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
