<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jake Wiesler</title>
    <description>The latest articles on DEV Community by Jake Wiesler (@jakewies).</description>
    <link>https://dev.to/jakewies</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%2F63770%2Fa58ce74b-43a5-457e-974b-18ff11c838cf.png</url>
      <title>DEV Community: Jake Wiesler</title>
      <link>https://dev.to/jakewies</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jakewies"/>
    <language>en</language>
    <item>
      <title>Essential Vim Movements</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Sun, 21 Nov 2021 15:17:29 +0000</pubDate>
      <link>https://dev.to/jakewies/essential-vim-movements-41k7</link>
      <guid>https://dev.to/jakewies/essential-vim-movements-41k7</guid>
      <description>&lt;p&gt;I’ve been using Vim for just over a year. A combination of &lt;a href="https://marketplace.visualstudio.com/items?itemName=vscodevim.vim" rel="noopener noreferrer"&gt;the Vim emulator in VSCode&lt;/a&gt; and &lt;a href="https://neovim.io" rel="noopener noreferrer"&gt;Neovim&lt;/a&gt; in the terminal. The former is a great way to start familiarizing yourself with the basics before you jump into the latter. In this post, I want to walk through some of the movements I use everyday to traverse files quickly.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Up, down, left and right&lt;/li&gt;
&lt;li&gt;
Up, down, left and right, but faster

&lt;ul&gt;
&lt;li&gt;Tip: Use the &lt;code&gt;scrolloff&lt;/code&gt; setting&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Jump to the beginning of a line&lt;/li&gt;

&lt;li&gt;Jump to the end of a line&lt;/li&gt;

&lt;li&gt;

Jump to a specific line

&lt;ul&gt;
&lt;li&gt;Tip: Use relative numbers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Search for text&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Up, down, left and right
&lt;/h2&gt;

&lt;p&gt;These are the first four movements anyone questing in Vim-land will learn. They are executed with the &lt;code&gt;h&lt;/code&gt;, &lt;code&gt;j&lt;/code&gt;, &lt;code&gt;k&lt;/code&gt; and &lt;code&gt;l&lt;/code&gt; keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;h&lt;/code&gt; - move left&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;j&lt;/code&gt; - move down&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;k&lt;/code&gt; - move up&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;l&lt;/code&gt; - move right&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can think of these four letters as flattened arrows, the ones you’d normally find on a keyboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up, down, left and right, but faster
&lt;/h2&gt;

&lt;p&gt;Getting comfortable with &lt;code&gt;hjkl&lt;/code&gt; will take time, but eventually you’ll be looking to traverse documents faster. You can achieve more speed in each direction using the following keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;w&lt;/code&gt; - move forward to beginning of next word&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b&lt;/code&gt; - move backward to beginning of previous word&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-u&lt;/code&gt; - move halfway up the screen&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl-d&lt;/code&gt; - move halfway down the screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tend to use the keys above much more frequently, usually to locate a specific part of the file. I'll then rely on the &lt;code&gt;hjkl&lt;/code&gt; keys for pinpointing specific characters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tip: Use the &lt;code&gt;scrolloff&lt;/code&gt; setting
&lt;/h3&gt;

&lt;p&gt;This one isn't a movement per se, but it will really improve your experience when you start utilizing vertical movements like &lt;code&gt;Ctrl-u&lt;/code&gt; and &lt;code&gt;Ctrl-d&lt;/code&gt;. Without overriding the default value of &lt;code&gt;scrolloff&lt;/code&gt;, the minimal number of lines above and below the cursor will be &lt;code&gt;0&lt;/code&gt;. This results in your cursor being potentially locked to the first or last line of the file depending on the direction you are heading.&lt;/p&gt;

&lt;p&gt;It's kind of annoying.&lt;/p&gt;

&lt;p&gt;I would suggest overriding the default value with a number you feel comfortable with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" .vimrc&lt;/span&gt;

&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;scrolloff&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration above will maintain, at minimum, 8 lines above or below the cursor based on your location in the file while scrolling. You can think of it as a &lt;em&gt;padding&lt;/em&gt; of sorts.&lt;/p&gt;

&lt;p&gt;This is an improvement, but I like to take it further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" .vimrc&lt;/span&gt;

&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;scrolloff&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the value &lt;code&gt;999&lt;/code&gt;, you can force the cursor to stay in the middle of the file in most scenarios. This prevents your eyes from jumping around looking for the cursor, as it will usually be in the middle of the screen until your reach the beginning or end of the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump to the beginning of a line
&lt;/h2&gt;

&lt;p&gt;This can be achieved in a few ways, depending on what your needs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;^&lt;/code&gt; - Go to the first character in the line&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; - Go to the first column in the line&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Shift-i&lt;/code&gt; - Place the cursor before the first character in the line, and enter &lt;code&gt;INSERT&lt;/code&gt; mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;^&lt;/code&gt; and &lt;code&gt;0&lt;/code&gt; are &lt;em&gt;almost&lt;/em&gt; the same. Their difference occurs when the line you're on is indented. &lt;code&gt;^&lt;/code&gt; takes you to the first non-blank character in a line. &lt;code&gt;0&lt;/code&gt; will &lt;em&gt;always&lt;/em&gt; take you to the start of a line, even if there is no character there.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Shift-i&lt;/code&gt; is useful when you want to navigate to the beginning of a line and start typing immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jump to the end of a line
&lt;/h2&gt;

&lt;p&gt;Similar to jumping to the beginning of a line, we can also jump to the end of a line using the following keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$&lt;/code&gt; - Go the last character in the line&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Shift-a&lt;/code&gt; - Place the cursor after the last character in the line, and enter &lt;code&gt;INSERT&lt;/code&gt; mode&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Jump to a specific line
&lt;/h2&gt;

&lt;p&gt;There are a few ways to jump to a specific line, and they all involve the letter "G".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gg&lt;/code&gt; - Jump to the first line in a file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;G&lt;/code&gt; - Jump to the last line in a file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;35G&lt;/code&gt; - Jump to line number 35&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use &lt;code&gt;gg&lt;/code&gt; and &lt;code&gt;G&lt;/code&gt; pretty heavily. They're great ways to go north and south, especially if you know what you're looking for.&lt;/p&gt;

&lt;p&gt;The last example shows how to jump to a specific line number, in this case line 35. If you type a line number that is beyond the max line number in the file, you will just go to the last line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tip: Use relative numbers
&lt;/h3&gt;

&lt;p&gt;If you read my &lt;a href="https://dev.toblog/getting-started-with-vim"&gt;Getting Started With Vim article&lt;/a&gt;, you may have added the following &lt;a href="http://localhost:3000/blog/getting-started-with-vim#general-settings" rel="noopener noreferrer"&gt;general settings&lt;/a&gt; to your Vim configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" .vimrc&lt;/span&gt;

&lt;span class="c"&gt;" add line numbers&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;number&lt;/span&gt;
&lt;span class="c"&gt;" add width to line numbers&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;numberwidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are must haves in my opinion, but if you are going to leverage line&lt;br&gt;
numbers while navigating files in Vim, you may want to consider adding a new&lt;br&gt;
setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" .vimrc&lt;/span&gt;

&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;relativenumber&lt;/code&gt; setting shows line numbers relative to the line you are&lt;br&gt;
currently on. In the example below, the cursor is on line 12, and the lines&lt;br&gt;
above and below are numbered 1, 2, 3 and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia54trapyrzc8pm3arw9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia54trapyrzc8pm3arw9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why do I suggest this? Using &lt;code&gt;relativenumber&lt;/code&gt; enables even more efficiency when&lt;br&gt;
traversing north and south:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;3j&lt;/code&gt; - Jump down 3 lines&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;8k&lt;/code&gt; - Jump up 8 lines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;
  Using &lt;code&gt;j&lt;/code&gt; and &lt;code&gt;k&lt;/code&gt; without line numbers as a prefix will go 1 line down and up&lt;br&gt;
  respectively. So this should feel pretty natural once you use it a few times.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;These movements are still possible without the &lt;code&gt;relativenumber&lt;/code&gt; setting on, but&lt;br&gt;
it is harder to identify how many lines up or down you want to jump.&lt;/p&gt;
&lt;h2&gt;
  
  
  Search for text
&lt;/h2&gt;

&lt;p&gt;To search for a specific text pattern in a file, you can prefix the pattern with&lt;br&gt;
a forward slash &lt;code&gt;/&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/my-word&lt;/code&gt; - Searches the file for the text pattern "my-word"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you complete typing out the pattern to search for, be sure to hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;br&gt;
This will jump you to the first occurrence of the pattern in the file. If there&lt;br&gt;
is more than one match, you can jump between them using the following keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; - Jump to next occurrence of text pattern&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Shift-n&lt;/code&gt; - Jump to previous occurrence of text pattern&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Tip: Improve search experience
&lt;/h3&gt;

&lt;p&gt;Here are a few settings that can improve your search experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" .vimrc&lt;/span&gt;

&lt;span class="c"&gt;" Disable highlighting of all search results&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;nohlsearch&lt;/span&gt;
&lt;span class="c"&gt;" Only highlight first search result&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;incsearch&lt;/span&gt;
&lt;span class="c"&gt;" Default to case insensitive search&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;ignorecase&lt;/span&gt;
&lt;span class="c"&gt;" Use case sensitive search if pattern includes uppercase letter&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;smartcase&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If you made it until the end, thanks for reading! I hope this article has been useful to you. I'm still very raw with Vim, but the movements described above have served me well. If you are new to the text editor, please give these a try and stick with it! You'll be surprised at how fast you adapt. It's all muscle memory!&lt;/p&gt;

&lt;p&gt;I will do my best to keep this document updated as I progress with Vim and learn new ways to do things. If you have any suggestions, &lt;a href="https://twitter.com/jakewies" rel="noopener noreferrer"&gt;let me know on Twitter&lt;/a&gt;! And as always, happy building!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>career</category>
      <category>learning</category>
      <category>coding</category>
    </item>
    <item>
      <title>Setup Zsh As Your Default Shell</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Sun, 21 Nov 2021 15:10:55 +0000</pubDate>
      <link>https://dev.to/jakewies/setup-zsh-as-your-default-shell-i78</link>
      <guid>https://dev.to/jakewies/setup-zsh-as-your-default-shell-i78</guid>
      <description>&lt;p&gt;If you're switching from Bash to Zsh and you want to know how to launch new terminal sessions &lt;em&gt;in Zsh&lt;/em&gt;, you've found the right blog post. I've had to do this a few times over the years, and it was always a struggle. Recently &lt;a href="https://www.youtube.com/playlist?list=PL1C97G3GhlHdANMFUIXTcFr14R7b7EBj9"&gt;I started working on a portable development environment&lt;/a&gt; and I learned how to set Zsh as my default shell &lt;em&gt;the right way&lt;/em&gt;. Here's how:&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite: Install Zsh
&lt;/h2&gt;

&lt;p&gt;How you install Zsh is up to you. Checkout &lt;a href="https://gist.github.com/derhuerst/12a1558a4b408b3b2b6e"&gt;this GitHub gist&lt;/a&gt; for some OS-specific options. The following steps will work for any Linux/Unix-based machine. I am not aware of how to accomplish this on Windows.&lt;/p&gt;

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

&lt;p&gt;The first step is to tell your terminal that Zsh can be used as a valid login shell. You do this by updating a special file named &lt;code&gt;/etc/shells&lt;/code&gt;. If you &lt;code&gt;cat&lt;/code&gt; this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/shells
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a list of filepaths that each point to an executable file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output above may vary depending on your circumstance. One of these executables is your current shell. You can see your current shell by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$SHELL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of the command above will be an executable file that resides within the &lt;code&gt;/etc/shells&lt;/code&gt; list. For example, if I run the &lt;code&gt;echo&lt;/code&gt; command above, the output I see is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Locate Zsh's executable filepath
&lt;/h3&gt;

&lt;p&gt;We need to append the Zsh exectuable to this list, but before doing that we need to know where it is. To find its location you can run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;command -v&lt;/code&gt; will print the absolute filepath used when invoking the argument provided, in this case &lt;code&gt;zsh&lt;/code&gt;. This gives us the path to Zsh's exectuable file. The location may vary depending on your installation method.&lt;/p&gt;

&lt;p&gt;For example, running the command above may yield:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you see no output at all, then you probably don't have Zsh installed. See Prerequisites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Append Zsh's executable filepath to &lt;code&gt;/etc/shells&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Once we have the location of Zsh's exectuable file, we can "pipe" it to another command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/shells
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;tee&lt;/code&gt; utility, which we are running in &lt;code&gt;sudo&lt;/code&gt;, takes an input and appends it to &lt;code&gt;/etc/shells&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Together, the full command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; zsh | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/shells
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;|&lt;/code&gt; is the pipe symbol. It takes the output of the left-hand side and uses it as the input for the right-hand side. In psuedo, what's happening is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Locate the filepath of Zsh&lt;/li&gt;
&lt;li&gt;Append the filepath to &lt;code&gt;/etc/shells&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After running the full command you should see Zsh's executable at the bottom of &lt;code&gt;/etc/shells&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2
&lt;/h2&gt;

&lt;p&gt;Now that we've told our terminal that Zsh can be used as a valid login shell, we need to explicitly use it as &lt;em&gt;the default&lt;/em&gt;. This can be accomplished with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;chsh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;which zsh&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command above is &lt;code&gt;chsh -s&lt;/code&gt;, or "change shell". We are running it in &lt;code&gt;sudo&lt;/code&gt; mode for elevated permissions, and we pass it two arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;$(which zsh)&lt;/code&gt; - The shell we want to use as the default&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$USER&lt;/code&gt; - The user whose default shell we are changing, ie. - you.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After running this command, the next time you start a new terminal session you will see Zsh! To confirm, you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$SHELL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;And that's it! Running the commands defined in steps 1 and 2 will turn Zsh into your new default shell. If you have any further questions, or maybe you have a better way of doing this, let me know on &lt;a href="https://twitter.com/jakewies"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Getting Started With Vim - A Practical Guide</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Sun, 17 Oct 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/jakewies/getting-started-with-vim-a-practical-guide-1mm5</link>
      <guid>https://dev.to/jakewies/getting-started-with-vim-a-practical-guide-1mm5</guid>
      <description>&lt;p&gt;So you want to use Vim full time do ya? It’s not going to be easy, but I believe the choice is worth it. &lt;a href="https://jakewiesler.com/blog/leaving-vscode-for-vim" rel="noopener noreferrer"&gt;I made the switch from VSCode to Vim a few weeks ago&lt;/a&gt;. I’ve been diving deep and learning a ton, and I still feel like I’ve only scratched the surface. To be honest, I don’t even feel qualified to write this post. But my recent efforts have to count for something, &lt;em&gt;right&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Vim presents you with a thousand roads to wander. There is no shortage of configuration options, plugins and unfamiliar syntax, leaving newbies like us lost and paralyzed with decision fatigue. If this is how you feel, you’re not alone. But I urge you to push through.&lt;/p&gt;

&lt;p&gt;Vim can be an incredibly powerful tool in your arsenal. When you have it tuned to your liking, it's an absolute joy to use. This post is aimed at guiding you through the difficult bits so you can get to the joyful bits as fast as possible. I will go through everything I know so far, helping you go from 0 to 1.&lt;/p&gt;

&lt;p&gt;We will create a strong foundation that you can build on over time. One that gets out of your way and allows you to produce your best work. So grab a coffee (maybe 2) and get your mind right. We will dine in the gates of Vim this day!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Neovim&lt;/li&gt;
&lt;li&gt;General Settings&lt;/li&gt;
&lt;li&gt;
Plugins

&lt;ul&gt;
&lt;li&gt;Manage plugins with vim-plug&lt;/li&gt;
&lt;li&gt;Pick a color scheme&lt;/li&gt;
&lt;li&gt;Search with telescope&lt;/li&gt;
&lt;li&gt;Better status lines with lightline&lt;/li&gt;
&lt;li&gt;Git integration with vim-fugitive&lt;/li&gt;
&lt;li&gt;gitsigns for git decorations&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Language Support

&lt;ul&gt;
&lt;li&gt;Using lspconfig&lt;/li&gt;
&lt;li&gt;Language servers&lt;/li&gt;
&lt;li&gt;Autcompletion with nvim-cmp&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

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

&lt;li&gt;Tips and Tricks&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Neovim
&lt;/h2&gt;

&lt;p&gt;Instead of using good ole’ Vim, the one that comes preinstalled on your MacOS or Linux machine (sorry Windows users), we will opt to use &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;Neovim&lt;/a&gt;. If you’re unfamiliar, Neovim &lt;a href="https://neovim.io/charter/" rel="noopener noreferrer"&gt;“is not a rewrite but a continuation and extension of Vim”&lt;/a&gt;. There isn’t much difference between Vim and Neovim from a user’s perspective. However, Neovim is optimized &lt;em&gt;out-of-the-box&lt;/em&gt; and has an active development team that is pushing the tool to greater heights quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;First, you’ll need to install Neovim on your machine. &lt;a href="https://github.com/neovim/neovim/wiki/Installing-Neovim" rel="noopener noreferrer"&gt;There are many ways to do this&lt;/a&gt;. I use MacOS and opted for Homebrew:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once the install is complete, you can test that it’s working by using the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvim &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  init.vim
&lt;/h3&gt;

&lt;p&gt;Vim uses a &lt;code&gt;.vimrc&lt;/code&gt; file to hold your configuration settings, but Neovim is a little different. It uses an &lt;code&gt;init.vim&lt;/code&gt; file located at the path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.config/nvim/init.vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;~/&lt;/code&gt; represents your home directory. You might already have a &lt;code&gt;~/.config&lt;/code&gt; directory, as it is common amongst other tools. Whatever your situation, you will want to create an &lt;code&gt;init.vim&lt;/code&gt; file at the path above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.config/nvim
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nvim
&lt;span class="nb"&gt;touch &lt;/span&gt;init.vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file, you can define general settings, plugins, color schemes, keybindings (called remaps) and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  General settings
&lt;/h2&gt;

&lt;p&gt;Now that your config file is created, let’s open it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvim init.vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file will be empty. Let’s add some basic settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" enables syntax highlighting&lt;/span&gt;
&lt;span class="nb"&gt;syntax&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;

&lt;span class="c"&gt;" Better colors&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;termguicolors&lt;/span&gt;

&lt;span class="c"&gt;" number of spaces in a &amp;lt;Tab&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;tabstop&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;softtabstop&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;expandtab&lt;/span&gt;

&lt;span class="c"&gt;" enable autoindents&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;smartindent&lt;/span&gt;

&lt;span class="c"&gt;" number of spaces used for autoindents&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;shiftwidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;

&lt;span class="c"&gt;" adds line numbers&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;number&lt;/span&gt;

&lt;span class="c"&gt;" columns used for the line number&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;numberwidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;

&lt;span class="c"&gt;" highlights the matched text pattern when searching&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;incsearch&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;nohlsearch&lt;/span&gt;

&lt;span class="c"&gt;" open splits intuitively&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;splitbelow&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;splitright&lt;/span&gt;

&lt;span class="c"&gt;" navigate buffers without losing unsaved work&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;

&lt;span class="c"&gt;" start scrolling when 8 lines from top or bottom&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;scrolloff&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;

&lt;span class="c"&gt;" Save undo history&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;undofile&lt;/span&gt;

&lt;span class="c"&gt;" Enable mouse support&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;a&lt;/span&gt;

&lt;span class="c"&gt;" case insensitive search unless capital letters are used&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;ignorecase&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;smartcase&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve collected these settings from community suggestions and others' dotfiles. I believe they are a great base without being too opinionated. Comments have been added for each setting to give some insight as to what they do. If you want more information on each, you can type &lt;code&gt;:h &amp;lt;setting-name&amp;gt;&lt;/code&gt; inside Neovim to bring up the help manual for that specific setting. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:h incsearch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will bring up the help section for the &lt;code&gt;incsearch&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Note: If you have suggestions on other settings to include, &lt;a href="https://twitter.com/jakewies" rel="noopener noreferrer"&gt;let me know on Twitter&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugins
&lt;/h2&gt;

&lt;p&gt;Now that we have some basic settings out of the way, let’s get to the fun part. &lt;em&gt;Plugins!&lt;/em&gt; In Vim, plugins are scripts that operate in a global scope or per filetype. Authoring these plugins is done in &lt;a href="https://learnvimscriptthehardway.stevelosh.com/" rel="noopener noreferrer"&gt;Vimscript&lt;/a&gt;. I’ve never worked with Vimscript, but I’ve heard it’s tricky, and a barrier to entry for developers who want to build plugins or contribute to Vim’s core.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://neovim.io/charter/" rel="noopener noreferrer"&gt;Neovim’s vision is to&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable new contributors&lt;/li&gt;
&lt;li&gt;Remove barriers to entry&lt;/li&gt;
&lt;li&gt;Unblock plugin authors&lt;/li&gt;
&lt;li&gt;Deliver a first-class scripting alternative to Vimscript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This alternative scripting language is &lt;a href="https://www.lua.org/" rel="noopener noreferrer"&gt;Lua&lt;/a&gt;. Neovim still supports Vimscript, but the core development team encourages plugin authors to write plugins in Lua. I’ve only been exposed to the language for a few weeks, but it is relatively simple to understand. Here are a few resources if you’d like to learn more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.google.com/search?q=Lua&amp;amp;oq=Lua&amp;amp;aqs=chrome..69i57j69i59l3j0i512j69i60l3.1250j0j1&amp;amp;sourceid=chrome&amp;amp;ie=UTF-8" rel="noopener noreferrer"&gt;lua.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nanotee/nvim-lua-guide" rel="noopener noreferrer"&gt;nanotee/nvim-lua-guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnxinyminutes.com/docs/lua/" rel="noopener noreferrer"&gt;Learn X in Y Minutes, Where X = Lua&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will need to use Neovim for a little bit before you can understand where your pain points are. For this reason, I’m going to keep my list of plugin recommendations short. I will only recommend plugins that I feel are “blue chips” in the Neovim community.&lt;/p&gt;

&lt;p&gt;That being said, both Vim and Neovim make it easy to pop plugins into your configuration and try them out for an hour or two. If you like them you can stick, but you don’t have to marry anything.&lt;/p&gt;

&lt;h3&gt;
  
  
  vim-plug
&lt;/h3&gt;

&lt;p&gt;In order to install plugins we need a plugin manager. Enter &lt;a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer"&gt;vim-plug&lt;/a&gt;. There are other options that I have my eye on (looking at you &lt;a href="https://github.com/wbthomason/packer.nvim" rel="noopener noreferrer"&gt;&lt;code&gt;packer.nvim&lt;/code&gt;&lt;/a&gt;), but for now we will use &lt;code&gt;vim-plug&lt;/code&gt;. It has 25k stars on GitHub, so I think we’re in good hands.&lt;/p&gt;

&lt;p&gt;To install it you can &lt;a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer"&gt;checkout the docs in the GitHub repository&lt;/a&gt;. Once installed, we’ll add a few lines to our &lt;code&gt;init.vim&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;" plugins will go here&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We call &lt;code&gt;plug#begin()&lt;/code&gt;, passing in the directory where we want to install our plugins, and &lt;code&gt;plug#end()&lt;/code&gt;, which signifies the end of our plugin declarations.&lt;/p&gt;

&lt;p&gt;But we haven’t declared any…yet!&lt;/p&gt;

&lt;h3&gt;
  
  
  A color scheme
&lt;/h3&gt;

&lt;p&gt;Most colors schemes in Neovim can be installed as a plugin. A color scheme is 100% preference, and there are a lot of really good ones. Here’s a list of some of my favorites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/gruvbox-community" rel="noopener noreferrer"&gt;Gruvbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://draculatheme.com/vim" rel="noopener noreferrer"&gt;Dracula&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/connorholyday/vim-snazzy" rel="noopener noreferrer"&gt;vim-snazzy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rakr/vim-one" rel="noopener noreferrer"&gt;vim-one&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside of &lt;code&gt;plug#begin()&lt;/code&gt; and &lt;code&gt;plug#end()&lt;/code&gt; , let’s declare our color scheme. I will use Gruvbox to illustrate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

Plug &lt;span class="s1"&gt;'gruvbox-community/gruvbox'&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above example illustrates how to declare plugins you wish to install with &lt;code&gt;vim-plug&lt;/code&gt;. It is shorthand notation for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;Plug &lt;span class="s1"&gt;'https://github.com/gruvbox-community/gruvbox'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see more examples of declaring plugins with &lt;code&gt;vim-plug&lt;/code&gt; &lt;a href="https://github.com/junegunn/vim-plug#example" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you declare your plugin in &lt;code&gt;init.vim&lt;/code&gt;, you need to take a few more steps to install it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save your &lt;code&gt;init.vim&lt;/code&gt; with &lt;code&gt;:w&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Source your &lt;code&gt;init.vim&lt;/code&gt; with &lt;code&gt;:so %&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;:PlugInstall&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The commands above need to be entered in &lt;code&gt;NORMAL&lt;/code&gt; mode. See here for an explanation on &lt;a href="https://www.freecodecamp.org/news/vim-editor-modes-explained/" rel="noopener noreferrer"&gt;Vim’s “modes”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Step one saves your &lt;code&gt;init.vim&lt;/code&gt; file without closing it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://unix.stackexchange.com/questions/466305/what-does-source-mean" rel="noopener noreferrer"&gt;Step two “sources” your &lt;code&gt;init.vim&lt;/code&gt;&lt;/a&gt;. The command is shorthand for &lt;code&gt;:source CURRENT_FILEPATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Step three calls a global function named &lt;code&gt;PlugInstall&lt;/code&gt; which is made available to you by &lt;code&gt;vim-plug&lt;/code&gt;. It will open &lt;a href="https://vim.fandom.com/wiki/Buffers" rel="noopener noreferrer"&gt;a vertical buffer&lt;/a&gt; to the left of your &lt;code&gt;init.vim&lt;/code&gt;. In this buffer you will be able to see a list of all the plugins you’ve installed along with the most recent installation’s progress. Once the plugin has finished installing, you can close the &lt;code&gt;vim-plug&lt;/code&gt; buffer by typing &lt;code&gt;:q!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After installing your color scheme, we’ll need to tell Neovim to use it. Inside of your &lt;code&gt;init.vim&lt;/code&gt; and below your plugin declarations, add the following lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

Plug &lt;span class="s1"&gt;'gruvbox-community/gruvbox'&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;" declare your color scheme&lt;/span&gt;
&lt;span class="k"&gt;colorscheme&lt;/span&gt; gruvbox
&lt;span class="c"&gt;" Use this for dark color schemes&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;background&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;dark&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you aren’t using &lt;code&gt;gruvbox&lt;/code&gt; then make sure to refer to your color scheme’s documentation for clear instructions on how to declare it.&lt;/p&gt;

&lt;p&gt;Save and exit with &lt;code&gt;:wq&lt;/code&gt;. Next time you reopen Neovim you should see a crisp new paint job!&lt;/p&gt;

&lt;h3&gt;
  
  
  Telescope
&lt;/h3&gt;

&lt;p&gt;The first function-focused plugin on the list is &lt;a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer"&gt;Telescope&lt;/a&gt;, created by one of Neovim’s core team members - &lt;a href="https://twitter.com/teejdevries" rel="noopener noreferrer"&gt;TJ Devries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Side note, TJ puts out a lot of Neovim-related content on &lt;a href="https://www.youtube.com/c/TJDeVries" rel="noopener noreferrer"&gt;his YouTube channel&lt;/a&gt; and &lt;a href="https://twitter.com/teejdevries" rel="noopener noreferrer"&gt;his Twitch streams&lt;/a&gt;. His YouTube channel in particular has been a great resource. Check him out!&lt;/p&gt;

&lt;p&gt;Telescope is a must-have plugin in my opinion. It is a &lt;a href="https://en.wikipedia.org/wiki/Fuzzy_finder" rel="noopener noreferrer"&gt;fuzzy finder&lt;/a&gt; built in Lua that can be configured every which way. It adds a ton of functionality to your Neovim setup. I haven’t realized its true potential so far, but for finding files and searching for text it’s amazing.&lt;/p&gt;

&lt;p&gt;Let’s install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" Telescope requires plenary to function&lt;/span&gt;
Plug &lt;span class="s1"&gt;'nvim-lua/plenary.nvim'&lt;/span&gt;
&lt;span class="c"&gt;" The main Telescope plugin&lt;/span&gt;
Plug &lt;span class="s1"&gt;'nvim-telescope/telescope.nvim'&lt;/span&gt;
&lt;span class="c"&gt;" An optional plugin recommended by Telescope docs&lt;/span&gt;
Plug &lt;span class="s1"&gt;'nvim-telescope/telescope-fzf-native.nvim'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'do'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'make'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we are installing more than one plugin here. Telescope requires another plugin named &lt;a href="https://github.com/nvim-lua/plenary.nvim" rel="noopener noreferrer"&gt;plenary&lt;/a&gt; to function. And the other plugin is a &lt;a href="https://github.com/nvim-telescope/telescope.nvim#sorters" rel="noopener noreferrer"&gt;Telescope Sorter&lt;/a&gt; that supports &lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;fzf&lt;/a&gt; syntax. &lt;a href="https://github.com/nvim-telescope/telescope.nvim#suggested-dependencies" rel="noopener noreferrer"&gt;Telescope recommends installing it for performance improvements&lt;/a&gt;. We like the gains, so we’ll take the gains.&lt;/p&gt;

&lt;p&gt;Make sure to perform these three steps to complete installation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save &lt;code&gt;init.vim&lt;/code&gt; with &lt;code&gt;:w&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Source &lt;code&gt;init.vim&lt;/code&gt; with &lt;code&gt;:so %&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install the plugins with &lt;code&gt;:PlugInstall&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next we need to configure Telescope. This is a common practice amongst Vim plugins, and in my opinion the most challenging aspect of Vim. If you’re working with a Vimscript-based plugin it can be even more so. This is why I’ve tried to stick to Lua-based plugins when possible.&lt;/p&gt;

&lt;p&gt;Telescope happens to be a Lua-based plugin, which is awesome! Since it’s the first plugin on the list, we’ll need to do some basic setup.&lt;/p&gt;

&lt;p&gt;First, we will need to create &lt;a href="https://github.com/nanotee/nvim-lua-guide#modules" rel="noopener noreferrer"&gt;a new directory in &lt;code&gt;~/.config/nvim&lt;/code&gt; named &lt;code&gt;lua&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nvim
&lt;span class="nb"&gt;mkdir &lt;/span&gt;lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside of the &lt;code&gt;lua&lt;/code&gt; directory we want to create a subdirectory to namespace our plugin configurations. To understand why this is necessary requires explaining how Vim handles file lookup. In short, creating a namespace subdirectory inside the &lt;code&gt;lua&lt;/code&gt; directory &lt;a href="https://github.com/nanotee/nvim-lua-guide#tips" rel="noopener noreferrer"&gt;prevents unwanted filename collisions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve opted to use my GitHub handle as the name of my subdirectory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nvim/lua
&lt;span class="nb"&gt;mkdir &lt;/span&gt;jakewies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can make it whatever you like. It really doesn’t matter. Once you’ve created it, you’ll want to cd into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nvim/lua/YOUR_NAMESPACE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;YOUR_NAMESPACE&lt;/code&gt; is being used as a placeholder. Replace it with the name of your namespace directory.&lt;/p&gt;

&lt;p&gt;In your namespace directory you will create a &lt;code&gt;.lua&lt;/code&gt; file for every plugin you want/need to configure. Telescope is the first one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvim telescope.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A helpful Vim tip is that you can open files before they exist. If you close the file without saving it, it won’t be created! In the example above, we open &lt;code&gt;telescope.lua&lt;/code&gt; in Neovim. As long as we save the file it will be created.&lt;/p&gt;

&lt;p&gt;Our configuration for Telescope will be short and sweet:&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="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;telescope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'telescope'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;telescope&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="c1"&gt;-- To get fzf loaded and working with telescope,&lt;/span&gt;
&lt;span class="c1"&gt;-- you need to call load_extension, somewhere after&lt;/span&gt;
&lt;span class="c1"&gt;-- the setup function.&lt;/span&gt;
&lt;span class="n"&gt;telescope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fzf'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above example gives us our first taste of Lua. See? Not that intimidating! I’ve found that, at a minimum, a lot of plugins need:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PLUGIN_NAME'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I won’t be diving deep into Telescope configuration in this post. If you want to see some of the available setup options and other customizations, &lt;a href="https://github.com/nvim-telescope/telescope.nvim#customization" rel="noopener noreferrer"&gt;check the docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Make sure to save &lt;code&gt;telescope.lua&lt;/code&gt; and exit Neovim with &lt;code&gt;:wq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we need to tell our &lt;code&gt;init.vim&lt;/code&gt; that it exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;
&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="k"&gt;lua&lt;/span&gt; require&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_NAMESPACE/telescope'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case I would put:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="k"&gt;lua&lt;/span&gt; require&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'jakewies/telescope'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple of notes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put this command below the &lt;code&gt;vim-plug&lt;/code&gt; functions!&lt;/li&gt;
&lt;li&gt;You don’t need to specify the &lt;code&gt;.lua&lt;/code&gt; filetype&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What does this do? It seems a little magical at first, and understanding requires Vim lookup knowledge, but essentially this command will look for a &lt;code&gt;lua&lt;/code&gt; folder in your Vim runtime, and source the specific &lt;code&gt;.lua&lt;/code&gt; file based on the path. So in this example the following file will be sourced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.config/nvim/lua/YOUR_NAMESPACE/telescope.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is great! But we can certainly improve on it.&lt;/p&gt;

&lt;p&gt;Instead of requiring each configuration file in our &lt;code&gt;init.vim&lt;/code&gt;, we can just require the namespace directory. In order for this to work, we’ll need to add a new file to our namespace directory called &lt;code&gt;init.lua&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nvim/lua/YOUR_NAMESPACE
&lt;span class="nb"&gt;touch &lt;/span&gt;init.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;init.lua&lt;/code&gt; file can be thought of as an index file, similar to &lt;code&gt;index.js&lt;/code&gt; for you JavaScript friends. Inside &lt;code&gt;init.lua&lt;/code&gt; we can require all of our configuration files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require('YOUR_NAMESPACE/telescope')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, inside of our &lt;code&gt;init.vim&lt;/code&gt; all we need to do is require the namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;
&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="k"&gt;lua&lt;/span&gt; require&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_NAMESPACE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case it would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="k"&gt;lua&lt;/span&gt; require&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'jakewies'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this point on, any new configuration files in &lt;code&gt;lua/YOUR_NAMESPACE&lt;/code&gt; can be required within &lt;code&gt;lua/YOUR_NAMESPACE/init.lua&lt;/code&gt; and they will be sourced in your &lt;code&gt;init.vim&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Ok, back to Telescope.&lt;/p&gt;

&lt;p&gt;Telescope exposes &lt;a href="https://github.com/nvim-telescope/telescope.nvim#vim-commands" rel="noopener noreferrer"&gt;a number of commands&lt;/a&gt; that can be accessed using &lt;code&gt;:Telescope&lt;/code&gt; in &lt;code&gt;NORMAL&lt;/code&gt; mode. A quick way to see all of the available commands is to enter &lt;code&gt;:Telescope&lt;/code&gt; followed by a space, and then hit &lt;code&gt;&amp;lt;Tab&amp;gt;&lt;/code&gt;. The tab button will open an autocomplete list full of available Telescope commands. You can navigate the list using &lt;code&gt;&amp;lt;Tab&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Shift-Tab&amp;gt;&lt;/code&gt; to go backwards.&lt;/p&gt;

&lt;p&gt;Below are a list of Telescope commands that I have found extremely useful thus far:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;:Telescope find_files&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- This command will search your current directory for filenames matching your query. It will not show hidden files by default, and it will also ignore files in `.ignore` files like `.gitignore`.

- This command will also try to identify if you are in a larger project using special directories like `.git`. This gives you access to subdirectories and parent directories if you have moved away from the project root.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;:Telescope buffers&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Search for files that have been opened in a buffer in your current Neovim instance. Really helpful when you are working on a few files at a time, and you want to “narrow your world”.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; &lt;code&gt;:Telescope live_grep&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Similar to `find_files`, but searches for a string match rather than a file name. [Note, this command will only work if you have `ripgrep` installed](https://github.com/nvim-telescope/telescope.nvim#suggested-dependencies). `ripgrep` is a command-line search utility that makes searching for text really fast. It can be installed like any other command-line tool, and Telescope will use it for its `live_grep` command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The commands above are just a few I’ve been leaning on lately, and there's many more. Another helpful tip is to think about mapping the commands above to a keybinding. For instance, I have the &lt;code&gt;find_files&lt;/code&gt; command mapped to &lt;code&gt;&amp;lt;Ctrl-p&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can view documentation on Telescope within Neovim using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:h telescope
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lightline
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/itchyny/lightline.vim" rel="noopener noreferrer"&gt;Lightline&lt;/a&gt; is a status line plugin for Vim. The status line exists at the bottom of your Vim window, and exposes a few helpful pieces of information, such as &lt;a href="https://www.freecodecamp.org/news/vim-editor-modes-explained/" rel="noopener noreferrer"&gt;the current MODE&lt;/a&gt; (normal, insert, visual, etc.), and what type of file you are in.&lt;/p&gt;

&lt;p&gt;I enjoy developer tools that do a small list of things well without trying to support every use case under the sun. Lightline fits that mold. It is relatively “light” in configuration options when compared to other popular alternatives like &lt;a href="https://github.com/vim-airline/vim-airline" rel="noopener noreferrer"&gt;vim-airline&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install Lightline, let’s open up our &lt;code&gt;init.vim&lt;/code&gt; again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" Lightline&lt;/span&gt;
Plug &lt;span class="s1"&gt;'itchyny/lightline.vim'&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, each time you declare a new plugin you have to take 3 steps to get it installed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save your &lt;code&gt;init.vim&lt;/code&gt; with &lt;code&gt;:w&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Source your &lt;code&gt;init.vim&lt;/code&gt; with &lt;code&gt;:so %&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;:PlugInstall&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will preemptively create a configuration file for Lightline now, because shortly we will install a plugin that integrates with the status line in a unique way.&lt;/p&gt;

&lt;p&gt;In our namespace directory, let’s create a &lt;code&gt;lightline.lua&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.config/nvim/lua/YOUR_NAMESPACE
nvim lightline.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the file, we’ll add a placeholder config object:&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="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lightline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The config above looks different from that of Telescope’s. The reason for this is that Lightline is not written in Lua, and so to hook into its configuration requires using &lt;a href="https://github.com/nanotee/nvim-lua-guide#the-vim-namespace" rel="noopener noreferrer"&gt;the Vim namespace&lt;/a&gt; within the Lua standard library:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Neovim exposes a global &lt;code&gt;vim&lt;/code&gt; variable which serves as an entry point to interact with its APIs from Lua. It provides users with an extended “standard library” of functions as well as various sub-modules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;vim.g&lt;/code&gt; is a &lt;a href="https://github.com/nanotee/nvim-lua-guide#using-meta-accessors-1" rel="noopener noreferrer"&gt;meta-accessor&lt;/a&gt; that lets you interact with Vim’s global variable. The config above sets a new global variable named &lt;code&gt;lightline&lt;/code&gt; to an empty object.&lt;/p&gt;

&lt;p&gt;Go ahead and save your &lt;code&gt;lightline.lua&lt;/code&gt; file before sourcing it within your namespace directory’s &lt;code&gt;init.lua&lt;/code&gt; file:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_NAMESPACE/telescope'&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="s1"&gt;'YOUR_NAMESPACE/lightline'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My &lt;code&gt;init.lua&lt;/code&gt; currently reads:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'jakewies/telescope'&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="s1"&gt;'jakewies/lightline'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save &lt;code&gt;init.lua&lt;/code&gt; with &lt;code&gt;:w&lt;/code&gt; and source it with &lt;code&gt;:so %&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One last thing we want to do is hide the Vim Mode text at the bottom left of our window. This is no longer necessary since Lightline is showing us the mode too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyice7n7naogbibzcfod5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyice7n7naogbibzcfod5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;init.vim&lt;/code&gt; file, append the following setting to your General Settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;noshowmode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent! That wraps up the Lightline section for now. If you want to view documentation on Lightline in the future, you can do so within Neovim using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:h lightline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  vim-fugitive
&lt;/h3&gt;

&lt;p&gt;If ever there was a “blue chip” plugin in the Vim ecosystem, &lt;a href="https://github.com/tpope/vim-fugitive" rel="noopener noreferrer"&gt;&lt;code&gt;vim-fugitive&lt;/code&gt;&lt;/a&gt; would probably be it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Git wrapper so awesome, it should be illegal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It has taken some getting used to, but the integrations provided by &lt;code&gt;vim-fugitive&lt;/code&gt; are powerful. Let’s install it. You know the drill by now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" vim-fugitive&lt;/span&gt;
Plug &lt;span class="s1"&gt;'tpope/vim-fugitive'&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s no configuration required for this plugin. Just save, source, install and you’re off to the races.&lt;/p&gt;

&lt;p&gt;I have a very familiar workflow with Git via the command line. Trying to re-train my brain to utilize &lt;code&gt;vim-fugitive&lt;/code&gt; is going to take some time. You might be in a similar boat, so let me give you some helpful tips.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;:Git&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is the “crown jewel” (straight from the Docs!) of &lt;code&gt;vim-fugitive&lt;/code&gt;. As you may have come to recognize with these types of commands, you can use &lt;code&gt;&amp;lt;Tab&amp;gt;&lt;/code&gt; autocomplete to see what else it can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;Git &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Tab&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there is a lot you can do! In fact, it’s a little overwhelming, but if you’re familiar with Git, then you will find &lt;code&gt;vim-fugitive&lt;/code&gt; is pretty intuitive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" view status of current branch&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git status

&lt;span class="c"&gt;" view available branches&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git branch

&lt;span class="c"&gt;" stash work&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git stash

&lt;span class="c"&gt;" stage changes for commit&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git &lt;span class="nb"&gt;add&lt;/span&gt;

&lt;span class="c"&gt;" commit staged changes&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git commit

&lt;span class="c"&gt;" push committed changes&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entering &lt;code&gt;:Git&lt;/code&gt; with no command will bring up &lt;a href="https://github.com/tpope/vim-fugitive/blob/69f5fcbd459c113181d18a0ea2641cd47c9e0318/doc/fugitive.txt#L50" rel="noopener noreferrer"&gt;the &lt;code&gt;fugitive-summary&lt;/code&gt; window&lt;/a&gt;. I actually like this feature a lot. You will see a list of untracked, staged and unstaged files along with the current branch name. Placing your cursor over these files will let you run certain “maps” like staging/unstaging, discarding changes, toggling inline diffs etc.&lt;/p&gt;

&lt;p&gt;For more info on what maps are available within the &lt;code&gt;fugitive-summary&lt;/code&gt; window, enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:h fugitive-maps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;a href="https://github.com/tpope/vim-fugitive/blob/69f5fcbd459c113181d18a0ea2641cd47c9e0318/doc/fugitive.txt#L258" rel="noopener noreferrer"&gt;check out this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vim-fugitive&lt;/code&gt; can integrate with Lightline to display the current working branch. Let’s set that up. In our placeholder &lt;code&gt;lightline.lua&lt;/code&gt; file from before, let’s add some configuration:&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="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lightline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'mode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'past'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'gitbranch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'filename'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'modified'&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="n"&gt;component_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;gitbranch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'fugitive#head'&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;There’s a lot going on here, but know that this comes straight out of Lightline’s &lt;code&gt;README&lt;/code&gt; in the &lt;a href="https://github.com/itchyny/lightline.vim#advanced-configuration" rel="noopener noreferrer"&gt;Advanced Configuration&lt;/a&gt; section. In short, we define a component named &lt;code&gt;gitbranch&lt;/code&gt; and set it equal to &lt;code&gt;fugitive#head&lt;/code&gt;, and then add the component to the left side of the status line.&lt;/p&gt;

&lt;p&gt;Save and source the file. If you are working in a &lt;code&gt;git&lt;/code&gt; project (pssst. &lt;a href="https://www.jakewiesler.com/blog/managing-dotfiles" rel="noopener noreferrer"&gt;you should!&lt;/a&gt;) , you will see the branch name on the bottom left!&lt;/p&gt;

&lt;p&gt;For more information on &lt;code&gt;vim-fugitive&lt;/code&gt; enter the following command in Neovim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:h fugitive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  gitsigns
&lt;/h3&gt;

&lt;p&gt;One thing I sorely miss from VSCode is the ability to view new, modified and deleted lines in a git project. Neovim doesn’t support this by default, so we’ll use &lt;a href="https://github.com/lewis6991/gitsigns.nvim" rel="noopener noreferrer"&gt;&lt;code&gt;gitsigns&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Super fast git decorations implemented purely in lua/teal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes please. In our &lt;code&gt;init.vim&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" gitsigns&lt;/span&gt;
Plug &lt;span class="s1"&gt;'lewis6991/gitsigns.nvim'&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;gitsigns&lt;/code&gt; requires &lt;code&gt;nvim-lua/plenary.nvim&lt;/code&gt; as well. Since we’ve installed this with Telescope (see above), we’re covered.&lt;/p&gt;

&lt;p&gt;You know what to do - Save, source and &lt;code&gt;:PlugInstall&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Gitsigns requires calling a &lt;code&gt;setup&lt;/code&gt; function in your configuration for it to work. Let’s create a &lt;code&gt;gitsigns.lua&lt;/code&gt; file in our namespaced directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvim ~/.config/nvim/lua/YOUR_NAMESPACE/gitsigns.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And inside of this file we’ll get things started:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gitsigns'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing no options will init the plugin using the default settings, which can be found &lt;a href="https://github.com/lewis6991/gitsigns.nvim#usage" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s add &lt;code&gt;gitsigns.lua&lt;/code&gt; to our namespace directory’s &lt;code&gt;init.lua&lt;/code&gt; file:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_NAMESPACE/telescope'&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="s1"&gt;'YOUR_NAMESPACE/lightline'&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="s1"&gt;'YOUR_NAMESPACE/gitsigns'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mine looks like this:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'jakewies/telescope'&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="s1"&gt;'jakewies/lightline'&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="s1"&gt;'jakewies/gitsigns'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source this file and you’re good to go! You can see gitsigns in action by navigating to a project on your machine that is version controlled with git. Any tracked file that has been modified will display that modification with a colored pipe on the left hand side of Vim.&lt;/p&gt;

&lt;p&gt;Although this is technically what we want, the placing of the sign is a little off. This is because we need to tell Vim to show diagnostics signs in the number column, rather than to the left of the number. Open up your &lt;code&gt;init.vim&lt;/code&gt; file and add a new &lt;code&gt;set&lt;/code&gt; option to your general settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ... general settings&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;signcolumn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="k"&gt;number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and source your &lt;code&gt;init.vim&lt;/code&gt; file and view the changes. Now your signs should be displayed in the number column, rather than to the left. To learn more about the &lt;code&gt;signcolumn&lt;/code&gt; option, run the following command in Neovim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;h&lt;/span&gt; &lt;span class="nb"&gt;signcolumn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git signs also supports a &lt;a href="https://git-scm.com/docs/git-blame" rel="noopener noreferrer"&gt;Git blame feature&lt;/a&gt; very similar to VSCode, and it’s a feature I desperately miss. When you are in a project that uses Git, keeping your cursor over a line for a certain period of time will cause some virtual text to display in a dim color. The text will read the name of the developer that last modified the line, the modification date and the commit message.&lt;/p&gt;

&lt;p&gt;I love this feature, but it’s not a requirement. &lt;code&gt;vim-fugitive&lt;/code&gt; has a &lt;code&gt;:Git blame&lt;/code&gt; command that solves the same use case, but I prefer the virtual text. To enable it, simply add the following to your Git signs configuration:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gitsigns'&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="c1"&gt;-- signs config&lt;/span&gt;
    &lt;span class="n"&gt;current_line_blame&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="n"&gt;current_line_blame_opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;virt_text_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'right_align'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it! Saving and sourcing the file will result in some inline Git blame goodness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language support
&lt;/h2&gt;

&lt;p&gt;At this point we have some general settings applied that make Neovim easier to work with. We have some plugins installed that improve our productivity. But we still haven’t done anything about language support.&lt;/p&gt;

&lt;p&gt;In text editors like VSCode, this isn’t a huge concern. Some languages like JavaScript and HTML are supported out of the box. Others, like Python and C++, have dedicated extensions that make your life a lot easier. These extensions provide &lt;a href="https://code.visualstudio.com/docs/editor/intellisense" rel="noopener noreferrer"&gt;Intellisense&lt;/a&gt;, code completion, formatting, linting and more.&lt;/p&gt;

&lt;p&gt;Extensions like this are possible because VSCode supports the &lt;a href="https://microsoft.github.io/language-server-protocol/" rel="noopener noreferrer"&gt;Language Server Protocol&lt;/a&gt;, a Microsoft-created standard for defining communications between language tooling and text editors.&lt;/p&gt;

&lt;p&gt;Before the Language Server Protocol (LSP), language tools and text editors had no uniform way of talking with each other. Each language tool needed a separate implementation for each editor, and each editor needed to adapt to each tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry42dm7bkd0m522rpi05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry42dm7bkd0m522rpi05.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LSP provides a reliable standard so language tools can be built once, and editors that support LSP will be able to utilize them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh3441ubs6k64oh9e1ri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh3441ubs6k64oh9e1ri.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neovim supports LSP natively as of &lt;code&gt;v0.5.0&lt;/code&gt;, meaning we can get the same benefits VSCode provides, albeit with a “little” configuration.&lt;/p&gt;

&lt;p&gt;There is a second option to Neovim’s native LSP called &lt;a href="https://github.com/neoclide/coc.nvim" rel="noopener noreferrer"&gt;&lt;code&gt;coc.nvim&lt;/code&gt;&lt;/a&gt;. It has been around for a while and a lot of people use it with success. I showed up to the Neovim party around the time that native LSP became a thing, so I never really tried &lt;code&gt;coc&lt;/code&gt;. I’ve heard that it is much easier to configure, but comes with significant features that you may or may not need.&lt;/p&gt;

&lt;p&gt;I like the idea of opting for the native solution when possible, and even though it can be a pain to configure at the moment, the fact that the core team is supporting LSP means that in the future things should improve. For this reason, we’ll opt for native LSP, but feel free to check out &lt;code&gt;coc&lt;/code&gt; if things get dicey.&lt;/p&gt;

&lt;p&gt;Note: In order for the rest of this section to work for you, make sure you are on &lt;code&gt;v.0.5.0&lt;/code&gt; of Neovim or above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lspconfig
&lt;/h3&gt;

&lt;p&gt;To get started with Neovim’s native LSP, we need to install a few plugins. The first is &lt;a href="https://github.com/neovim/nvim-lspconfig" rel="noopener noreferrer"&gt;&lt;code&gt;nvim-lspconfig&lt;/code&gt;&lt;/a&gt;. Per the README, this plugin:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Handles automatically launching and initializing language servers that are installed on your system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can install it the same we install any other plugin in our &lt;code&gt;init.vim&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" LSP&lt;/span&gt;
Plug &lt;span class="s1"&gt;'neovim/nvim-lspconfig'&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to save, source and &lt;code&gt;:PlugInstall&lt;/code&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Install a language server
&lt;/h3&gt;

&lt;p&gt;The next step is installing the necessary language server(s) for your development environment. This is equivalent to installing a VSCode language extension. &lt;a href="https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md" rel="noopener noreferrer"&gt;Neovim lists available language servers here&lt;/a&gt;. Find one that supports your language of choice, and &lt;strong&gt;install it on your machine&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, I work with Typescript every day, so I need to install a Typescript language server. The list of available servers tells me that I should install &lt;a href="https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md#tsserver" rel="noopener noreferrer"&gt;tsserver&lt;/a&gt;. It can be installed via &lt;code&gt;npm&lt;/code&gt; globally like so:&lt;br&gt;
&lt;/p&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; &lt;span class="nt"&gt;-g&lt;/span&gt; typescript typescript-language-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to note here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You are not installing the language server as a Neovim plugin. You are installing the server as a global package on your computer.&lt;/li&gt;
&lt;li&gt;Not all language servers are installed the same. &lt;code&gt;tsserver&lt;/code&gt; can be installed via &lt;code&gt;npm&lt;/code&gt;, but others may require different installation methods.&lt;/li&gt;
&lt;li&gt;Always refer to &lt;a href="https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md" rel="noopener noreferrer"&gt;the list of available language servers&lt;/a&gt; to find the one you need. If installation instructions are not provided, look at the server’s official &lt;code&gt;README&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configure the language server
&lt;/h3&gt;

&lt;p&gt;After you have successfully installed your language server(s), it’s time to tell Neovim about them. This is going to be the most challenging aspect of setting up native LSP. I am still trying to understand all the moving parts!&lt;/p&gt;

&lt;p&gt;Let’s create a new &lt;code&gt;.lua&lt;/code&gt; file in our namespace directory called &lt;code&gt;lsp.lua&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nvim ~/.config/nvim/lua/YOUR_NAMESPACE/lsp.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file will hold all of our LSP-related configuration. Inside, we need to add some boilerplate code. Note that the code we are adding here is not only &lt;a href="https://github.com/neovim/nvim-lspconfig#keybindings-and-completion" rel="noopener noreferrer"&gt;documented on the lspconfig README&lt;/a&gt;, but it’s also &lt;a href="https://github.com/mjlbach/defaults.nvim/blob/a203b26da856d190809bf1addf66455488335739/init.lua#L202" rel="noopener noreferrer"&gt;the recommended configuration&lt;/a&gt; by &lt;a href="https://github.com/mjlbach" rel="noopener noreferrer"&gt;Michael Lingelbach&lt;/a&gt;, one of Neovim’s core team members, who has worked heavily on the native LSP implementation.&lt;/p&gt;

&lt;p&gt;I will walk through setting up this file step-by-step using &lt;code&gt;tsserver&lt;/code&gt; is an example.&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;-- lspconfig&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="s1"&gt;'lspconfig'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tsserver&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above is a minimal representation of how we can get Neovim’s LSP working with &lt;code&gt;tsserver&lt;/code&gt;. This works just fine, however we can improve on this:&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;-- lspconfig&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;nvim_lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspconfig'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lsp&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lsp&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above is now capable of setting up any arbitrary amount of servers defined in the &lt;code&gt;servers&lt;/code&gt; variable. Installing more than one server? This will effectively loop through each one and wire it up to &lt;code&gt;nvim_lsp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, maybe you added a few more language servers to your setup:&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="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'clangd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pyright'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only thing that has to change in your code is the &lt;code&gt;servers&lt;/code&gt; variable. Everything else can stay the same!&lt;/p&gt;

&lt;p&gt;Note: &lt;code&gt;ipairs&lt;/code&gt; is a &lt;code&gt;lua&lt;/code&gt; function that returns index-value pairs. We don’t need the index, hence the &lt;code&gt;_&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each server’s &lt;code&gt;setup&lt;/code&gt; function is capable of accepting a special &lt;code&gt;on_attach&lt;/code&gt; function that runs when a server attaches to your current Neovim buffer. Think of the current buffer as the current file you are editing in Neovim. Based on the filetype, you can have LSP configured to do certain things.&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;-- lspconfig&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;nvim_lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspconfig'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;on_attach&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;-- Do things when language server attaches to&lt;/span&gt;
    &lt;span class="c1"&gt;-- the current buffer&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lsp&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lsp&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;on_attach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on_attach&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above creates a new &lt;code&gt;on_attach&lt;/code&gt; function and passes it to each server in our list of &lt;code&gt;servers&lt;/code&gt;. But what to put in it?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/neovim/nvim-lspconfig#keybindings-and-completion" rel="noopener noreferrer"&gt;Keybindings and completion&lt;/a&gt; section of the lspconfig &lt;code&gt;README&lt;/code&gt; illustrates adding some basic remaps that will work across every language server. Let’s add these:&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;-- lspconfig&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;nvim_lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspconfig'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;on_attach&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buf_set_keymap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&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_buf_set_keymap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;noremap&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="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="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;-- our first remap&lt;/span&gt;
  &lt;span class="n"&gt;buf_set_keymap&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="s1"&gt;'gD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.declaration()&amp;lt;CR&amp;gt;'&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="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lsp&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lsp&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;on_attach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on_attach&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a lot to digest. Let’s walk through it one step at a time.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We define a function named &lt;code&gt;buf_set_keymap&lt;/code&gt; that accepts a variable number of arguments, denoted by the &lt;code&gt;…&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;buf_set_keymap&lt;/code&gt; is an abstraction over the &lt;code&gt;vim.api.nvim_buf_set_keymap&lt;/code&gt; function.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;We define a table named &lt;code&gt;opts&lt;/code&gt; that holds some basic options for all of our remaps.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;opts.noremap&lt;/code&gt; means “no recursive mapping”&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;opts.silent&lt;/code&gt; means the command will not be echoed in the command line&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;We call &lt;code&gt;buf_set_keymap&lt;/code&gt; for the first time, passing in 4 parameters.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;'n'&lt;/code&gt; tells LSP that this remap can run in NORMAL MODE&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'gD'&lt;/code&gt; is the remap itself. The letters that you will type to perform some action.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.declaration()&amp;lt;CR&amp;gt;'&lt;/code&gt; is the actual command you want to perform. Without the remap, you would have to write this every time. &lt;code&gt;&amp;lt;cmd&amp;gt;&lt;/code&gt; is like typing &lt;code&gt;:&lt;/code&gt;. &lt;code&gt;&amp;lt;CR&amp;gt;&lt;/code&gt; denotes a carriage return, such as hitting &lt;code&gt;ENTER&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;opts&lt;/code&gt; is the options table we declared earlier&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our first remap is &lt;code&gt;'gD'&lt;/code&gt;. Typing it will be equivalent to typing &lt;code&gt;:lua vim.lsp.buf.declaration()&lt;/code&gt; followed by &lt;code&gt;ENTER&lt;/code&gt;. This remap will jump to the definition of the symbol under your cursor while in Neovim. It’s a really helpful remap that I use all the time.&lt;/p&gt;

&lt;p&gt;Now that you have the basics down, let’s add the rest of the remaps defined in the lspconfig &lt;code&gt;README&lt;/code&gt;:&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;-- lspconfig&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;nvim_lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspconfig'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;on_attach&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buf_set_keymap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&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_buf_set_keymap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;noremap&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="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="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;buf_set_keymap&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="s1"&gt;'gD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.declaration()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'gd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.definition()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'K'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.hover()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'gi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.implementation()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;C-k&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.signature_help()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;wa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.add_workspace_folder()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;wr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.remove_workspace_folder()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;wl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;D'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.type_definition()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;rn'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.rename()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;ca'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.code_action()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'gr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.references()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.show_line_diagnostics()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'[d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.goto_prev()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;']d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.goto_next()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.set_loclist()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.formatting()&amp;lt;CR&amp;gt;'&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="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lsp&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lsp&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;on_attach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on_attach&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your language server(s) will be able to run these remaps whenever you please. There’s a lot here, and there’s a good chance you won’t use them all. But it’s helpful to have them in case you do. I like to keep my &lt;code&gt;lsp.lua&lt;/code&gt; file close while I work, referencing these remaps in case I want to perform some actions. Here are a few that I like to use:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;gD&lt;/code&gt; - see the declaration of a symbol (function, variable, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;K&lt;/code&gt; - see some info about a symbol in a hover window&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gr&lt;/code&gt; - show a list of references to a symbol&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;space&amp;gt;e&lt;/code&gt; - show any issues on the current line&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the rest of our remaps have been added to our &lt;code&gt;on_attach&lt;/code&gt; function, let’s save the file and require it inside of &lt;code&gt;init.lua&lt;/code&gt;:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_NAMESPACE/telescope'&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="s1"&gt;'YOUR_NAMESPACE/lightline'&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="s1"&gt;'YOUR_NAMESPACE/gitsigns'&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="s1"&gt;'YOUR_NAMESPACE/lsp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To give your remaps a test drive, make sure to save and source this file. Hop into a project that utilizes one of your language servers and you’re all set! If you're not sure that LSP is working in a particular file, type the command &lt;code&gt;:LspInfo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can read more about Neovim's built-in LSP using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:h lsp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Autocompletion
&lt;/h3&gt;

&lt;p&gt;Next on the list of language-related items to configure is autocompletion. Autocompletion is a huge productivity improvement for developers, and it does not come baked into Neovim. We need to install a few plugins to make it happen.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;init.vim&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#begin&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'~/.config/nvim/plugged'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" Autocompletion&lt;/span&gt;
Plug &lt;span class="s1"&gt;'hrsh7th/nvim-cmp'&lt;/span&gt;
Plug &lt;span class="s1"&gt;'hrsh7th/cmp-nvim-lsp'&lt;/span&gt;
Plug &lt;span class="s1"&gt;'L3MON4D3/LuaSnip'&lt;/span&gt;
Plug &lt;span class="s1"&gt;'saadparwaiz1/cmp_luasnip'&lt;/span&gt;
Plug &lt;span class="s1"&gt;'onsails/lspkind-nvim'&lt;/span&gt;

&lt;span class="k"&gt;call&lt;/span&gt; plug#end&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes we just installed 5 plugins for autocompletion. Don’t freak out. They each have their purpose:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;nvim-cmp&lt;/code&gt; -&lt;a href="https://github.com/hrsh7th/nvim-cmp" rel="noopener noreferrer"&gt;The main plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cmp-nvim-lsp&lt;/code&gt; - &lt;a href="https://github.com/hrsh7th/cmp-nvim-lsp" rel="noopener noreferrer"&gt;A dependency of nvim-cmp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LuaSnip&lt;/code&gt; - &lt;a href="https://github.com/L3MON4D3/LuaSnip" rel="noopener noreferrer"&gt;Autocomplete your snippets&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cmp_luasnip&lt;/code&gt; - &lt;a href="https://github.com/saadparwaiz1/cmp_luasnip" rel="noopener noreferrer"&gt;Snippet completion source&lt;/a&gt;. Not quite sure what it does? But it’s recommended in &lt;a href="https://github.com/mjlbach/defaults.nvim/blob/cddcfb7821f4799df8767f362fc3d024be4fd7d1/init.lua#L40" rel="noopener noreferrer"&gt;&lt;code&gt;defaults.nvim&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lspkind-nvim&lt;/code&gt; - &lt;a href="https://github.com/onsails/lspkind-nvim" rel="noopener noreferrer"&gt;Autocompletion icons&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Save, source and &lt;code&gt;:PlugInstall&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that all of these plugins are installed, let’s glue them together. But, instead of configuring it within its own &lt;code&gt;.lua&lt;/code&gt; file, we’ll want to colocate it with our LSP config. You'll see why.&lt;/p&gt;

&lt;p&gt;Ino our &lt;code&gt;lsp.lua&lt;/code&gt; file:&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;-- ...lspconfig&lt;/span&gt;

&lt;span class="c1"&gt;-- nvim-cmp&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cmp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;lspkind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspkind'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;luasnip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'luasnip'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- better autocompletion experience&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;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completeopt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'menuone,noselect'&lt;/span&gt;

&lt;span class="n"&gt;cmp&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="c1"&gt;-- Format the autocomplete menu&lt;/span&gt;
    &lt;span class="n"&gt;formatting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lspkind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmp_format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;-- Use Tab and shift-Tab to navigate autocomplete menu&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;Tab&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&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="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_next_item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_or_jumpable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_or_jump&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
              &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;end&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="s1"&gt;'&amp;lt;S-Tab&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&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="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_prev_item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jumpable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
              &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;end&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="s1"&gt;'&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;behavior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfirmBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;select&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="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;expand&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lsp_expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&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="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nvim_lsp'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'luasnip'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A lot to take in. Let’s walk it out.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/nvim-cmp#formattingformat-type-funentry-cmpentry-vim_item-vimcompleteditem-vimcompleteditem" rel="noopener noreferrer"&gt;&lt;code&gt;formatting.format&lt;/code&gt;&lt;/a&gt; - Formats the autocomplete menu using &lt;code&gt;lspkind&lt;/code&gt;, a plugin we installed earlier. This adds icons to keywords and variables to autocomplete options.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/nvim-cmp#mapping-type-tablestring-funfallback-function" rel="noopener noreferrer"&gt;&lt;code&gt;mapping&lt;/code&gt;&lt;/a&gt; - Custom keybindings for better autocomplete menu navigation. Press &lt;code&gt;&amp;lt;Tab&amp;gt;&lt;/code&gt; to navigate forward, and &lt;code&gt;&amp;lt;Shift-Tab&amp;gt;&lt;/code&gt; to navigate backwards. Press &lt;code&gt;&amp;lt;CR&amp;gt;&lt;/code&gt; (Enter) to confirm selection.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;snippet&lt;/code&gt; - Enables snippet autocompletion via &lt;code&gt;LuaSnip&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hrsh7th/nvim-cmp#sources-type-tablecmpsourceconfig" rel="noopener noreferrer"&gt;&lt;code&gt;sources&lt;/code&gt;&lt;/a&gt; - Declare autocompletion sources.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After configuring the autocompletion plugins, we need to wire it all up with our language servers:&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;-- lspconfig&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;on_attach&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;-- ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;capabilities&lt;/span&gt; &lt;span class="o"&gt;=&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;lsp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_client_capabilities&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;capabilities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cmp_nvim_lsp'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;update_capabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lsp&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lsp&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;capabilities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;on_attach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on_attach&lt;/span&gt;&lt;span class="p"&gt;,&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;-- ...nvim-cmp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code adds a new property named &lt;code&gt;capabilities&lt;/code&gt; to each language server. All together, &lt;code&gt;lsp.lua&lt;/code&gt; now looks like this:&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;-- lspconfig&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;nvim_lsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspconfig'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;on_attach&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="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buf_set_keymap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&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_buf_set_keymap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufnr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;noremap&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="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="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;buf_set_keymap&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="s1"&gt;'gD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.declaration()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'gd'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.definition()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'K'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.hover()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'gi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.implementation()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;C-k&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.signature_help()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;wa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.add_workspace_folder()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;wr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.remove_workspace_folder()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;wl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;D'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.type_definition()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;rn'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.rename()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;ca'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.code_action()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'gr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.references()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;e'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.show_line_diagnostics()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'[d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.goto_prev()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;']d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.goto_next()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.diagnostic.set_loclist()&amp;lt;CR&amp;gt;'&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="n"&gt;buf_set_keymap&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="s1"&gt;'&amp;lt;space&amp;gt;f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;cmd&amp;gt;lua vim.lsp.buf.formatting()&amp;lt;CR&amp;gt;'&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="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;capabilities&lt;/span&gt; &lt;span class="o"&gt;=&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;lsp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_client_capabilities&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;capabilities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cmp_nvim_lsp'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;update_capabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;servers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'tsserver'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lsp&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;nvim_lsp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lsp&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;capabilities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;on_attach&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on_attach&lt;/span&gt;&lt;span class="p"&gt;,&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;-- nvim-cmp&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cmp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;lspkind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'lspkind'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;luasnip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'luasnip'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- better autocompletion experience&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;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completeopt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'menuone,noselect'&lt;/span&gt;

&lt;span class="n"&gt;cmp&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="c1"&gt;-- Format the autocomplete menu&lt;/span&gt;
    &lt;span class="n"&gt;formatting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lspkind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmp_format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;mapping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;-- Use Tab and shift-Tab to navigate autocomplete menu&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;Tab&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&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="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_next_item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_or_jumpable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand_or_jump&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
              &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;end&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="s1"&gt;'&amp;lt;S-Tab&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&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="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;select_prev_item&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;elseif&lt;/span&gt; &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jumpable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
              &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
              &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;end&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="s1"&gt;'&amp;lt;CR&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;behavior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfirmBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nb"&gt;select&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="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;snippet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;expand&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;luasnip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lsp_expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&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="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'nvim_lsp'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'luasnip'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Treesitter
&lt;/h3&gt;

&lt;p&gt;The last plugin to improve Neovim's language support is &lt;code&gt;treesitter&lt;/code&gt;. &lt;a href="https://github.com/nvim-treesitter/nvim-treesitter#installation" rel="noopener noreferrer"&gt;We will be installing it&lt;/a&gt; for improved syntax highlighting. In your &lt;code&gt;init.vim&lt;/code&gt;, add the following to you list of declared plugins:&lt;br&gt;
&lt;/p&gt;

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

call plug#begin('~/.config/nvim/plugged')

" ...plugins

" Treesitter
Plug 'nvim-treesitter/nvim-treesitter', { 'do': ':TSUpdate' }

call plug#end()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a &lt;code&gt;treesitter.lua&lt;/code&gt; file in your namespace directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nvim ~/.config/nvim/lua/YOUR_NAMESPACE/treesitter.lua
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside this file we'll add the following:&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="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;treesitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nvim-treesitter.configs'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;treesitter&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;highlight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;enable&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, update your &lt;code&gt;init.lua&lt;/code&gt; to include &lt;code&gt;treesitter.lua&lt;/code&gt;:&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="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_NAMESPACE/telescope'&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="s1"&gt;'YOUR_NAMESPACE/lightline'&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="s1"&gt;'YOUR_NAMESPACE/gitsigns'&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="s1"&gt;'YOUR_NAMESPACE/lsp'&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="s1"&gt;'YOUR_NAMESPACE/treesitter'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Source your &lt;code&gt;init.vim&lt;/code&gt; and you should see an improved syntax highlighting experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remaps
&lt;/h2&gt;

&lt;p&gt;The following will not be a tutorial on remaps themselves, as remaps in Vim are a topic worthy of a separate article. There are also plenty of great resources on the topic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vim.fandom.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_1)" rel="noopener noreferrer"&gt;Mapping Keys in Vim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alldrops.info/posts/vim-drops/2018-05-15_understand-vim-mappings-and-create-your-own-shortcuts/" rel="noopener noreferrer"&gt;Understand Vim Mappings and Create Your Own Shortcuts!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnvimscriptthehardway.stevelosh.com/chapters/03.html" rel="noopener noreferrer"&gt;Basic Mapping - Learn Vimscript the Hard Way&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, I will quickly explain what they are and why you would want to use them, and ultimately hope to inspire you to seek out your own.&lt;/p&gt;

&lt;p&gt;Let's begin.&lt;/p&gt;

&lt;p&gt;Over the course of this article, we've covered many plugins that introduce their own commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" Telescope&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Telescope find_files
&lt;span class="p"&gt;:&lt;/span&gt;Telescope &lt;span class="k"&gt;buffers&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Telescope live_grep

&lt;span class="c"&gt;" vim-fugitive&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git
&lt;span class="p"&gt;:&lt;/span&gt;Git &lt;span class="nb"&gt;add&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;Git status
&lt;span class="p"&gt;:&lt;/span&gt;Git blame

&lt;span class="c"&gt;" lspconfig&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;LspInfo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of these plugin-specific commands, Vim has its own set of commands. It can be cumbersome to type out the entire command every time you want to use it. Instead, you can "map" these commands to a pattern of keystrokes. These are remaps.&lt;/p&gt;

&lt;p&gt;I declare my remaps inside &lt;code&gt;init.vim&lt;/code&gt;. Here's an example of one of my favorites:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" remaps&lt;/span&gt;
nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;C&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;Telescope find_files&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Cr&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remap above is &lt;code&gt;&amp;lt;C-p&amp;gt;&lt;/code&gt;, or &lt;code&gt;CTRL+p&lt;/code&gt;. Each time I type this pattern, the command &lt;code&gt;:Telescope find_files&amp;lt;Cr&amp;gt;&lt;/code&gt; will run. &lt;code&gt;&amp;lt;Cr&amp;gt;&lt;/code&gt; must be explicitly declared in order for the command to be "entered", i.e - with &lt;code&gt;&amp;lt;Enter&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here are a few others:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="c"&gt;" ...general settings&lt;/span&gt;

&lt;span class="c"&gt;" ...plugins&lt;/span&gt;

&lt;span class="c"&gt;" remaps&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; mapleader &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;

nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;h&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;wincmd&lt;/span&gt; &lt;span class="k"&gt;h&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Cr&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;j&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;wincmd&lt;/span&gt; &lt;span class="k"&gt;j&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Cr&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;k&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;wincmd&lt;/span&gt; &lt;span class="k"&gt;k&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Cr&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;l&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;wincmd&lt;/span&gt; &lt;span class="k"&gt;l&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Cr&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

nnoremap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;C&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;Telescope find_files&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Cr&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;mapleader&lt;/code&gt; is a special character you can appoint to "lead" your remaps. This both simplifies remap keystrokes and prevents unwanted remap collisions. It defaults to &lt;code&gt;\&lt;/code&gt;, but here I'm setting it to an empty space, &lt;code&gt;' '&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;leader&amp;gt;h&lt;/code&gt; is equivalent to typing the &lt;code&gt;&amp;lt;Space&amp;gt;&lt;/code&gt; bar followed by the letter &lt;code&gt;h&lt;/code&gt;. Doing so will run the command &lt;code&gt;:wincmd h&amp;lt;Cr&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I believe remaps are one of those things you'll undoubtedly look for as you notice pain points in your own workflow. It can be helpful to puruse others' Vim configurations and see what they use for inspiration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips and tricks
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Use relative line numbers&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your Neovim terminal supports line numbers with the following setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you get more advanced with your Vim movements, you'll look for ways to move vertically faster. One of the ways to do this is to explicitly type the line number you want to go to. This is easier with relative line numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;number&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nb"&gt;relativenumber&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does it work? The line your cursor is currently on will be denoted "line 0". The rest of the lines above and below will be numbered relative to their distance from line 0. For example, the line directly below your cursor will be line 1. So will the line directly above. To jump to line 1 below, type the number &lt;code&gt;1&lt;/code&gt; plus the letter &lt;code&gt;j&lt;/code&gt;. To go to line 1 above, type the number &lt;code&gt;1&lt;/code&gt; plus the letter &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Alias the &lt;code&gt;nvim&lt;/code&gt; command&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An alias is a shortcut defined in your terminal shell’s settings that allows you to run regular commands with custom names. An example alias that many people use is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git checkout"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of typing &lt;code&gt;git checkout&lt;/code&gt; every time I want to check out a branch, I just need to type &lt;code&gt;gc&lt;/code&gt;. My terminal knows that when I enter &lt;code&gt;gc&lt;/code&gt;, what I really mean is &lt;code&gt;git checkout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Defining your aliases is dependent on which terminal shell you are using. There are a number of shells that people use these days, like &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;zsh&lt;/code&gt; and &lt;code&gt;fish&lt;/code&gt;. I use &lt;code&gt;zsh&lt;/code&gt;, but you may not be. If you don’t know what shell you are using, you can run the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$SHELL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aliases are defined in your shell’s configuration file. For bash users, that would be &lt;code&gt;.bashrc&lt;/code&gt;. For zsh users, &lt;code&gt;.zshrc&lt;/code&gt;. etc. etc. Your mileage may vary. Locate your specific file, and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;vim &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"nvim"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now anytime you run the command &lt;code&gt;vim&lt;/code&gt;, you will really be running the command &lt;code&gt;nvim&lt;/code&gt;. If you still want access to the original &lt;code&gt;vim&lt;/code&gt; command, you can prefix it with a &lt;code&gt;\&lt;/code&gt; character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="se"&gt;\v&lt;/span&gt;im
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command above will use the unaliased &lt;code&gt;vim&lt;/code&gt; command. The more you know, right?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Save your Neovim configuration in Git&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a major key! &lt;a href="https://jakewiesler.com/blog/managing-dotfiles" rel="noopener noreferrer"&gt;I've written a blog post on this very topic&lt;/a&gt;. I suggest you take a look. Your Neovim configuration is a subset of a larger group of configuration files dubbed "dotfiles". Dotfiles are the life blood of a good development environment. Make sure you keep yours portable!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Experiment!&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Programming is way more fun when you're being efficient. Neovim can get you there over time, but you have to be willing to try new things. New plugins, new remaps, etc.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Keep an eye on the community&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neovim has an active &lt;a href="https://www.reddit.com/r/neovim" rel="noopener noreferrer"&gt;subreddit&lt;/a&gt; and &lt;a href="https://neovim.discourse.group/" rel="noopener noreferrer"&gt;forum&lt;/a&gt;. These are great places to learn and find new plugins to experiment with.&lt;/p&gt;

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

&lt;p&gt;Phew! If you've stuck with me to the end, I appreciate you! This was a massive article, one of the biggest I've ever written. We discussed settings, plugins, color schemes, language support, autocompletion and remaps. I hope you learned something!&lt;/p&gt;

&lt;p&gt;If you're curious to see my configuration, you can checkout &lt;a href="https://github.com/jakewies/.dotfiles/tree/main/nvim/.config/nvim" rel="noopener noreferrer"&gt;my dotfiles on GitHub, specifically the &lt;code&gt;nvim&lt;/code&gt; directory&lt;/a&gt;. And if you're just getting started on your Vim journey, please let me know! It's been a challenging and exciting one for me, and I'm only getting started.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>career</category>
      <category>learning</category>
      <category>coding</category>
    </item>
    <item>
      <title>Manage Your Dotfiles Like a Superhero</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Mon, 20 Sep 2021 21:01:04 +0000</pubDate>
      <link>https://dev.to/jakewies/manage-your-dotfiles-like-a-superhero-4gpd</link>
      <guid>https://dev.to/jakewies/manage-your-dotfiles-like-a-superhero-4gpd</guid>
      <description>&lt;p&gt;I'm going to assume you are reading this because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;you have one or more dotfiles lying around and you don't know how to organize them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you heard that you can save dotfiles for later use in case your computer explodes or something.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you've seen other people's dotfiles on GitHub, and they look so damn cool.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you like superheros.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing about programming, or anything you try to teach yourself for that matter, is that when you start, &lt;em&gt;you don't know what you don't know&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Dotfiles are a perfect example. You don't know about them until either someone tells you they are thing, or you fall down a rabbit hole and find yourself setting the &lt;code&gt;$PATH&lt;/code&gt; variable in your &lt;code&gt;.bashrc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Eventually you go from not knowing about dotfiles, to really caring about them. You realize just how essential they are for your workflow. How having a good relationship with them can accelerate your ability to solve problems.&lt;/p&gt;

&lt;p&gt;And most important, you realize &lt;em&gt;just how picky you really are&lt;/em&gt;, and how your dotfiles are a perfect outlet for such pickiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're probably doing
&lt;/h2&gt;

&lt;p&gt;If you are not managing your dotfiles in any way, then they are probably strewn about your home directory. You probably have a file for your shell, say a &lt;code&gt;.bashrc&lt;/code&gt; or a &lt;code&gt;.zshrc&lt;/code&gt;. You probably have a &lt;code&gt;.vimrc&lt;/code&gt; or something like that. You might even have a &lt;code&gt;.gitconfig&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this scenario your dotfiles are ephemeral. They exist as long as your machine exists. And when your machine goes down for the long nap, so will your dotfiles. You don't want to be that girl or guy setting up a new machine from scratch every single time. It sucks. I've been there. Avoid at all costs!&lt;/p&gt;

&lt;h2&gt;
  
  
  What you should be doing
&lt;/h2&gt;

&lt;p&gt;Your dotfiles should be stored in such a way that you can access them anywhere at any time. If something happens to your current machine, or you need to setup another one, you're a &lt;code&gt;git clone&lt;/code&gt; away from an identical development environment. That is the dream.&lt;/p&gt;

&lt;p&gt;You may have come across resources like &lt;em&gt;Atlassian's&lt;/em&gt; &lt;a href="https://www.atlassian.com/git/tutorials/dotfiles"&gt;How to store dotfiles&lt;/a&gt;, or &lt;a href="https://yadm.io/"&gt;Yet Another Dotfiles Manager&lt;/a&gt;. I've tried them both. They work, but they're a little involved.&lt;/p&gt;

&lt;p&gt;Instead I recommend you use &lt;a href="https://www.gnu.org/software/stow/"&gt;GNU Stow&lt;/a&gt;, a utility that lets you symlink files from different parts of your machine and make them appear as if they are installed in the same place. This will make sense soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to do it
&lt;/h2&gt;

&lt;p&gt;Step one is to create a &lt;code&gt;.dotfiles&lt;/code&gt; directory in your home directory. For example, let's say Alice's home directory is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;/Users/alice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alice will create her &lt;code&gt;.dotfiles&lt;/code&gt; directory here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /Users/alice/.dotfiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you'll need to get your hands on the &lt;code&gt;stow&lt;/code&gt; package. Luckily it is a pretty ubiquitous piece of software, so most package managers should make it available to you. For MacOS users you can use &lt;a href="https://formulae.brew.sh/formula/stow"&gt;&lt;code&gt;homebrew&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The third step is to move existing dotfiles to the &lt;code&gt;.dotfiles&lt;/code&gt; directory, and/or create new ones depending on your scenario. This step requires some explanation of how &lt;code&gt;stow&lt;/code&gt; works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding &lt;code&gt;stow&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;stow&lt;/code&gt; has a few key concepts that, when understood, should make this entire process a breeze. These concepts can be found in the &lt;a href="https://www.gnu.org/software/stow/manual/stow.html#Terminology"&gt;Terminology documentation&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;package&lt;/strong&gt; is a folder containing related dotfiles.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;stow directory&lt;/strong&gt; is a folder containing one or more packages.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;target directory&lt;/strong&gt; is the location in which a package's contents will be symlinked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What this will look like in practice is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;target-directory
├── stow-directory
│   ├── package-1
│   │   └── .dotfile-1
│   ├── package-2
│   │   └── .dotfile-2
│       └── .dotfile-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's use Alice again as an example. Alice's &lt;code&gt;.dotfiles&lt;/code&gt; directory is located at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;/Users/alice/.dotfiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.dotfiles&lt;/code&gt; directory is her &lt;strong&gt;stow directory&lt;/strong&gt;. Currently it's empty. She will need to create one or more &lt;strong&gt;packages&lt;/strong&gt; within it to house her dotfiles.&lt;/p&gt;

&lt;p&gt;Alice has the following dotfiles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.bashrc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.vimrc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.gitconfig&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since each file belongs to a different "package", &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; respectively, Alice will create a package for each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /Users/alice/.dotfiles &lt;/span&gt;

&lt;span class="nb"&gt;mkdir &lt;/span&gt;bash
&lt;span class="nb"&gt;mkdir &lt;/span&gt;vim
&lt;span class="nb"&gt;mkdir &lt;/span&gt;git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, Alice can move each dotfile into their respective package directory. As a result, the &lt;code&gt;.dotfiles&lt;/code&gt; directory will look 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;.dotfiles
├── bash
│   └── .bashrc
├── vim
│   └── .vimrc
└── git
    └── .gitconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.dotfiles&lt;/code&gt; is the stow directory. &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; are package directories. Their contents are the dotfiles themselves. Now, all that's left is to use the &lt;code&gt;stow&lt;/code&gt; command to target each package inside of the stow directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /Users/alice/.dotfiles &lt;/span&gt;

stow bash
stow vim
stow git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commands above will symlink the contents of each package to the &lt;strong&gt;target directory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But what is the target directory?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;stow&lt;/code&gt;, the target directory is one directory above the stow directory. In Alice's case, if her stow directory is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;/Users/alice/.dotfiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then her target directory is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Let me explain these symlinks a little bit better. When you run &lt;code&gt;stow &amp;lt;package&amp;gt;&lt;/code&gt;, the &lt;code&gt;stow&lt;/code&gt; utility will create the least amount of symlinks necessary to mirror the contents of the package to your target directory. If your package had a single dotfile, &lt;code&gt;stow&lt;/code&gt; will create a single symlink:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/alice
├── .dotfiles
│   ├── vim
│   │   └── .vimrc
├── .vimrc -&amp;gt; .dotfiles/vim/.vimrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;br&gt;
  The arrow (-&amp;gt;) represents a symlinked file pointing to its original source.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;This might seem obvious to some, but what happens if you have a subfolder within a package? For instance, it's common for Vim users to not only have a &lt;code&gt;.vimrc&lt;/code&gt; file in their home directory, but also a &lt;code&gt;.vim&lt;/code&gt; directory with scripts and plugins. How would you stow that?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/alice
├── .dotfiles
│   ├── vim
│       └── .vimrc
│       └── .vim
|           └── script.vim
├── .vimrc -&amp;gt; .dotfiles/vim/.vimrc
├── .vim   -&amp;gt; .dotfiles/vim/.vim
|   └── script.vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a ton of flexibilty, because very often command line tools like &lt;code&gt;git&lt;/code&gt; and &lt;code&gt;vim&lt;/code&gt; use more than dotfiles stored in the home directory. They have their own directories and subdirectories, and being able to model this out within a package is really powerful.&lt;/p&gt;

&lt;p&gt;Once you can visualize how &lt;code&gt;stow&lt;/code&gt; mirrors a package's contents to the target directory, structuring your &lt;code&gt;.dotfiles&lt;/code&gt; directory will be easy peezy lemon squeezy.&lt;/p&gt;

&lt;h3&gt;
  
  
  One more example
&lt;/h3&gt;

&lt;p&gt;The last example I'll give should really drive &lt;code&gt;stow&lt;/code&gt; symlinks home. Let's say Alice is a &lt;code&gt;vim&lt;/code&gt; user, but instead of traditional vim she uses &lt;a href="https://neovim.io/"&gt;Neovim&lt;/a&gt;. Neovim doesn't use a &lt;code&gt;.vimrc&lt;/code&gt; file in her home directory, but instead an &lt;code&gt;init.vim&lt;/code&gt; file which is located at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/alice/.config/nvim/init.vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;init.vim&lt;/code&gt; file is heavily nested. If Alice wants to stow this, how would she do it?&lt;/p&gt;

&lt;p&gt;It's pretty simple actually. In her &lt;code&gt;.dotfiles&lt;/code&gt; directory she creates a package called &lt;code&gt;nvim&lt;/code&gt;. It would look 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;.dotfiles
├── nvim
│   ├── .config
│       └── nvim
|           └── init.vim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The folder structure starting from &lt;code&gt;.dotfiles/nvim&lt;/code&gt; is the same structure that is expected in Alice's home directory. Now she just needs to stow the &lt;code&gt;nvim&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /Users/alice/.dotfiles &lt;/span&gt;

stow nvim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the result will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Users/alice
├── .dotfiles
│   ├── nvim
│       └── .config
|           └── nvim
|               └── init.vim
├── .config -&amp;gt; .dotfiles/nvim/.config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Storing dotfiles in git
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.dotfiles&lt;/code&gt; directory is just like any old directory on your machine. You can &lt;code&gt;git init&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt; up to your git repository of choice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# /path/to/your/.dotfiles&lt;/span&gt;

git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"storing initial dotfiles"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add a &lt;code&gt;.gitignore&lt;/code&gt; file to prevent unwanted configs from being pushed to your remote repository, and a &lt;code&gt;README&lt;/code&gt; to illustrate how it all works.&lt;/p&gt;

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

&lt;p&gt;I hope this has helped you gain a better understanding of how to manage your dotfiles. This is by no means the only way to do it, but it is my preferred way.&lt;/p&gt;

&lt;p&gt;You can really take this as far as you want to go. For instance, I am planning on creating an &lt;code&gt;install&lt;/code&gt; script that can be run on new machines. This script will install a bunch of dependencies like &lt;code&gt;node&lt;/code&gt; and the like, stow my dotfiles so the symlinks are created, etc.&lt;/p&gt;

&lt;p&gt;If you want to take a peak at my dotfiles, you can &lt;a href="https://github.com/jakewies/.dotfiles"&gt;see them here&lt;/a&gt;. They're still a work in progress, but it should be a good resource for learning.&lt;/p&gt;

&lt;p&gt;If you made it to the end, thanks for reading! Have questions? See a mistake? Just want to say hi? Reach out to me on &lt;a href="https://twitter.com/jakewies"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>git</category>
      <category>tooling</category>
    </item>
    <item>
      <title>The Next Iteration of the Web</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Mon, 13 Sep 2021 10:27:00 +0000</pubDate>
      <link>https://dev.to/jakewies/the-next-iteration-of-the-web-580f</link>
      <guid>https://dev.to/jakewies/the-next-iteration-of-the-web-580f</guid>
      <description>&lt;p&gt;&lt;em&gt;The following is a post from my newsletter, &lt;strong&gt;Original Copy&lt;/strong&gt;. Want early access to posts like this? &lt;a href="https://jakewiesler.com"&gt;Subscribe here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Tim Berners-Lee is best known for inventing the World Wide Web. In 1989, while working at CERN, he &lt;a href="https://home.cern/science/computing/birth-web/short-history-web"&gt;proposed an idea&lt;/a&gt;: &lt;em&gt;"a&lt;/em&gt; &lt;strong&gt;&lt;em&gt;web&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;of&lt;/em&gt; &lt;strong&gt;&lt;em&gt;hypertext documents&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;viewable by&lt;/em&gt; &lt;strong&gt;&lt;em&gt;browsers&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You can see the appeal of such a technology. An industry ripe with innovation and experimentation requires rapid knowledge transfer.  The idea was so powerful that a few years later it extended to the general public. &lt;em&gt;The Internet&lt;/em&gt; was born.&lt;/p&gt;

&lt;p&gt;This first iteration of the Web, dubbed &lt;em&gt;Web 1.0&lt;/em&gt;, lasted from the early 90s until about 2004. It was all about consuming static content, usually in the form of texts, links and images. Creators of this content were the "webmasters" who knew how to publish on the Web. An uncommon skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web 2.0
&lt;/h2&gt;

&lt;p&gt;Today we experience an interactive and social Web. Anyone can create and publish content with the click of a button. No webmaster needed. This is &lt;em&gt;Web 2.0&lt;/em&gt;, the modern Web.&lt;/p&gt;

&lt;p&gt;Personal computers and smart phones led the way. Tech giants like Google and Facebook followed. The applications created by these companies empower users to learn, shared and build. They grew faster than the open protocols created during the Web 1.0 era. A massive improvement, but not without consequences.&lt;/p&gt;

&lt;p&gt;Web 2.0 is a &lt;strong&gt;centralized&lt;/strong&gt; Web. Applications are owned by a single entity or corporation.  Within centralized applications, the owner is the single source of truth. &lt;em&gt;They&lt;/em&gt; make the rules, and &lt;em&gt;you&lt;/em&gt; play the game.&lt;/p&gt;

&lt;p&gt;The owner has control of everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;li&gt;Governance&lt;/li&gt;
&lt;li&gt;Data&lt;/li&gt;
&lt;li&gt;Equity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This has lead to issues of censorship, security and the topic that intrigues me the most: privacy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data wars
&lt;/h3&gt;

&lt;p&gt;In 1973, the artist Richard Serra coined the phrase, &lt;a href="https://techhq.com/2018/04/facebook-if-something-is-free-you-are-the-product/#:~:text=In%201973%2C%20he%20said%3A%20%E2%80%9C,delivered%20people%20to%20an%20advertiser."&gt;"If something is free, you're the product."&lt;/a&gt; At the time he was referring to television. His point was that television was the audience. Its main purpose was to shuttle viewers to an advertiser. A funnel.&lt;/p&gt;

&lt;p&gt;Today's most popular Internet applications, Google, Facebook, Twitter etc., act the same way. They're all free to use, yet they are among the richest companies in the United States. At the time of writing this, &lt;a href="https://companiesmarketcap.com/usa/largest-companies-in-the-usa-by-market-cap/"&gt;Google and Facebook are in the top 5 companies in the US by market capitalization&lt;/a&gt;. &lt;em&gt;And their product is free!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The modern Web empowers companies to give away their technology at no cost. This removes barriers to entry so users can pile in. The company's only goal is to get you in and keep you in as long as possible, because you have something they need:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A pair of eyeballs&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The more users they acquire, the more data they collect and the more money they make off of advertising. This is THE playbook used by technology companies all over the world. &lt;em&gt;Why?&lt;/em&gt; Because it works! It works so well that you and I, aware of this madness, continue to "use".&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Enter Web 3.0&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I was too young to recognize the dramatic shift from Web 1.0 to Web 2.0. One day you wake up, and MySpace exists. Everyone's talking about it. Everyone's using it. It happened fast!&lt;/p&gt;

&lt;p&gt;Now that I'm older, and I've been in this Web Development racket for almost a decade, I have my ear closer to the ground. I sense the paradigm shifting again.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web 3.0&lt;/em&gt;, the rapidly approaching (&lt;em&gt;pssssst!&lt;/em&gt; It's already here) next iteration of the Web, is a &lt;strong&gt;decentralized&lt;/strong&gt; Web. It aims to flip the system on its head, turning the keys over to the participants of the network (the users).&lt;/p&gt;

&lt;p&gt;Web 3.0 applications are usually referred to as &lt;em&gt;Decentralized applications&lt;/em&gt; or Dapps. They are not owned by a single entity. They are not deployed to a centralized server. Rather, they're isolated pieces of code deployed to a peer-to-peer network: a blockchain.&lt;/p&gt;

&lt;p&gt;The validity of the blockchain and its app ecosystem is upheld through incentives. These incentives take the form of digital tokens - Cryptocurrency. Awarded to participants on the network through various "&lt;a href="https://decrypt.co/resources/tokenomics"&gt;tokenomics&lt;/a&gt;".&lt;/p&gt;

&lt;p&gt;The use of these tokens is where things get interesting. The most basic function is paying for actions taken on the network. Any action that effect's the "state" of the blockchain costs money - a transaction. And that's only scratching the surface.&lt;/p&gt;

&lt;p&gt;Anyone can use a decentralized app. Logins are anonymous. Payments are built-in, anonymous and backed by cryptography.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The early adopters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With all the buzz around Web 3.0, it is still early days.&lt;/p&gt;

&lt;p&gt;Certain use cases still rely on some layer of centralization. Centralization makes things easier to coordinate. Achieving full decentralization introduces difficult problems like communication, incentives and consensus. But they are problems worth solving.&lt;/p&gt;

&lt;p&gt;There is a mass migration of smart engineers and designers heading to the Wild West of Web 3.0. I am excited for its future.&lt;/p&gt;

&lt;p&gt;Most of my work is still in Web 2.0 land, but I'm ramping up on Web 3.0 technology. A lot of the content I'll be sharing will focus on it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>blockchain</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build a JAMStack Blog with NextJS and Ghost</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Thu, 26 Aug 2021 14:09:41 +0000</pubDate>
      <link>https://dev.to/jakewies/build-a-jamstack-blog-with-nextjs-and-ghost-2pnh</link>
      <guid>https://dev.to/jakewies/build-a-jamstack-blog-with-nextjs-and-ghost-2pnh</guid>
      <description>&lt;p&gt;I have a certain affinity for &lt;a href="https://ghost.org/"&gt;Ghost&lt;/a&gt;, an open-source publishing platform created in 2013 and built on &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt;. It was the first platform I used to build my website back in 2015, but over time curiosity struck and I moved away from it.&lt;/p&gt;

&lt;p&gt;Now, in 2021, I'm revisiting my old friend again. Why? It's latest release, &lt;a href="https://ghost.org/changelog/4/"&gt;version 4&lt;/a&gt;, brings some awesome &lt;a href="https://ghost.org/features/"&gt;new features&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A revamped Dashboard&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ghost.org/docs/newsletters/"&gt;Native support for Newsletters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ghost.org/docs/members/#2-subscriptions"&gt;Paid subscriptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ghost.org/integrations/"&gt;Tons of integrations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A cheaper Ghost(Pro) starter plan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a huge update, and I'm a sucker for brand redesigns. However, since 2015 I've acquired the skills to build a site without needing to rely on tools like Ghost, so why go back?&lt;/p&gt;

&lt;h2&gt;
  
  
  The beauty of Ghost
&lt;/h2&gt;

&lt;p&gt;Personal publishing and gated content as a business continues to increase in popularity, and Ghost is well positioned in this space. &lt;a href="https://ghost.org/docs/members/"&gt;The new Members feature&lt;/a&gt; gives creators a way to monetize their work with very little effort on the administrative side.&lt;/p&gt;

&lt;p&gt;Managing content is a real pleasure. You can write posts, schedule them for publishing and send them straight to the inbox of your members. You can mark some posts as free and others as paid, providing a way for your audience to fund your work.&lt;/p&gt;

&lt;p&gt;And all of this is controlled from a clean Dashboard. It's a really great way to manage your content. Much better than writing in markdown files.&lt;/p&gt;

&lt;h2&gt;
  
  
  My issues with Ghost
&lt;/h2&gt;

&lt;p&gt;Customization. I want full control of how my site looks and feels. In reality, Ghost does not stop you from having this level of power. The platform has &lt;a href="https://ghost.org/themes/"&gt;a rich theme ecosystem&lt;/a&gt;, with paid and free themes available to all. However, if you want to build a theme of your own &lt;a href="https://ghost.org/docs/themes/"&gt;you have to do it in handlebars&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll pass.&lt;/p&gt;

&lt;p&gt;Another is performance. Ghost is pretty fast and comes with a lot of niceties out of the box (SEO for one), but it's not fast enough for me. I want blazing fast, and I have the skills to do that on my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the JAMStack
&lt;/h2&gt;

&lt;p&gt;The good news is that &lt;a href="https://ghost.org/docs/jamstack/"&gt;Ghost is front-end agnostic&lt;/a&gt;, meaning you can "bring your own" front-end. Manage content in Ghost, and query for it using their APIs. A Headless CMS.&lt;/p&gt;

&lt;p&gt;Headless because the CMS has no dedicated front-end. You can plop whichever one you want on top, and the CMS will abide. It's a beautiful thing really, and gets us to the foundation of JAMStack.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;J&lt;/strong&gt;avaScript - build your front-end with a modern JavaScript framework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;PIs - Query for your content with Ghost's APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;M&lt;/strong&gt;arkup - Write your content as markup in Ghost&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this post we'll use &lt;a href="https://nextjs.org/"&gt;NextJS&lt;/a&gt; as our front-end framework of choice. With NextJS, we can build a blazing fast front-end and query Ghost for our content.&lt;/p&gt;

&lt;p&gt;It's the best of both worlds. Keep the Dashboard and Editor on Ghost's side, and the performance and developer experience on NextJS's side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Ghost
&lt;/h2&gt;

&lt;p&gt;The first thing we need to do is &lt;a href="https://ghost.org/docs/ghost-cli/"&gt;install Ghost on our development machine&lt;/a&gt;. Open a terminal and install the latest version of &lt;a href="https://github.com/TryGhost/Ghost-CLI"&gt;&lt;code&gt;ghost-cli&lt;/code&gt;&lt;/a&gt; globally using your package manager of choice. I am a &lt;code&gt;yarn&lt;/code&gt; man.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add ghost-cli@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to note that your version of &lt;code&gt;node&lt;/code&gt; matters. I've run into issues in the past when being on some of the latest versions. At time of writing this, &lt;a href="https://ghost.org/docs/faq/node-versions/"&gt;Ghost recommends v14 of &lt;code&gt;node&lt;/code&gt;&lt;/a&gt;, their long term support version.&lt;/p&gt;

&lt;p&gt;Next we'll run a few terminal commands to create a directory and install a ghost instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;ghost-development

&lt;span class="nb"&gt;cd &lt;/span&gt;ghost-development

ghost &lt;span class="nb"&gt;install local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two commands are self-explanatory. The name of your directory does not matter. The third command, &lt;code&gt;ghost install local&lt;/code&gt;, runs a number of scripts and sets you up with a local installation of Ghost hosted at a local url. Usually this url is &lt;code&gt;http://localhost:2368&lt;/code&gt;. If you navigate to it in your browser of choice, what you'll see is &lt;a href="https://github.com/TryGhost/Casper"&gt;Casper&lt;/a&gt;, the default Ghost theme.&lt;/p&gt;

&lt;p&gt;If you go to &lt;code&gt;http://localhost:2368/ghost&lt;/code&gt;, you'll hit the admin side of Ghost. This will require you to create an account. It's not going to be a live account, just a local one that will let you interface with the admin side for development 🐳.&lt;/p&gt;

&lt;p&gt;Put in whatever data you want, skip the staff users section and boom. You're logged in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing NextJS
&lt;/h2&gt;

&lt;p&gt;Now that we have Ghost up and running locally, we'll setup our front-end. In a separate directory, &lt;a href="https://nextjs.org/docs/getting-started#setup"&gt;bootstrap a NextJS app&lt;/a&gt;. Again, with &lt;code&gt;yarn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn create next-app my-next-front-end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will set you up with a NextJS project in the &lt;code&gt;my-next-front-end&lt;/code&gt; directory. That's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying for blog posts
&lt;/h2&gt;

&lt;p&gt;When you start up a Ghost site for the first time, the theme will deploy with some "Getting Started" posts. This is adequate for our purpose. No need to create more. I mean, you can if you want. Do you.&lt;/p&gt;

&lt;p&gt;Eventually we'll need to fetch those blog posts. Ghost has two APIs, the &lt;a href="https://ghost.org/docs/content-api/"&gt;Content API&lt;/a&gt; and the &lt;a href="https://ghost.org/docs/admin-api/"&gt;Admin API&lt;/a&gt;. For a list of blog posts we'll need to interface with the Content API.&lt;/p&gt;

&lt;p&gt;Ghost also provides a few API clients to make things easy. Let's install the client for the Content API. In your NextJS app directory run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @tryghost/content-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're interested, here is &lt;a href="https://ghost.org/docs/content-api/javascript/"&gt;the documentation for the &lt;code&gt;@tryghost/content-api&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have our dependency installed, we'll write some logic to fetch a list of blog posts. I like to put this sort of logic in a &lt;code&gt;lib&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course you can also create the folder in your text editor, but we're terminal first here.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;lib&lt;/code&gt; directory we'll create a &lt;code&gt;ghost.js&lt;/code&gt; file to keep it simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;lib
&lt;span class="nb"&gt;touch &lt;/span&gt;ghost.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright now we're cooking. Let's open up &lt;code&gt;ghost.js&lt;/code&gt; and import &lt;code&gt;@tryghost/content-api&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GhostContentAPI&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;@tryghost/content-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to start using the client we'll need to initiate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GhostContentAPI&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;@tryghost/content-api&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;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GhostContentAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&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="na"&gt;key&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="na"&gt;version&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've left the 3 configuration properties empty for a reason. They each need explanation:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Both the &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; values will be different in a development and production environment. We are only concerned with the development environment for now. Navigate to the local instance of your Ghost admin (&lt;code&gt;http://localhost:2368/ghost&lt;/code&gt;) in the browser. On the left hand side you should see an "Integrations" section. Click it. We will need to create a new custom integration to get these values.&lt;/p&gt;

&lt;p&gt;Click the "Add Custom Integration" button. Name the integration whatever you want. I'm calling it "NextJS Front-end". Click "Create", and you should then see a few values. The ones you want are the "Content API Key" and the "API URL". These will be your development &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; values. Remember, in production they will be different.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;version&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;At time of writing, this is the 3rd version of the Ghost API, though they also support a &lt;code&gt;canary&lt;/code&gt; value. Let's be safe and use &lt;code&gt;'v3'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our final configuration should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GhostContentAPI&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;@tryghost/content-api&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;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GhostContentAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;YOUR_API_URL&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;YOUR_CONTENT_API_KEY&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of hardcoding the &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; values, lets make it environment-agnostic. Create a &lt;code&gt;.env.local&lt;/code&gt; file at the root of your NextJS application, and inside add the two values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GHOST_API_URL=&amp;lt;YOUR_API_URL&amp;gt;
GHOST_CONTENT_API_KEY=&amp;lt;YOUR_CONTENT_API_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in our &lt;code&gt;ghost.js&lt;/code&gt; file we can update our config to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GhostContentAPI&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;@tryghost/content-api&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;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GhostContentAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GHOST_API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GHOST_CONTENT_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works out really well, because in development we can use our local instance of Ghost, but in production we can use the production instance. Just so long as you make sure to supply the correct values to these environment variables when deploying to production. But more on that later.&lt;/p&gt;

&lt;p&gt;Now that we are configured, lets right a function that fetches a list of all blog posts from Ghost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getAllPosts&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;posts&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&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="nx"&gt;posts&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;Easy peezy lemon squeezy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Render a list of blog posts
&lt;/h2&gt;

&lt;p&gt;Now that we have our function to fetch a list of posts, lets use it to render them on our homepage. In your &lt;code&gt;pages/index.js&lt;/code&gt; file, remove the bootstrapped code and replace with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAllPosts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&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;posts&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;getAllPosts&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;posts&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;posts&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&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;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;post&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&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="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&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;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation"&gt;&lt;code&gt;getStaticProps&lt;/code&gt; function&lt;/a&gt; is a special function in NextJS that lets us fetch data at build time. What this means is we can fetch our list of posts once, when we build our project, and generate a static html document that renders them.&lt;/p&gt;

&lt;p&gt;Using our &lt;code&gt;getAllPosts&lt;/code&gt; function from &lt;code&gt;lib/ghost.js&lt;/code&gt;, our posts will be fetched and supplied to the &lt;code&gt;Home&lt;/code&gt; page component as the &lt;code&gt;posts&lt;/code&gt; prop. Inside of our render tree we display the &lt;code&gt;title&lt;/code&gt; of each post as a &lt;a href="https://nextjs.org/docs/api-reference/next/link"&gt;NextJS &lt;code&gt;Link&lt;/code&gt;&lt;/a&gt;. For a list of all available values on a post coming from Ghost (hey that rhymes), see &lt;a href="https://ghost.org/docs/content-api/#posts"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating pages for each post
&lt;/h2&gt;

&lt;p&gt;We might have hundreds of blog posts to render on our site, and we can't possibly be burdened to predefine all of their routes. But, we know that every post has an associated &lt;code&gt;slug&lt;/code&gt; value. We can use this to our advantage.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;pages&lt;/code&gt; directory, lets create a new file called &lt;code&gt;[slug].js&lt;/code&gt;. These are known as &lt;a href="https://nextjs.org/docs/routing/dynamic-routes"&gt;dynamic routes in NextJS&lt;/a&gt;, and they are an awesome feature. Think of this page as a "catch all". The &lt;code&gt;slug&lt;/code&gt; will be used as the parameter that lets us dynamically render content based on its value.&lt;/p&gt;

&lt;p&gt;To do this, we'll need to utilize another special function in NextJS called &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation"&gt;&lt;code&gt;getStaticPaths&lt;/code&gt;&lt;/a&gt;. Here's how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAllPosts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticPaths&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;posts&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;getAllPosts&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;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;slug&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="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&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;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;&lt;code&gt;getStaticPaths&lt;/code&gt; is the mechanism by which we statically generate dynamic routes. We fetch all the blog posts from Ghost at build time, map through each and pull out their &lt;code&gt;slug&lt;/code&gt; value. The &lt;code&gt;slug&lt;/code&gt; will be given to each page as a unique parameter, and we can use it to fetch the content for the corresponding blog post.&lt;/p&gt;

&lt;p&gt;If this is confusing, consider the following example. If you had only one blog post in your Ghost instance, and that post had the slug &lt;code&gt;/my-happy-slug&lt;/code&gt;, then a page will be created at the route &lt;code&gt;yoursite.com/my-happy-slug&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering post content
&lt;/h2&gt;

&lt;p&gt;Now that we can generate a page for every post, we'll need to render some content. In order to accomplish this, we'll need to create a new function in our &lt;code&gt;lib/ghost.js&lt;/code&gt; file called &lt;code&gt;getPostBySlug&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getPostBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;post&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;formats&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;html&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function accepts the slug of a post and returns the post's data, making sure to include the post's content in &lt;code&gt;html&lt;/code&gt; format. If you're curious to see what a post object returned from Ghost looks like, you can refer to &lt;a href="https://ghost.org/docs/content-api/#posts"&gt;the documentation again&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's use this function in our &lt;code&gt;[slug].js&lt;/code&gt; page to fetch post content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getPostBySlug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticPaths&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;posts&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;getAllPosts&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;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;slug&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="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&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;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&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;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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;data&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;getPostBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="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;When each page is built, the &lt;code&gt;getStaticProps&lt;/code&gt; function is called. As an argument, &lt;code&gt;getStaticProps&lt;/code&gt; receives the page's &lt;code&gt;params&lt;/code&gt; object. Inside of this object we can gain access to the page's &lt;code&gt;slug&lt;/code&gt; value. Now, getting the page's data is trivial. We call &lt;code&gt;getPostBySlug&lt;/code&gt;, pass the &lt;code&gt;slug&lt;/code&gt; value, and the returned data representing our post can be passed to our page's component as the &lt;code&gt;data&lt;/code&gt; prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAllPosts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getPostBySlug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticPaths&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;posts&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;getAllPosts&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;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;slug&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="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&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;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&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;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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;data&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;getPostBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/article&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rendering post tags
&lt;/h2&gt;

&lt;p&gt;One feature that comes baked-in with most Ghost themes is this concept of tags. It is a way to categorize your content. &lt;code&gt;#Programming&lt;/code&gt;, &lt;code&gt;#Health&lt;/code&gt;, &lt;code&gt;#Bitcoin&lt;/code&gt;, etc. These are examples of tags that you can attach to each post you write. This may not be a feature you care to support, but some people might, so I'll include a section on how to achieve it.&lt;/p&gt;

&lt;p&gt;Attaching a tag to your post inside of the Ghost editor should be self-explanatory. When you query for a blog post, you can add an option to include that post's tags in the returned data. Let's update &lt;code&gt;getPostBySlug&lt;/code&gt; to do just that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getPostBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;formats&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;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tags&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;Now, every post will have access to its &lt;code&gt;tags&lt;/code&gt;. Render them however you'd like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tag&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tag&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;))}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/article&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a list of all available properties on the &lt;code&gt;tag&lt;/code&gt; object, &lt;a href="https://ghost.org/docs/content-api/#tags"&gt;see here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want your readers to be able to click these tags to see a list of all posts tagged with this specific tag, you'll need to do a bit more work. First, lets change the &lt;code&gt;span&lt;/code&gt; element above to a &lt;a href="https://nextjs.org/docs/api-reference/next/link"&gt;NextJS &lt;code&gt;Link&lt;/code&gt; component&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tag&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tag&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="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/tag/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&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="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&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;Next, we'll need to dynamically generate a page that will render at the route &lt;code&gt;/tag/[slug]&lt;/code&gt;. This is a similar process to how we generated dynamic pages for our blog posts.&lt;/p&gt;

&lt;p&gt;First let's create a function called &lt;code&gt;getAllTags&lt;/code&gt; inside of &lt;code&gt;lib/ghost.js&lt;/code&gt; that will fetch all available tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getAllTags&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;tags&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&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="nx"&gt;tags&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;Next, In your &lt;code&gt;pages&lt;/code&gt; directory, create a subdirectory called &lt;code&gt;tag&lt;/code&gt;, and inside of it create a file called &lt;code&gt;[slug].js&lt;/code&gt;. This page is going to be responsible for rendering a list of posts that are tagged with a specific tag name.&lt;/p&gt;

&lt;p&gt;For example, if you had 4 posts tagged &lt;code&gt;#React&lt;/code&gt; in Ghost, then a route called &lt;code&gt;yoursite.com/tag/react&lt;/code&gt; will be created, and the page will render a list of those 4 posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAllTags&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticPaths&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;tags&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;getAllTags&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;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;slug&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="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&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;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;The above code will handle dynamic generation of each tag's page, but we still need to render some content. Let's create a new function inside of &lt;code&gt;lib/ghost.js&lt;/code&gt; file called &lt;code&gt;getAllPostsByTagSlug&lt;/code&gt; that will help us with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getAllPostsByTagSlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;posts&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`tag:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;posts&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 function will return all the posts that are tagged with a tag represented by its corresponding &lt;code&gt;slug&lt;/code&gt;. Let's use this in our &lt;code&gt;pages/tag/[slug].js&lt;/code&gt; 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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAllTags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getAllPostsByTagSlug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticPaths&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;tags&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;getAllTags&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;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;slug&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="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&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;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&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;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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;posts&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;getAllPostsByTagSlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;posts&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;Let's step through what's happening here. For every tag we have in our Ghost content, we are going to generate a corresponding page. Each page will fetch a list of all posts that are tagged with that specific tag. So the tag &lt;code&gt;#Programming&lt;/code&gt; will have its own page at &lt;code&gt;yoursite.com/tag/programming&lt;/code&gt;, and you will be able to see a list of all your posts that are tagged with &lt;code&gt;#Programming&lt;/code&gt;. Pretty sweet!&lt;/p&gt;

&lt;p&gt;The one thing we haven't addressed is that, even though you can see all the posts for a particular tag, you don't actually no what tag the page is representing unless you look at the url. Let's add one more function called &lt;code&gt;getTagBySlug&lt;/code&gt; to our &lt;code&gt;lib/ghost.js&lt;/code&gt; 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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getTagBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;tag&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count.posts&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;
&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can call this function inside of &lt;code&gt;getStaticProps&lt;/code&gt; to fetch some meta information about the tag itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;getAllTags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getAllPostsByTagSlug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getTagBySlug&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticPaths&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;tags&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;getAllTags&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;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;slug&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="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&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;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&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;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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;posts&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;getAllPostsByTagSlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;tagData&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;getTagBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tagData&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;Now lets create the component that will render all of this information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tagData&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&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;tagData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tagData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&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;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* render posts here */&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the full file will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;getAllTags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getAllPostsByTagSlug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getTagBySlug&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticPaths&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;tags&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;getAllTags&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;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;slug&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="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&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;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&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;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&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;posts&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;getAllPostsByTagSlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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;tagData&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;getTagBySlug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tagData&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tagData&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&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;tagData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tagData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&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;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* render posts here */&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! If you followed closely you should have a simple NextJS-powered blog using Ghost as a headless CMS. I kept it lean and basic, so that you can build upon this over time. If you are interested in the full example, there is &lt;a href="https://github.com/jakewies/next-ghost-demo"&gt;a GitHub repository here&lt;/a&gt;. Feel free to clone, fork and go wild.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to production
&lt;/h2&gt;

&lt;p&gt;The last step in this journey is getting your site in production. This involves two separate applications: your CMS (Ghost), and your front-end (NextJS).&lt;/p&gt;

&lt;p&gt;Deploying NextJS to production is actually a breeze if you use &lt;a href="https://vercel.com/home"&gt;Vercel&lt;/a&gt;, the company behind the framework. They have a generous free tier, and all you have to do is hook up your GitHub repository and click a few buttons. It's painless really. They have great documentation, and setting up your environment variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;process.env.GHOST_API_URL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;process.env.GHOST_CONTENT_API_URL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;is a breeze.&lt;/p&gt;

&lt;p&gt;Deploying Ghost to production is a little trickier. You have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let Ghost take care of everything using Ghost(Pro)&lt;/li&gt;
&lt;li&gt;Self-host your Ghost instance&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ghost(Pro) has a starter package that starts at $9 a month, and although this isn't bad for what you get, they do not support custom themes. Unfortunately JAMStack falls into this category, as you need to create a custom integration to gain access to your &lt;code&gt;CONTENT_API_KEY&lt;/code&gt;. In order to get this you'd have to go with their creator option at $25 a month.&lt;/p&gt;

&lt;p&gt;Your other option is to self-host. &lt;a href="https://ghost.org/docs/hosting/"&gt;Ghost has some great documentation on how to do this&lt;/a&gt; using a variety of avenues. Your mileage may vary.&lt;/p&gt;

&lt;p&gt;Regardless of which choice you go with, you're going to want to make sure that you supply your NextJS app with the production &lt;code&gt;GHOST_API_URL&lt;/code&gt; and &lt;code&gt;GHOST_CONTENT_API_KEY&lt;/code&gt; values. These can be found in your production instance of Ghost once you deploy.&lt;/p&gt;

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

&lt;p&gt;I hope you found this article helpful. I've been experimenting with Ghost a lot in the last few weeks and I hope to continue to build out my site to support some of its awesome features. Next I'll be writing up a tactic for integrating your JAMStack with &lt;a href="https://ghost.org/docs/members/"&gt;Ghost's Members feature&lt;/a&gt;, so stay tuned for that! And as always, if you have questions feel free to reach out on &lt;a href="https://twitter.com/jakewies"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding ⚡️&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>ghost</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Subscribing Ghost Members From Your JAMStack Site</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Thu, 26 Aug 2021 14:05:17 +0000</pubDate>
      <link>https://dev.to/jakewies/subscribing-ghost-members-from-your-jamstack-site-40a7</link>
      <guid>https://dev.to/jakewies/subscribing-ghost-members-from-your-jamstack-site-40a7</guid>
      <description>&lt;p&gt;In a &lt;a href="https://jakewiesler.com/blog/ghost-nextjs-jamstack" rel="noopener noreferrer"&gt;previous post&lt;/a&gt; we walked through a basic blog setup using &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt; and &lt;a href="https://ghost.org/" rel="noopener noreferrer"&gt;Ghost&lt;/a&gt; as a headless CMS. In this post, we'll take things up a notch by adding support for &lt;a href="https://ghost.org/docs/members/" rel="noopener noreferrer"&gt;Ghost Memberships&lt;/a&gt;, a new feature in &lt;a href="https://ghost.org/changelog/4/" rel="noopener noreferrer"&gt;v4.0&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ghost Memberships
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Memberships&lt;/em&gt; is a native feature to Ghost. It is essentially an email list that readers of your site can subscribe to. They can choose to subscribe as free or paid members, and the Ghost Dashboard displays analytics like &lt;strong&gt;Total Members&lt;/strong&gt;, &lt;strong&gt;Email Open Rate&lt;/strong&gt; and &lt;strong&gt;Monthly Recurring Revenue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ghost's official themes support memberships out-of-the-box, meaning you get form inputs and subscribe buttons throughout. No extra work needed. And most custom themes purchasable through the &lt;a href="https://ghost.org/themes/" rel="noopener noreferrer"&gt;Themes Directory&lt;/a&gt; also support memberships.&lt;/p&gt;

&lt;p&gt;As a writer this means you can publish posts and choose to send them to any and all of your members with one click of a button. It's a powerful model for content creators who want to capture some of the value they create via monetization.&lt;/p&gt;

&lt;p&gt;However, if you use Ghost as a headless CMS, you're opting out of this native functionality and can only take advantage of it through Ghost's APIs. Specifically the &lt;a href="https://ghost.org/docs/admin-api/" rel="noopener noreferrer"&gt;Admin API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ghost's Admin API
&lt;/h2&gt;

&lt;p&gt;The Admin API exposes &lt;a href="https://ghost.org/docs/admin-api/#endpoints" rel="noopener noreferrer"&gt;a set of endpoints&lt;/a&gt; that let you do things you would normally do in the Ghost Dashboard. Things like creating, editing and deleting &lt;a href="https://ghost.org/docs/admin-api/#posts" rel="noopener noreferrer"&gt;posts&lt;/a&gt; and &lt;a href="https://ghost.org/docs/admin-api/#pages" rel="noopener noreferrer"&gt;static pages&lt;/a&gt;, uploading &lt;a href="https://ghost.org/docs/admin-api/#images" rel="noopener noreferrer"&gt;images&lt;/a&gt; and &lt;a href="https://ghost.org/docs/admin-api/#themes" rel="noopener noreferrer"&gt;themes&lt;/a&gt;, or manipulating the &lt;a href="https://ghost.org/docs/admin-api/#site" rel="noopener noreferrer"&gt;site object&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also perform basic &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete" rel="noopener noreferrer"&gt;CRUD&lt;/a&gt; operations on your list of members.&lt;/p&gt;

&lt;p&gt;In this post we are &lt;em&gt;only&lt;/em&gt; concerned with the creation of a member, i.e &lt;em&gt;subscribing&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the client
&lt;/h3&gt;

&lt;p&gt;At the time of writing this post, interacting with member data via the Admin API is an undocumented feature. I was not able to find any reference to it within &lt;a href="https://ghost.org/docs" rel="noopener noreferrer"&gt;Ghost's developer documentation&lt;/a&gt;, and the only reason I know about this is through hours of poking around a number of &lt;a href="https://github.com/TryGhost" rel="noopener noreferrer"&gt;Ghost's GitHub repositories&lt;/a&gt;, specifically &lt;a href="https://github.com/TryGhost/Admin" rel="noopener noreferrer"&gt;this one&lt;/a&gt;, &lt;a href="https://github.com/TryGhost/Ghost" rel="noopener noreferrer"&gt;this one&lt;/a&gt; and &lt;a href="https://github.com/TryGhost/Members" rel="noopener noreferrer"&gt;this one&lt;/a&gt;. Oh and &lt;a href="https://github.com/TryGhost/SDK/tree/main/packages/admin-api" rel="noopener noreferrer"&gt;this one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I will keep my eye out for any changes on Ghost's end and update this post when applicable.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Even though it is undocumented, Ghost still considers &lt;a href="https://github.com/TryGhost/SDK/blob/dc77bf8dcff320117a2c3d709494ac6cac035b2f/packages/admin-api/lib/index.js#L73" rel="noopener noreferrer"&gt;the members endpoints stable&lt;/a&gt;, so we shouldn't have much to worry about. The only obstacle is figuring out how to use it.&lt;/p&gt;

&lt;p&gt;First, we need to install the &lt;a href="https://ghost.org/docs/admin-api/javascript/" rel="noopener noreferrer"&gt;Admin API client&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @tryghost/admin-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or with &lt;code&gt;npm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @tryghost/admin-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring the client
&lt;/h3&gt;

&lt;p&gt;Configuring this client is the same as configuring &lt;a href="https://jakewiesler.com/blog/ghost-nextjs-jamstack#querying-for-blog-posts" rel="noopener noreferrer"&gt;the Content API client from the previous post&lt;/a&gt;. If you are confused how we got here, I recommend reading that one first.&lt;/p&gt;

&lt;p&gt;Let's add on to our &lt;code&gt;lib/ghost.js&lt;/code&gt; file by importing the Admin API client and initiating it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GhostAdminAPI&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;@tryghost/admin-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GhostAdminAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&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="na"&gt;key&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="na"&gt;version&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with the Content API, the Admin API requires a &lt;code&gt;url&lt;/code&gt; and a &lt;code&gt;key&lt;/code&gt; value. They can be found in the &lt;strong&gt;Integrations&lt;/strong&gt; section of your Ghost Dashboard as well. In fact, the &lt;code&gt;url&lt;/code&gt; value will be the same for the Content API client and the Admin API client. The only difference is the &lt;code&gt;key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To find them, navigate to your locally hosted Ghost instance, which is usually deployed at &lt;code&gt;http://localhost:2368/ghost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you do not have Ghost running locally, see &lt;a href="https://jakewiesler.com/blog/ghost-nextjs-jamstack#installing-ghost" rel="noopener noreferrer"&gt;the previous&lt;br&gt;
  post&lt;/a&gt; for more details.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once there, navigate to the &lt;strong&gt;Integrations&lt;/strong&gt; section on the left sidebar. If you have yet to create a custom integration for Ghost, you will need to do so by clicking "Add Custom Integration". You can name the integration whatever you want.&lt;/p&gt;

&lt;p&gt;If you are continuing on with the previous post, your custom integration should already exist, "NextJS Front-end".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz138l5swukdjjw3oncwf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz138l5swukdjjw3oncwf.png" alt="integration"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Once you've confirmed that your custom integration exists, open it to see more information. This is where you will find your &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; values. The value of &lt;code&gt;url&lt;/code&gt; will correspond to the &lt;code&gt;API URL&lt;/code&gt; value, and the value of &lt;code&gt;key&lt;/code&gt; will correspond to &lt;code&gt;ADMIN API key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Copy these values into a &lt;code&gt;.env.local&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GHOST_API_URL=&amp;lt;YOUR_API_URL&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;GHOST_ADMIN_API_KEY=&amp;lt;YOUR_ADMIN_API_KEY&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If you are following along from &lt;a href="https://jakewiesler.com/blog/ghost-nextjs-jamstack" rel="noopener noreferrer"&gt;the previous&lt;br&gt;
  post&lt;/a&gt; in this series, you may already have the &lt;code&gt;GHOST_API_URL&lt;/code&gt; in your &lt;code&gt;.env.local&lt;/code&gt; file.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now that we have that sorted, lets update &lt;code&gt;lib/ghost.js&lt;/code&gt; with these values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GhostAdminAPI&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;@tryghost/admin-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GhostAdminAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GHOST_API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GHOST_ADMIN_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also updated the &lt;code&gt;version&lt;/code&gt; by using &lt;code&gt;"v3"&lt;/code&gt;. This is the value Ghost currently uses in &lt;a href="https://ghost.org/docs/admin-api/javascript/" rel="noopener noreferrer"&gt;their documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's important to note that the &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; values will be different in your production instance of Ghost. Since we are injecting these values as environment variables, it should be trivial to update them for production. Wherever you deploy your JAMStack site, just make sure to include the production values of &lt;code&gt;GHOST_API_URL&lt;/code&gt; and &lt;code&gt;GHOST_ADMIN_API_KEY&lt;/code&gt; in your environment variable configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribing members
&lt;/h2&gt;

&lt;p&gt;Now that we have the client setup, we'll need to write a function that adds a member to our list. This was the tricky part about the Admin API. There is no documentation on how to do so. After some digging around, I came up with this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GhostAdminAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;addMember&lt;/code&gt; function above accepts an &lt;code&gt;email&lt;/code&gt; and a &lt;code&gt;name&lt;/code&gt;. Only &lt;code&gt;email&lt;/code&gt; is required by the API, though the &lt;code&gt;name&lt;/code&gt; will also be set if you include it.&lt;/p&gt;

&lt;p&gt;The form to capture these values can be built in any way you choose. I won't include that in this post. Just make sure that when the form submits, you call &lt;code&gt;addMember&lt;/code&gt; and pass the right values.&lt;/p&gt;

&lt;p&gt;In development, you should be able to call this function and see the newly added member show up in the &lt;strong&gt;Members&lt;/strong&gt; section of your local instance of Ghost. This is how you know it is working properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending a confirmation email
&lt;/h2&gt;

&lt;p&gt;One of the great things about Ghost's native Members feature is that a reader will receive a special email when they subscribe for the first time. It is a feature that I want to support on my site, but again, there's no documentation on how to do this.&lt;/p&gt;

&lt;p&gt;After some more digging I was able to find a few options that can be passed along with the &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt; that make this possible:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GhostAdminAPI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;email&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;send_email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subscribe&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These options are not required, but &lt;a href="https://github.com/TryGhost/Ghost/blob/19fd16649a5a04cd19ccf802e1ad8f79db26d7f8/core/server/api/canary/members.js#L96" rel="noopener noreferrer"&gt;the Members API will look for them&lt;/a&gt;, and if they are passed, a special email will be sent to your member. Pretty neat!&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding a bug in Ghost's email logic
&lt;/h3&gt;

&lt;p&gt;During this process of discovering how to send subscription emails to new members, I realized that the content of the email being sent had nothing to do with confirming a subscription. Instead it contained a magic link to "Sign In" to my website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0zwfplurn5d6bwurlhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0zwfplurn5d6bwurlhr.png" alt="email"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The image above is a snapshot of the output from the command &lt;code&gt;ghost log&lt;/code&gt;, which can be run in development to inspect the logs of your running Ghost instance.&lt;/p&gt;

&lt;p&gt;Why would this be?&lt;/p&gt;

&lt;p&gt;If you use an official Ghost theme like &lt;a href="https://demo.ghost.io/" rel="noopener noreferrer"&gt;Casper&lt;/a&gt;, your members can sign in to view paywalled content, and this email enables that. But I'm not requesting that my members sign in, I'm requesting that they confirm their subscription.&lt;/p&gt;

&lt;p&gt;If we take a step back to the options passed to the &lt;code&gt;addMember&lt;/code&gt; function, you'll notice there is an &lt;code&gt;email_type&lt;/code&gt; option being used. This option &lt;a href="https://github.com/TryGhost/Ghost/blob/19fd16649a5a04cd19ccf802e1ad8f79db26d7f8/core/server/api/canary/members.js#L106" rel="noopener noreferrer"&gt;can have one of three values&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;'signin'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'signup'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'subscribe'&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The value of this &lt;code&gt;email_type&lt;/code&gt; is used to select &lt;a href="https://github.com/TryGhost/Ghost/tree/main/core/server/services/members/emails" rel="noopener noreferrer"&gt;one of three different email templates&lt;/a&gt; to be sent to the member. Since we are using &lt;code&gt;'subscribe'&lt;/code&gt;, you would think that the &lt;a href="https://github.com/TryGhost/Ghost/blob/main/core/server/services/members/emails/subscribe.js" rel="noopener noreferrer"&gt;&lt;code&gt;subscribe&lt;/code&gt; template&lt;/a&gt; would be sent, but that's not the case.&lt;/p&gt;

&lt;p&gt;I did some debugging through the various repositories in Ghost's GitHub organization, and have found what I believe to be a bug in the way that email templates are selected when adding a member from the &lt;code&gt;@tryghost/admin-api&lt;/code&gt; client.&lt;/p&gt;

&lt;p&gt;If you're curious to know more, I've laid out my thesis in &lt;a href="https://github.com/TryGhost/Ghost/issues/13222" rel="noopener noreferrer"&gt;a new issue in the &lt;code&gt;TryGhost/Ghost&lt;/code&gt; repository on GitHub&lt;/a&gt;. I'll make sure to keep this post updated if the issue gets resolved.&lt;/p&gt;

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

&lt;p&gt;So there you have it. It's not a perfect solution by any means, but if you are using Ghost as a headless CMS, this is currently the way to add members to your Ghost dashboard.&lt;/p&gt;

&lt;p&gt;Hopefully, with continued growth in the JAMStack ecosystem, some of these issues will get ironed out over time. That's the beauty of open source!&lt;/p&gt;

&lt;p&gt;Feel free to reach out to me &lt;a href="https://twitter.com/jakewies" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt; if you have any further questions regarding this post.&lt;/p&gt;

&lt;p&gt;Happy coding ⚡️&lt;/p&gt;

</description>
      <category>ghost</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>I'm Leaving VSCode for Vim</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Wed, 10 Mar 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/jakewies/i-m-leaving-vscode-for-vim-4a2e</link>
      <guid>https://dev.to/jakewies/i-m-leaving-vscode-for-vim-4a2e</guid>
      <description>&lt;p&gt;I started programming in 2014. At that time, &lt;a href="https://code.visualstudio.com/"&gt;VSCode&lt;/a&gt; didn’t exist. At that time, I didn’t know about &lt;a href="https://www.vim.org/"&gt;Vim&lt;/a&gt; either. I knew of two text editors: &lt;a href="https://atom.io/"&gt;Atom&lt;/a&gt; and &lt;a href="https://www.sublimetext.com/"&gt;Sublime Text&lt;/a&gt;. I tried Sublime first. Remember that popup you would get when you didn’t pay for the license? 😅 Good times.&lt;/p&gt;

&lt;p&gt;I switched to Atom shortly thereafter. Atom was marketed as a &lt;em&gt;hackable text editor&lt;/em&gt;. This appealed to me. It was my first exposure to configuring a development environment. The colors, the keybindings, the plugins, etc.&lt;/p&gt;

&lt;p&gt;In late 2015 I started seeing a ton of buzz around a new text editor named VSCode. I knew of &lt;a href="https://visualstudio.microsoft.com/"&gt;Visual Studio&lt;/a&gt;, the IDE. But this was an entirely new product from Microsoft. After a while it was hard to ignore. I adopted VSCode in mid-2016, and it's been my text editor ever since. But after 5 years I've decided to make the switch to Vim full time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The case for VSCode
&lt;/h2&gt;

&lt;p&gt;Before I go down the rabbit hole that is Vim, let me first address VSCode. It’s an incredible piece of software. And to be honest, there’s no rational reason for me to stop using it. It does everything I need it to do.&lt;/p&gt;

&lt;p&gt;I have it configured exactly how I want, and it’s not a headache to do so. The plugin ecosystem is massive. There are hundreds, maybe thousands, of themes. It has an integrated terminal. For TypeScript, it’s an absolute dream. You can alter VSCode to be anything you want it to be, but most important:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It just gets the job done.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All of these aspects of the product make it a joy to use, and it’s probably why VSCode has taken off and left everything else in the dust. Here’s a graphic from the State of JS Survey in 2020.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MSMwsUbB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lx2demna17ecodlhnabf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MSMwsUbB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lx2demna17ecodlhnabf.png" alt="State of JS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;VSCode is good&lt;/em&gt;. There’s a reason it’s so popular. And let me tell you, it’s a hard drug to quit. So why the hell would I leave?&lt;/p&gt;

&lt;h2&gt;
  
  
  Vim's alluring nature
&lt;/h2&gt;

&lt;p&gt;I used Vim early in my development career for a single purpose: Git commits. Vim opens up, I write my message, and I’m out. It was the epitome of scratching the surface.&lt;/p&gt;

&lt;p&gt;This went on for years. It wasn’t until I saw &lt;a href="https://www.youtube.com/channel/UC8ENHE5xdFSwx71u3fDH5Xw"&gt;The Primeagen&lt;/a&gt; on Twitch about a year ago that I gave the text editor a thought. If you’ve never seen this dude in action, you have to. Even if you have no desire to use Vim (in which case I’m not sure why you’ve made it this far), you must. He showed me that there are levels to text editing, and I was clearly on level 0.&lt;/p&gt;

&lt;p&gt;Copying and pasting with my mouse. Moving through files with my mouse. Searching for files in the file tree. Looking up that keymap I still hadn’t memorized. This is how I did things, because it’s how I always did them. I was so busy trying to learn how to code that I never had time to consider improving my process. I never considered &lt;em&gt;efficiency&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And don’t get me wrong, I don’t think there’s anything wrong with this. Thousands of people work this way every day and they get the job done. But I’m the type of person that’s always looking for an edge, and I think Vim can be an edge.&lt;/p&gt;

&lt;p&gt;There’s a common phrase, &lt;a href="https://www.amazon.com/Practical-Vim-Thought-Pragmatic-Programmers/dp/1934356980"&gt;even a book title&lt;/a&gt;, that’s used to describe Vim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Precision editing at the speed of thought.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Primeagen is the ultimate example of this. He has a level of command over his text editor that I’ve never seen, and that is a concept that really strikes home with me. If you use a tool every day, it makes sense to know it inside and out.&lt;/p&gt;

&lt;p&gt;I think it’s possible to achieve this level of efficiency with VSCode as well. As I said earlier, there’s no rational reason to make the switch. But Vim has lured me into its cave of wonder, and I might be too far in to turn back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The transition process
&lt;/h2&gt;

&lt;p&gt;The first thing I did was add the &lt;a href="https://marketplace.visualstudio.com/items?itemName=vscodevim.vim"&gt;Vim plugin to VSCode&lt;/a&gt;, giving me an opportunity to learn the basic movements in a familiar place. The plan was to get these basics down and then start migrating over to Vim, but life got in the way.&lt;/p&gt;

&lt;p&gt;Weeks turned into months, and I kept pushing off the day where I’d take the leap. There are certain parts of my day job that require a high level of output, and switching up my development workflow was a chore I just didn’t want to put up with.&lt;/p&gt;

&lt;p&gt;Even so, I was developing a level of proficiency with Vim movements that made it easier to consider the switch. I wasn’t thinking about what keys to press anymore. Instead I just pressed them. My muscle memory was improving. I started layering on advanced movements, and I was developing a preference for certain actions.&lt;/p&gt;

&lt;p&gt;Let me tell you, editing text in this way is a whole lot of fun. You really feel like a wizard once you get the hang of it. I was spending less time reaching for the mouse. Any time I did, I would ask myself if what I’m trying to do can be solved with a keystroke. Almost always the answer is yes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's going
&lt;/h2&gt;

&lt;p&gt;A few weeks ago I decided to bite the bullet and go all in. I feel really comfortable with the tool itself, I just need a proper development environment. The goal now is to make Vim feel cozy.&lt;/p&gt;

&lt;p&gt;VSCode abstracts away much of the heavy lifting that comes with a good development environment. Things like language support, formatting, search, diagnostics, etc. Vim doesn’t support these out-of-the-box, and they are paramount to my workflow. Configuring them is the biggest obstacle to entry, and I definitely underestimated how much work it would take.&lt;/p&gt;

&lt;p&gt;So far, I've managed to get a pretty decent setup. I'm using &lt;a href="https://neovim.io/"&gt;Neovim&lt;/a&gt; instead of vanilla Vim, which has &lt;a href="https://www.reddit.com/r/neovim/"&gt;a vibrant community&lt;/a&gt; and &lt;a href="https://github.com/neovim/neovim"&gt;an active development team&lt;/a&gt;. I have a good base of plugins, and &lt;a href="https://github.com/jakewies/.dotfiles/tree/main/nvim/.config/nvim"&gt;my dotfiles&lt;/a&gt; are starting to look like proper dotfiles.&lt;/p&gt;

&lt;p&gt;The title of this post specifically mentions Vim, but what I've really fallen in love with is Neovim. I believe I've stumbled upon a really great tool that is actively being developed, and that is a dream for someone like me who plans to use it long term.&lt;/p&gt;

&lt;p&gt;There is still a lot to learn, and over the next few weeks I'll be sharing how I'm going about the process. If you'd like to learn more about my transition to Vim, and get access to the content I'll be putting out, &lt;a href="https://www.jakewiesler.com/newsletter"&gt;you can subscribe to my newsletter&lt;/a&gt; or &lt;a href="https://dev.to/jakewies"&gt;follow me on dev.to&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Any questions? Just want to say hi? &lt;a href="https://twitter.com/jakewies"&gt;Reach out to me on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy building!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>programming</category>
      <category>productivity</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>What are your favorite JAMStack CMSs?</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Mon, 10 Aug 2020 20:02:24 +0000</pubDate>
      <link>https://dev.to/jakewies/what-are-your-favorite-jamstack-cmss-2ppg</link>
      <guid>https://dev.to/jakewies/what-are-your-favorite-jamstack-cmss-2ppg</guid>
      <description>&lt;p&gt;I'm working on a small project for a client that requires a basic blog setup. Nothing too fancy. I'm planning to build the site with NextJS, and am looking for a way to make content creation easy for the client.&lt;/p&gt;

&lt;p&gt;So far I've explored Sanity.io and Contentful. Initial impressions have been interesting. Curious to hear everyone's thoughts.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>How often are you increasing font size on blog posts?</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Sat, 08 Aug 2020 23:02:43 +0000</pubDate>
      <link>https://dev.to/jakewies/how-often-are-you-increasing-text-size-on-blog-posts-3dpk</link>
      <guid>https://dev.to/jakewies/how-often-are-you-increasing-text-size-on-blog-posts-3dpk</guid>
      <description>&lt;p&gt;I have been doing this a lot lately. I'm not sure if it's just my age showing, or my shitty eye sight (boy I hope not), or the fact that my monitors are higher rez, but I really enjoy reading with large type that fills the entire screen.&lt;/p&gt;

&lt;p&gt;Hell, I even do this with my own site and it makes me want to go and refactor my font size setup.&lt;/p&gt;

&lt;p&gt;Anyone else find themselves doing the same?&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>webdev</category>
      <category>design</category>
    </item>
    <item>
      <title>Deep Work and Music - What are some of your favorite artists / songs / playlists when working?</title>
      <dc:creator>Jake Wiesler</dc:creator>
      <pubDate>Sat, 08 Aug 2020 22:46:39 +0000</pubDate>
      <link>https://dev.to/jakewies/deep-work-and-music-what-are-some-of-your-favorite-artists-songs-playlists-when-working-2kbb</link>
      <guid>https://dev.to/jakewies/deep-work-and-music-what-are-some-of-your-favorite-artists-songs-playlists-when-working-2kbb</guid>
      <description>&lt;p&gt;Would love to get more variety in what I'm listening to. I struggle with any audio that has lyrics, so most of what I listen to falls into the categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lofi&lt;/li&gt;
&lt;li&gt;classical&lt;/li&gt;
&lt;li&gt;instrumental&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I do enjoy some upbeat techno when I'm in a groove. Usually when I'm trying to figure out a difficult problem I opt for silence first.&lt;/p&gt;

&lt;p&gt;Curious to hear how everyone else approaches audio when working?&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
