<?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: Simon</title>
    <description>The latest articles on DEV Community by Simon (@simondosda).</description>
    <link>https://dev.to/simondosda</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%2F620656%2F515d8ab8-b9fe-4ff0-b4a4-cff78eec6d53.jpeg</url>
      <title>DEV Community: Simon</title>
      <link>https://dev.to/simondosda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/simondosda"/>
    <language>en</language>
    <item>
      <title>I messed up my git history: retrieving your lost commits</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Sun, 23 Jan 2022 13:39:45 +0000</pubDate>
      <link>https://dev.to/simondosda/i-messed-up-my-git-history-retrieving-your-lost-commits-3c0f</link>
      <guid>https://dev.to/simondosda/i-messed-up-my-git-history-retrieving-your-lost-commits-3c0f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@elisa_ventur"&gt;Elisa Ventur&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When using git commands that rewrite a branch's history, like &lt;code&gt;rebase&lt;/code&gt; or &lt;code&gt;reset&lt;/code&gt;, you might end up losing some commits by mistake. &lt;/p&gt;

&lt;p&gt;But don't worry, as long as you committed your changes once, you should be able to get them back!&lt;/p&gt;

&lt;p&gt;Indeed, a magical git command can help us: &lt;a href="https://git-scm.com/docs/git-reflog"&gt;git reflog&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Git keeps a trace of all your commit in reference logs: this is, for instance, what allows you to use commands like &lt;code&gt;git checkout my-branch@{two.week.ago}&lt;/code&gt; to checkout on your branch as it was two weeks ago.&lt;/p&gt;

&lt;p&gt;So if you just messed up with your branch, you can do something like &lt;code&gt;git checkout my-branch@{ten.minute.ago}&lt;/code&gt;, otherwise, it can be more accurate to look directly at the reference logs entries with the &lt;code&gt;git reflog&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the reference logs
&lt;/h2&gt;

&lt;p&gt;The format of the reference logs is as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;commit hash&lt;/code&gt; (&lt;code&gt;branch name&lt;/code&gt;) &lt;code&gt;handle&lt;/code&gt;: &lt;code&gt;action&lt;/code&gt;: &lt;code&gt;action description&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example of a git reflog output:&lt;br&gt;
can be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0e60901 &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; feature-branch, main&lt;span class="o"&gt;)&lt;/span&gt; HEAD@&lt;span class="o"&gt;{&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;: reset: moving to main
2b38384 HEAD@&lt;span class="o"&gt;{&lt;/span&gt;1&lt;span class="o"&gt;}&lt;/span&gt;: commit: feat: new feature
0e60901 &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; feature-branch, main&lt;span class="o"&gt;)&lt;/span&gt; HEAD@&lt;span class="o"&gt;{&lt;/span&gt;2&lt;span class="o"&gt;}&lt;/span&gt;: checkout: moving from main to feature-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here for instance we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HEAD@{2} - creation of a new branch named &lt;code&gt;feature-branch&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;HEAD@{1} - add a commit with the message &lt;code&gt;feat: new feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;HEAD@{0} - reset on branch &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I have reset my branch on &lt;code&gt;main&lt;/code&gt;, I used the &lt;code&gt;--hard&lt;/code&gt; option, so currently the changes from my commit &lt;code&gt;feat: new feature&lt;/code&gt; are lost.&lt;/p&gt;

&lt;p&gt;But thanks to the &lt;code&gt;reflog,&lt;/code&gt; I can checkout on them or directly reset my head on it 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;git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; HEAD@&lt;span class="o"&gt;{&lt;/span&gt;1&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# or with the commit hash: git reset --hard 2b38384&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we go, I got back my lost commit!&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Git: how can I force pull a distant repository</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Fri, 21 Jan 2022 06:48:02 +0000</pubDate>
      <link>https://dev.to/simondosda/git-how-can-i-force-pull-a-distant-repository-17he</link>
      <guid>https://dev.to/simondosda/git-how-can-i-force-pull-a-distant-repository-17he</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@vidarnm"&gt;Vidar Nordli-Mathisen&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes you want to set the state of your local branch to its remote version but cannot do so using the &lt;code&gt;git pull&lt;/code&gt; command because your local version and the remote one have diverged.&lt;/p&gt;

&lt;p&gt;This can happen for several reasons. &lt;br&gt;
For me, the two main ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have checked out on a colleague branch for a review, he has made some changes on it that have changed the branch history, and I want to pull this new version&lt;/li&gt;
&lt;li&gt;I have messed up my history during a rebase with conflicts and want to start again from the remote version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these cases, using &lt;code&gt;git pull&lt;/code&gt; will try to merge or rebase the remote branch into your local one, which is not the behavior we are looking for.&lt;/p&gt;

&lt;p&gt;What we want instead is to start over with the distant branch. Of course, we could naively do so by deleting our local branch before pulling the distant one, but that is a bit cumbersome if you have to do so regularly.&lt;/p&gt;

&lt;p&gt;Thankfully, git provides a more handy way to manage this scenario with the &lt;code&gt;reset --hard&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; origin/my-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git reset&lt;/code&gt; is a command that took me some time to understand but is worth knowing.&lt;br&gt;
If you are interested to learn more about it, I have &lt;a href="https://simondosda.github.io/posts/2022-01-04-git-reset.html"&gt;written an article about its possibilities&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basically, the &lt;code&gt;reset&lt;/code&gt; command set our head to the targeted commit, here to the last commit on &lt;code&gt;origin/my-branch&lt;/code&gt;. &lt;br&gt;
Using it with the &lt;code&gt;--hard&lt;/code&gt; option allows to discard all local changes, committed or not, and therefore force the current state to the distant one.&lt;/p&gt;

&lt;p&gt;If you find yourself using it often, I would advise you &lt;a href="https://simondosda.github.io/posts/2021-04-13-git-aliases.html"&gt;to set an alias for it&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;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.rh &lt;span class="s1"&gt;'!git fetch &amp;amp;&amp;amp; git reset --hard origin/$(git rev-parse --abbrev-ref HEAD)'&lt;/span&gt;
git rh  &lt;span class="c"&gt;# rebase hard on your local repo after having fetched its last state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last thing. When modifying your history, you might end up losing commits that you wanted to keep. Fortunately, &lt;a href="https://simondosda.github.io/posts/2022-01-06-git-lost-commits.html"&gt;there is a way to retrieve these commits&lt;/a&gt; that are no longer in your branch.&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Git reset, when and how to use it</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Wed, 19 Jan 2022 12:04:03 +0000</pubDate>
      <link>https://dev.to/simondosda/git-reset-when-and-how-to-use-it-4na3</link>
      <guid>https://dev.to/simondosda/git-reset-when-and-how-to-use-it-4na3</guid>
      <description>&lt;p&gt;&lt;a href="https://git-scm.com/docs/git-reset"&gt;Git reset&lt;/a&gt; is part of the git commands that used to confuse me, as I did not understand its purpose or use-cases at first.&lt;/p&gt;

&lt;p&gt;On top of that, it seemed to be doing very different things depending on its input, which is either a commit or a path.&lt;/p&gt;

&lt;p&gt;But once I dug a bit deeper into each option of this command and understood the logic behind it, which eventually is about &lt;strong&gt;undoing staged or committed changes&lt;/strong&gt;, it really helped me to manage my commit history or revert my mistakes, which is particularly useful for &lt;a href="https://simondosda.github.io/posts/2022-01-03-git-rebase-workflow.html"&gt;my rebase workflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With this article, I hope to help you clarify how this command works and how you can use it in your daily development tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git reset with a path: unstaging files
&lt;/h2&gt;

&lt;p&gt;When used with a path, the reset command does the opposite of git add: it removes changes from the staging area.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6s8QWEy1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imxeq2tlscsx2wbpe4mf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6s8QWEy1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imxeq2tlscsx2wbpe4mf.png" alt="git reset with a file" width="470" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NB: a new command was recently added to git, &lt;code&gt;git restore&lt;/code&gt;, to fulfill this purpose and will probably become the advised way to perform this action.&lt;br&gt;
For now, &lt;code&gt;git restore&lt;/code&gt; is still experimental. Still, I think it should be preferred over &lt;code&gt;git reset&lt;/code&gt; for unstaging changes, as its implementation seems more intuitive, whereas &lt;code&gt;git reset&lt;/code&gt; should be used to update the branch's head, as we will see after.&lt;/p&gt;

&lt;p&gt;But if you want to use the reset command for unstaging files, here are some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="c"&gt;# unstage all changes&lt;/span&gt;
git reset file-a file-b &lt;span class="c"&gt;# unstage file-a and file-b files&lt;/span&gt;
git reset folder-a/ &lt;span class="c"&gt;# unstage all changes in the folder folder-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I personally do not use &lt;code&gt;git reset&lt;/code&gt; for this purpose as I don't deal much with staging my changes. I usually commit everything (I have &lt;a href="https://simondosda.github.io/posts/2021-04-13-git-aliases.html"&gt;set an alias&lt;/a&gt; for this purpose that I am always using), and if I need to commit partial changes, I prefer to use my IDE to do so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git reset with a commit: modifying the head of your branch
&lt;/h2&gt;

&lt;p&gt;When used with a commit, &lt;code&gt;git reset&lt;/code&gt; allows us to change the head of our branch. While doing so, it allows us to extract the changes between our current head and the new one, and put them in our staging area, working directory, or forget them depending on the mode used.&lt;/p&gt;

&lt;p&gt;Let's see how the different modes work.&lt;/p&gt;

&lt;h3&gt;
  
  
  The soft mode 🪶
&lt;/h3&gt;

&lt;p&gt;The soft mode keeps your current staging area and working tree as it is while moving your head to the specified commit. Changes between your previous head and the new one are added to the staging area.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SC8R9-NC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ph7tc9wpatrrbq54971n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SC8R9-NC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ph7tc9wpatrrbq54971n.png" alt="git reset in soft mode" width="880" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This option provides a great way to rewrite your history in only one commit, instead of using an interactive &lt;code&gt;rebase&lt;/code&gt; to squash your commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; main
git commit &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="s1"&gt;' feat: an atomic feature'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The mixed mode 🥗
&lt;/h3&gt;

&lt;p&gt;Using git reset with the mixed mode (the default mode) puts all your changes between your previous and current head, including your current staged changes, in the working directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r6mgguNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ucr7hpnf9n84ve0u9qpl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r6mgguNE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ucr7hpnf9n84ve0u9qpl.png" alt="git reset in mixed mode" width="880" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This option is handy when you want to rewrite your history in several commits, for instance to create two commits from all the changes you made from your main branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="nt"&gt;--mixed&lt;/span&gt; main
git commit &lt;span class="nt"&gt;--interactive&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="s1"&gt;' fix: a correction aside the main feature'&lt;/span&gt;
git commit &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="s1"&gt;' feat: the main feature'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The hard mode 🪨
&lt;/h3&gt;

&lt;p&gt;The heavy-handed one! &lt;br&gt;
It sets the index to the given commit and removes all changes between your previous and current index or in your working directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qMvqdvPo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtqurytrebyn68po5c0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qMvqdvPo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtqurytrebyn68po5c0s.png" alt="git reset in hard mode" width="880" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This option is perfect if you want to get rid of your current changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; HEAD &lt;span class="c"&gt;# or just git reset --hard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NB: use this command only if you know you are not interested in keeping your current changes, as you will not be able to recover them. &lt;br&gt;
If you are unsure yet but want to start back from your last commit, use &lt;code&gt;git stash&lt;/code&gt; instead to put your current changes aside.&lt;/p&gt;

&lt;p&gt;Another interesting use-case is to reset your branch to the state of the remote one (&lt;a href="https://simondosda.github.io/posts/2022-01-05-git-force-pull.html"&gt;a kind of force pull&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;git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; origin/my-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to be cautious when using this command as you might lose changes. &lt;br&gt;
If you made a mistake, it is &lt;a href="https://simondosda.github.io/posts/2022-01-06-git-lost-commits.html"&gt;still possible to get your lost commits back&lt;/a&gt;, but not your current staged and unstaged changes. &lt;/p&gt;

&lt;h3&gt;
  
  
  The keep mode ✊
&lt;/h3&gt;

&lt;p&gt;This mode is a bit peculiar. It is similar to the hard mode as it will drop the commits between your previous and new head. But it will carry on your unstaged changes and abort if there is any conflict between these changes and the removed commits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Krs5GW5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xznu6wt03hgw7dbv5v9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Krs5GW5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xznu6wt03hgw7dbv5v9l.png" alt="git reset in keep mode" width="880" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command is dedicated to doing one thing: remove commits while being safe that they won't conflict with our current unstaged changes. I don't really find myself in this kind of situation, but if that happens, I guess it is valuable to know this option.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about the merge mode?
&lt;/h3&gt;

&lt;p&gt;As explained in the documentation, &lt;code&gt;reset --merge&lt;/code&gt; is meant to be used when resetting out of a conflicted merge.&lt;br&gt;
This is a particular situation, and as I don't use &lt;code&gt;merge&lt;/code&gt; in &lt;a href="https://simondosda.github.io/posts/2022-01-03-git-rebase-workflow.html"&gt;my workflow&lt;/a&gt;, I have never had the use for this command.&lt;/p&gt;

&lt;p&gt;But do not hesitate to &lt;a href="https://git-scm.com/docs/git-reset"&gt;read the documentation&lt;/a&gt; if you think you might be interested.&lt;/p&gt;

</description>
      <category>git</category>
      <category>tooling</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Get a clean git history with a rebase workflow</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Tue, 18 Jan 2022 18:27:06 +0000</pubDate>
      <link>https://dev.to/simondosda/get-a-clean-git-history-with-a-rebase-workflow-4pnd</link>
      <guid>https://dev.to/simondosda/get-a-clean-git-history-with-a-rebase-workflow-4pnd</guid>
      <description>&lt;p&gt;When I started using git, I had little concern about my commit history. &lt;br&gt;
I only used git as a backup tool for my work without trying to have consistent and readable commits.&lt;/p&gt;

&lt;p&gt;That's a common pitfall with git. I have often worked in teams where we would integrate all our commit iterations into our main branch, with commit messages as descriptive as &lt;code&gt;wip&lt;/code&gt;, ending with a &lt;strong&gt;hardly useful history&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Doing so strongly limits the benefits of using git. &lt;br&gt;
When we choose to enforce having a clean, readable history, composed of well-defined atomic commits on a single linear branch, the advantages of doing so greatly overcome the little extra time we spend to ensure everything we integrate to our main branch is clean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we can now &lt;strong&gt;easily read the history&lt;/strong&gt; and understand what changes were made&lt;/li&gt;
&lt;li&gt;if there are some changes we don't understand, we can read all the &lt;strong&gt;relevant associated changes in the same commit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;if we have a problem with a deployment, we can &lt;strong&gt;easily revert the last commit&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I will introduce the main rules to maintain a clean git history and show you how I personally apply them in my day-to-day development.&lt;/p&gt;

&lt;p&gt;There is no right way to use git, so don't take anything as an absolute truth but rather as an inspiration to create your own workflow.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rules for a clean history
&lt;/h2&gt;

&lt;p&gt;There are only three rules to enforce a clean git history:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a commit should be atomic: it should contain &lt;strong&gt;all and only&lt;/strong&gt; the changes required for a feature or a fix&lt;/li&gt;
&lt;li&gt;a commit should have a &lt;strong&gt;clear, descriptive message&lt;/strong&gt;. I also use &lt;a href="https://www.conventionalcommits.org"&gt;conventional commits&lt;/a&gt; format, which can be helpful when working on a versioned project&lt;/li&gt;
&lt;li&gt;a commit should be directly integrated to long-running branches, using the &lt;code&gt;rebase&lt;/code&gt; functionality of git, to &lt;strong&gt;avoid merge commits&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This last one can seem surprising, as it is easy to use git without even knowing the &lt;code&gt;rebase&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;What &lt;code&gt;rebase&lt;/code&gt; does is that it "moves" your commits on top of the branch you are rebasing on.&lt;/p&gt;

&lt;p&gt;The main drawback of using &lt;code&gt;rebase&lt;/code&gt; is that it rewrites your branch history. Therefore it won't be consistent with your remote repository, and you will need to force push on it.&lt;/p&gt;

&lt;p&gt;But it will allow you to have a linear commit history, whereas the &lt;code&gt;merge&lt;/code&gt; command will create a new commit for each merge with the changes from the merged branch, resulting in a less readable history.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vXrd5YEZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vci71gklxc0m78u0bg2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vXrd5YEZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vci71gklxc0m78u0bg2y.png" alt="git merge vs git rebase" width="880" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This workflow works well, no matter if you are &lt;strong&gt;alone on a personal project or working in a big team&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But if you can always deal with your mess alone, these rules will be helpful if they are enforced in a team.&lt;/p&gt;
&lt;h2&gt;
  
  
  Before we start, a word on git aliases
&lt;/h2&gt;

&lt;p&gt;I use several times a day the commands shown in this article. &lt;br&gt;
To be more efficient, I have set up aliases for most of them. &lt;br&gt;
In the below examples, I will use the full command and show you as a comment the version using my aliases.&lt;/p&gt;

&lt;p&gt;If you want to know more about them, you can go to my article explaining my &lt;a href="https://simondosda.github.io/posts/2021-04-13-git-aliases.html"&gt;git aliases for an efficient workflow&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Starting a new development
&lt;/h2&gt;

&lt;p&gt;I usually start by creating a new branch from the latest main branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout main  &lt;span class="c"&gt;# git co main&lt;/span&gt;
git pull
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; my-branch  &lt;span class="c"&gt;# git nb my-branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NB: there are two main ways to create a new branch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git branch my-branch&lt;/code&gt; will create a new branch but stay on the current one, which is useful when you want to save your current branch before altering it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git checkout -b my-branch&lt;/code&gt; is a checkout command with the &lt;code&gt;-b&lt;/code&gt; option that will create the new branch before checking out on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, we just created a new branch similar to the main one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I5IcFJq---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f4n1ofuf0507vey4ts31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I5IcFJq---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f4n1ofuf0507vey4ts31.png" alt="git workflow create branch" width="870" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Working on the issue
&lt;/h2&gt;

&lt;p&gt;I then start working and will usually create several commits, depending on the complexity of the development. This allows going back if I take a wrong direction, or change my mind at one point. I still usually try to break things into commits that make sense.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'feat: step 1'&lt;/span&gt;  &lt;span class="c"&gt;# git c 'feat: step 1'&lt;/span&gt;
git git push &lt;span class="nt"&gt;--set-upstream&lt;/span&gt; origin/my-branch  &lt;span class="c"&gt;# git p&lt;/span&gt;
...
git commit &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'feat: stept 7'&lt;/span&gt;  &lt;span class="c"&gt;# git c 'feat: stept 7'&lt;/span&gt;
git push  &lt;span class="c"&gt;# git p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0Cn92cYz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/og7gqh52syz6hyscm3d2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Cn92cYz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/og7gqh52syz6hyscm3d2.png" alt="git workflow create commits" width="870" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But at the end of the feature development, I will rewrite my history to have only one clean commit; or several if it is more appropriate (for instance, if I also fixed something while working on a feature).&lt;/p&gt;

&lt;p&gt;To do so, I usually use an interactive &lt;code&gt;rebase&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;git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; HEAD~7  &lt;span class="c"&gt;# git ri 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The way interactive &lt;code&gt;rebase&lt;/code&gt; works is that it displays the list of commits, with the action you can do on it. &lt;br&gt;
The main ones I use are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pick&lt;/code&gt; or &lt;code&gt;p&lt;/code&gt;: the default, keep the commit as it is&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reword&lt;/code&gt; or &lt;code&gt;r&lt;/code&gt;: allows you to rewrite the commit message in a next step&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;squash&lt;/code&gt; or &lt;code&gt;s&lt;/code&gt;: integrate the commit with the previous one, you will be asked to edit the message&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fixup&lt;/code&gt; or &lt;code&gt;f&lt;/code&gt;: integrate the commit with the previous one while keeping its message&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;drop&lt;/code&gt; or &lt;code&gt;d&lt;/code&gt;: remove the commit. You can also get the same result by removing the entire line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--keJVk5Yg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2ud5upje6n6aarav98b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--keJVk5Yg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2ud5upje6n6aarav98b.png" alt="git workflow squash" width="870" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the history has been rewritten, I need to force push to the remote repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;--force&lt;/span&gt;  &lt;span class="c"&gt;# git fp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rebasing your branch
&lt;/h2&gt;

&lt;p&gt;Before pushing my changes for a review, I will usually rebase on the main branch so that my branch is up to date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch &amp;amp; git rebase origin/main  &lt;span class="c"&gt;# git rom&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is needed only if new changes were integrated into the main branch. &lt;br&gt;
If any conflicts were created, this is when you will be asked to solve them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OppBPIc8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2apw9hb8tr8brkb4mfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OppBPIc8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2apw9hb8tr8brkb4mfa.png" alt="git workflow rebase" width="870" height="643"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Reviewing the changes before integration
&lt;/h2&gt;

&lt;p&gt;I then open a merge/pull request in gitlab/github, and almost always find corrections to make.&lt;/p&gt;

&lt;p&gt;If the changes I want to make are important, I will create new commits, but for small corrections I usually integrate them directly to the current commit by amending it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &amp;amp; git push &lt;span class="nt"&gt;--force&lt;/span&gt;  &lt;span class="c"&gt;# git afp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When everything seems clean, I will ask for a review and will usually integrate the changes requested in a new commit so that the reviewer can quickly check them.&lt;/p&gt;

&lt;p&gt;Eventually, before integrating this new feature to the main branch, I will regroup the commits into one again as done previously, and rebase on the main branch if necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NB: For this workflow to be effective, you need to enforce rebase merging in gitlab/github.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cleaning up
&lt;/h2&gt;

&lt;p&gt;Finally, Once merged, I will delete my branch on the server (this is configured to be done automatically after merging) and on my local repository, so that these repositories stay clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-D&lt;/span&gt; my-branch  &lt;span class="c"&gt;# git del my-branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have automated the setup of all my projects &lt;a href="https://simondosda.github.io/posts/2021-05-03-tmux.html"&gt;thanks to tmuxp&lt;/a&gt;, and I always start by updating the repository and displaying its status. &lt;br&gt;
That allows me to quickly see what branches I am currently working on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git fetch &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git branch &lt;span class="nt"&gt;-vv&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git status  &lt;span class="c"&gt;# git state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;These are the rules and the workflow I try to follow on all my projects.&lt;/p&gt;

&lt;p&gt;I admit it requires some effort, but the time you spend making sure that your git history is clean and readable will save you a lot of time when you will need to understand why and how some changes were made.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Follow Your Website Analitycs With Umami</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Sun, 17 Oct 2021 08:58:19 +0000</pubDate>
      <link>https://dev.to/simondosda/follow-your-website-analitycs-with-umami-h31</link>
      <guid>https://dev.to/simondosda/follow-your-website-analitycs-with-umami-h31</guid>
      <description>&lt;p&gt;This article is part of a series showing you how to quickly and freely build and host your own &lt;a href="https://jekyllrb.com/" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt; blog on &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;. This series will also cover more advanced topics like adding a comment system directly in our code using &lt;a href="https://staticman.net/" rel="noopener noreferrer"&gt;Staticman&lt;/a&gt; and adding privacy-friendly but still free analytics using &lt;a href="https://umami.is/" rel="noopener noreferrer"&gt;Umami&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I divided the tutorial into several parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-13-blog-github-pages-1-introduction" rel="noopener noreferrer"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-14-blog-github-pages-2-setup" rel="noopener noreferrer"&gt;Setting Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-15-blog-github-pages-3-content" rel="noopener noreferrer"&gt;Create Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-16-blog-github-pages-4-custom" rel="noopener noreferrer"&gt;Customize Display&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simondosda.github.io/posts/2021-09-17-blog-github-pages-5-comment-1" rel="noopener noreferrer"&gt;Commenting System Part 1&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-18-blog-github-pages-6-comment-2" rel="noopener noreferrer"&gt;Commenting System - Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Analytics &lt;strong&gt;&amp;lt;- you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that our blog is fully functional, our main job is to write interesting articles. &lt;/p&gt;

&lt;p&gt;But you are writing a blog and sharing it with the world, you probably want people to read it.&lt;br&gt;
To do so you will have to bring traffic to your website, and will therefore need to be able to follow your it, know which article attracts the most people, on so on.&lt;/p&gt;
&lt;h2&gt;
  
  
  Picking a Traffic Analytics solution
&lt;/h2&gt;

&lt;p&gt;When it comes to gathering traffic data from your website, you mainly have two choices of products.&lt;/p&gt;

&lt;p&gt;The first one is to use a free solution. &lt;br&gt;
Let's be honest, we are talking about &lt;a href="https://analytics.google.com/analytics/web/" rel="noopener noreferrer"&gt;Google Analytics&lt;/a&gt; here, which is already pre-integrated with Jekyll.&lt;br&gt;
It is indeed very easy to set up but raises some confidentiality issues as its trackers are very invasive, and all the data collected eventually goes to google.&lt;/p&gt;

&lt;p&gt;If you are concerned about collecting your user data through a company that will use them, you can go for a more privacy-focused solution, like &lt;a href="https://get.gaug.es/" rel="noopener noreferrer"&gt;Gauges&lt;/a&gt; or &lt;a href="https://simpleanalytics.com/" rel="noopener noreferrer"&gt;Simple Analytics&lt;/a&gt;. &lt;br&gt;
These companies will also allow you to collect your visitors' data, but without exploiting them. &lt;br&gt;
Most of the time, they are less privacy-invading and only collect relevant information. &lt;br&gt;
They also usually don't use cookies, allowing you to avoid having to put an annoying cookie banner for European visitors to comply with GDPR policies.&lt;/p&gt;

&lt;p&gt;The counterpart for using a solution not exploiting your visitor's data? &lt;br&gt;
Well, companies need to make profits to live, so you need to pay for it. &lt;br&gt;
This is totally ok and understandable, but you might not want to spend money just to know how many visitors come to your blog, which is already a generous donation of your time to the community.&lt;/p&gt;

&lt;p&gt;Fortunately, development is an amazing world and some analytics companies have open-sourced their code. &lt;br&gt;
Here, we will see how we can deploy one of them, &lt;a href="https://umami.is/" rel="noopener noreferrer"&gt;Umami&lt;/a&gt;, and how to use it for our website.  &lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying Umami on Heroku
&lt;/h2&gt;

&lt;p&gt;As we already did for our comment system, we will deploy &lt;em&gt;Umami&lt;/em&gt; on &lt;em&gt;Heroku&lt;/em&gt;. You can see how to do so &lt;a href="https://umami.is/docs/running-on-heroku" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but we will do it together anyway.&lt;/p&gt;

&lt;p&gt;First, you need to fork the &lt;a href="https://github.com/mikecao/umami" rel="noopener noreferrer"&gt;Umamy repository&lt;/a&gt; on &lt;em&gt;GitHub&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Then, go to your &lt;em&gt;Heroku&lt;/em&gt; account, create a new app, and under the &lt;code&gt;Deploy&lt;/code&gt; section choose &lt;code&gt;connect to Github&lt;/code&gt; as &lt;code&gt;Deployment method&lt;/code&gt;, search for the repository you just forked, and connect to it.&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%2F0vfup5lf6lcjq7c8fmwr.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%2F0vfup5lf6lcjq7c8fmwr.png" alt="Connect to Github"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then need to add a database. Go to the &lt;code&gt;Ressources&lt;/code&gt; tab, search for &lt;code&gt;Heroku Postgres&lt;/code&gt; in the &lt;code&gt;Add-ons&lt;/code&gt; section and install it.&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%2F4227xb76ddvy9kdqzpel.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%2F4227xb76ddvy9kdqzpel.png" alt="Add Heroku Postgres"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to create the required tables. &lt;br&gt;
Click on your new add-on (open in a new tab), then go to &lt;code&gt;Settings -&amp;gt; View Credentials&lt;/code&gt;. &lt;br&gt;
You will get everything you need to connect to your database. Personally, I have installed the &lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt;Heroku CLI&lt;/a&gt; so I just run the corresponding line in my terminal. &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%2Fuexj2ycospghf36ao8qg.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%2Fuexj2ycospghf36ao8qg.png" alt="Connection to Postgres"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then copied the lines from the &lt;a href="https://github.com/SimonDosda/umami/blob/master/sql/schema.postgresql.sql" rel="noopener noreferrer"&gt;schema.postgresql.sql&lt;/a&gt; file to init the tables. &lt;br&gt;
You can check everything went ok by running the &lt;code&gt;\dt&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;

&lt;span class="n"&gt;List&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;relations&lt;/span&gt;
 &lt;span class="k"&gt;Schema&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="n"&gt;Name&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Type&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;     &lt;span class="k"&gt;Owner&lt;/span&gt;      
&lt;span class="c1"&gt;--------+----------+-------+----------------&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ivcqpajmowwzfs&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ivcqpajmowwzfs&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;pageview&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ivcqpajmowwzfs&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;session&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ivcqpajmowwzfs&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;website&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ivcqpajmowwzfs&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then go back to the app page and, in the &lt;code&gt;Settings -&amp;gt; Config Vars&lt;/code&gt;, add an &lt;code&gt;HASH_SALT&lt;/code&gt; environment variable with any random string as a key. &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%2F530etn4f0ge69a3mv28r.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%2F530etn4f0ge69a3mv28r.png" alt="App Environment Variables"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Phew, we finally ended the configuration of our app!&lt;/p&gt;

&lt;p&gt;You can now go to the &lt;code&gt;Deploy&lt;/code&gt; section and click &lt;code&gt;Deploy Branch&lt;/code&gt; in the &lt;code&gt;Manual deploy&lt;/code&gt; part.&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%2Fzpl7y9whuu5j3on8rran.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%2Fzpl7y9whuu5j3on8rran.png" alt="Deploy App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the build is over, you will be able to open the app and log into it with the username &lt;code&gt;admin&lt;/code&gt; and the password &lt;code&gt;umami&lt;/code&gt;.&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%2Fapu39gx70ds3guv74vps.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%2Fapu39gx70ds3guv74vps.png" alt="Umami Login"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing you want to do is to change this password in &lt;code&gt;Settings -&amp;gt; Profile -&amp;gt; Change password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We now have to add our website to the application. To do so, go to &lt;code&gt;Settings -&amp;gt; Website -&amp;gt; Add website&lt;/code&gt;&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%2F3isdwsg3rs8k0vfjh4su.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%2F3isdwsg3rs8k0vfjh4su.png" alt="Add Website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click then on &lt;code&gt;get tracking code&lt;/code&gt; to get the script line to add to your website head.&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%2Fey6gcqz4emilb8wcsmvc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fey6gcqz4emilb8wcsmvc.gif" alt="Add Traking Code"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/head.html --&amp;gt;&lt;/span&gt;
...
{%- if jekyll.environment == 'production' -%}
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt; 
            &lt;span class="na"&gt;data-website-id=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;your-id&amp;gt;"&lt;/span&gt; 
            &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;your-src&amp;gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
{%- endif -%}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redeploy your website with it and visit it, then go to your Umami dashboard, you should see your first visit! &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%2F99o0xjw7dg15ya34kvto.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%2F99o0xjw7dg15ya34kvto.png" alt="Analytics Display"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah, my first visitor, myself!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Congrats, you have reached the end of this tutorial!&lt;/p&gt;

&lt;p&gt;I hope it was beneficial to you and that you enjoyed it. You can find the code of this sample project &lt;a href="https://github.com/SimonDosda/gp-blog" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We covered quite a lot, from setting up and deploying your blog to its customization and the add of features like a commenting system or analytics.&lt;/p&gt;

&lt;p&gt;A lot can still be done, and I encourage you to explore new possibilities from here.&lt;/p&gt;

&lt;p&gt;If there is a subject you are particularly interested in, please share it in the comment section.&lt;/p&gt;

&lt;p&gt;But in any case, don't forget that the most important on the website is the content ;).&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>tutorial</category>
      <category>github</category>
      <category>portfolio</category>
    </item>
    <item>
      <title>Add A Comment System To A Jekyll Blog Using Staticman - 2 / 2</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Sat, 16 Oct 2021 15:10:00 +0000</pubDate>
      <link>https://dev.to/simondosda/add-a-comment-system-to-a-jekyll-blog-using-staticman-2-2-l0h</link>
      <guid>https://dev.to/simondosda/add-a-comment-system-to-a-jekyll-blog-using-staticman-2-2-l0h</guid>
      <description>&lt;p&gt;This article is part of a series showing you how to quickly and freely build and host your own &lt;a href="https://jekyllrb.com/" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt; blog on &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;. This series will also cover more advanced topics like adding a comment system directly in our code using &lt;a href="https://staticman.net/" rel="noopener noreferrer"&gt;Staticman&lt;/a&gt; and adding privacy-friendly but still free analytics using &lt;a href="https://umami.is/" rel="noopener noreferrer"&gt;Umami&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I divided the tutorial into several parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-13-blog-github-pages-1-introduction" rel="noopener noreferrer"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-14-blog-github-pages-2-setup" rel="noopener noreferrer"&gt;Setting Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-15-blog-github-pages-3-content" rel="noopener noreferrer"&gt;Create Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-16-blog-github-pages-4-custom" rel="noopener noreferrer"&gt;Customize Display&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simondosda.github.io/posts/2021-09-17-blog-github-pages-5-comment-1" rel="noopener noreferrer"&gt;Commenting System Part 1&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Commenting System Part 2 &lt;strong&gt;&amp;lt;- you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-19-blog-github-pages-7-analytics" rel="noopener noreferrer"&gt;Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the last part, we added a functional comment system.&lt;/p&gt;

&lt;p&gt;But there are still a lot of features that can be added, starting by allowing to respond to a comment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a reply feature
&lt;/h2&gt;

&lt;p&gt;To enable replying to a message, we need to know that a comment is a reply to another one.&lt;/p&gt;

&lt;p&gt;To do so, we will add a &lt;code&gt;parent_id&lt;/code&gt; field in our messages. &lt;br&gt;
To allow it, add it to the &lt;code&gt;allowedFields&lt;/code&gt; property in the &lt;code&gt;staticman.yml&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="c1"&gt;# staticman.yml&lt;/span&gt;
&lt;span class="na"&gt;comments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;allowedFields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parent_id"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We do not add it to the &lt;code&gt;requiredFields&lt;/code&gt; as only replies will have a parent message.&lt;/p&gt;

&lt;p&gt;Then we need to add this field in our &lt;code&gt;comment-form.html&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comment-form.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ site.staticman_url }}"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- options inputs --&amp;gt;&lt;/span&gt;
  ...

  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fields[parent_id]"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt;
    &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ include.parent_id }}"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- user fields inputs --&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We store the &lt;code&gt;parent_id&lt;/code&gt; field in a hidden input field using the value given by the &lt;code&gt;include&lt;/code&gt; variable, which holds the include's inputs.&lt;/p&gt;

&lt;p&gt;Now we will add this form below each comment with its corresponding parent id. This will be the input form used to reply to this comment.&lt;/p&gt;

&lt;p&gt;For each comment, we will also display its replies. To do so, we will recursively include &lt;code&gt;comment-list.html&lt;/code&gt; with the &lt;code&gt;parent_id&lt;/code&gt; as input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comment-list.html --&amp;gt;&lt;/span&gt;
{% assign parent_id = include.parent_id | default: '' %} 
{% assign comments = site.data.comments[page.slug] | where_exp: "item", "item.parent_id == parent_id" %} 
{% assign sorted_comments = comments | sort: 'date' %}
{% for comment in sorted_comments %}
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;{{comment.name}}&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-meta dt-published"&lt;/span&gt;
    &lt;span class="na"&gt;datetime=&lt;/span&gt;&lt;span class="s"&gt;"{{ page.date | date_to_xmlschema }}"&lt;/span&gt;
    &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"datePublished"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {%- assign date_format = site.minima.date_format | default: "%b %-d, %Y" -%}
    {{ comment.date | date:"%H:%M - %b %-d, %Y, %Y" }}
  &lt;span class="nt"&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{comment.message | strip_html | markdownify }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-reply"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Reply to {{ comment.name }}:&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    {% include comment-form.html parent_id=comment._id %} 
    {% include comment-list.html parent_id=comment._id %}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have the ability to reply each message and we display its replies under it.&lt;/p&gt;

&lt;p&gt;Here is the result. I just added &lt;code&gt;.comment-reply { padding: 15px; }&lt;/code&gt; in &lt;code&gt;_sass/comments.scss&lt;/code&gt; to create a simple nested effect.&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%2Fqt8wce1q01tt74rsijqo.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%2Fqt8wce1q01tt74rsijqo.png" alt="Comment Reply Feature"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a start, but we definitely don't want to display all these reply boxes. They should appear only if we hit a reply button. &lt;/p&gt;

&lt;p&gt;We will implement this feature using only HTML and CSS.&lt;/p&gt;

&lt;p&gt;First, let's update our comment reply box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comment-list.html --&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-reply"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"reply-{{ comment._id}}"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"reply-{{ comment._id }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Reply to {{ comment.name }}
  &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"close"&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"reply-{{ comment._id }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;X&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  {% include comment-form.html parent_id=comment._id %} 
  {% include comment-list.html parent_id=comment._id %}
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The display of the reply box is held by a checkbox. This one will not be displayed, but instead targeted by the two labels, &lt;code&gt;reply to ...&lt;/code&gt; to open the reply box and &lt;code&gt;X&lt;/code&gt; to close it.&lt;/p&gt;

&lt;p&gt;Each label will be displayed depending on the &lt;code&gt;checked&lt;/code&gt; property of the checkbox.&lt;/p&gt;

&lt;p&gt;Here is the css code to make it work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _sass/comments.scss&lt;/span&gt;
&lt;span class="nc"&gt;...&lt;/span&gt;

&lt;span class="nc"&gt;.comment-reply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nc"&gt;.open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;italic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.close&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;align-self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex-end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.open&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.close&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.comment-form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.checkbox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.open&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.close&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:checked&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt; &lt;span class="nc"&gt;.comment-form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a much cleaner reply feature!&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%2Ft5pnazkld45ic293lhin.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5pnazkld45ic293lhin.gif" alt="Comment Reply Animation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a markdown editor for messages
&lt;/h2&gt;

&lt;p&gt;There are still a few improvements we can make to our new functionality. &lt;br&gt;
The first one is to use a markdown editor to allow your readers editing their comments in markdown easily&lt;/p&gt;

&lt;p&gt;To do so, we will use a javascript markdown editor called &lt;a href="https://simplemde.com/" rel="noopener noreferrer"&gt;SimpleMDE&lt;/a&gt;. This is quite an elegant solution as this library will target our textarea and replace them, which mean that our solution will still work if one of our users has javascript disabled on its browser (who does that?).&lt;/p&gt;

&lt;p&gt;First, we need to add the link to the library and its CSS in our &lt;code&gt;head&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/head.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  ...

  &lt;span class="c"&gt;&amp;lt;!-- Simple Markdown Editor --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
    &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then edit the comment form HTML to add a &lt;code&gt;SimpleMDE&lt;/code&gt; instance attached to each &lt;code&gt;textarea&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comment-form.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ site.staticman_url }}"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"message-{{ include.parent_id }}"&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-message"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fields[message]"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Comment (markdown accepted)"&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;simplemde&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;SimpleMDE&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message-{{ include.parent_id }}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;forceSync&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;spellChecker&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="na"&gt;status&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="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Comment (markdown supported)&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I also added a unique id to the textarea based on the parent id.&lt;/p&gt;

&lt;p&gt;I also added some changes to the editor style.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _sass/comments.scss&lt;/span&gt;
&lt;span class="nc"&gt;...&lt;/span&gt;

&lt;span class="nc"&gt;.CodeMirror&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.editor-toolbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.CodeMirror&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.CodeMirror-scroll&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150px&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 here we are!&lt;/p&gt;

&lt;h2&gt;
  
  
  Better management of redirect
&lt;/h2&gt;

&lt;p&gt;Currently, when we send a comment, we are redirected back to the article page. This behavior can be pretty confusing. Usually, we expect to get a notification that our comment was sent. Moreover, as our website need to be rebuilt to display the comment we sent, everything looks as if nothing happened!&lt;/p&gt;

&lt;p&gt;Let's work on improving this part. &lt;/p&gt;

&lt;p&gt;To improve the experience, we will redirect readers to a thank-you page displaying that the comment will be readable soon.&lt;/p&gt;

&lt;p&gt;Let's create a new page, &lt;code&gt;comment-success.markdown&lt;/code&gt; in our root directory, that will be almost the same as &lt;code&gt;index.markdown&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;&amp;lt;!-- comment-success.markdown --&amp;gt;
---
&lt;/span&gt;layout: home
list_title: "Read Our Latest Posts"
&lt;span class="gh"&gt;title: ''
---
&lt;/span&gt;
&lt;span class="gu"&gt;## Thank you!&lt;/span&gt;

Your comment was successfully sent!

It will appear on our website soon.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now change the redirect option in &lt;code&gt;_includes/comment-form.html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comment-form.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; 
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"options[redirect]"&lt;/span&gt; 
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; 
    &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ 'comment-success' | absolute_url }}"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;p&gt;Of course, there is a lot that could still be done to improve our comment system. But that's already a good start, and we fully build it ourselves!&lt;/p&gt;

&lt;p&gt;You can find the code for this part &lt;a href="https://github.com/SimonDosda/gp-blog/tree/step-4.2-comment" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's quite a journey since we started setting up our blog!&lt;/p&gt;

&lt;p&gt;In our last chapter, we will see how to implement &lt;a href="https://simondosda.github.io/posts/2021-09-19-blog-github-pages-7-analytics.html" rel="noopener noreferrer"&gt;free privacy-friendly analytics&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>portfolio</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Add A Comment System To A Jekyll Blog Using Staticman - 1 / 2</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Fri, 15 Oct 2021 06:13:32 +0000</pubDate>
      <link>https://dev.to/simondosda/add-a-comment-system-to-a-jekyll-blog-using-staticman-1-2-2782</link>
      <guid>https://dev.to/simondosda/add-a-comment-system-to-a-jekyll-blog-using-staticman-1-2-2782</guid>
      <description>&lt;p&gt;This article is part of a series showing you how to quickly and freely build and host your own &lt;a href="https://jekyllrb.com/" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt; blog on &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;. This series will also cover more advanced topics like adding a comment system directly in our code using &lt;a href="https://staticman.net/" rel="noopener noreferrer"&gt;Staticman&lt;/a&gt; and adding privacy-friendly but still free analytics using &lt;a href="https://umami.is/" rel="noopener noreferrer"&gt;Umami&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I divided the tutorial into several parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-13-blog-github-pages-1-introduction" rel="noopener noreferrer"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-14-blog-github-pages-2-setup" rel="noopener noreferrer"&gt;Setting Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-15-blog-github-pages-3-content" rel="noopener noreferrer"&gt;Create Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-16-blog-github-pages-4-custom" rel="noopener noreferrer"&gt;Customize Display&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Commenting System - Part 1 &lt;strong&gt;&amp;lt;- you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-18-blog-github-pages-6-comment-2" rel="noopener noreferrer"&gt;Commenting System - Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-19-blog-github-pages-7-analytics" rel="noopener noreferrer"&gt;Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have our blog, let's see how to add some interactivity to it with a commenting system.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a static website can become dynamic
&lt;/h2&gt;

&lt;p&gt;Adding a commenting system to a Jekyll website is tricky as it is a static site generator, with no backend allowing us to store comments in a database and serve them.&lt;/p&gt;

&lt;p&gt;The immediate solution to solve this issue is to use an external service with its own database. Among the popular ones, &lt;a href="https://disqus.com/" rel="noopener noreferrer"&gt;disqus&lt;/a&gt; is often used with Jekyll. It has the advantage of being easy to implement, as it is a script loaded on your page, but it also presents privacy issues and displays ads in its free version.&lt;/p&gt;

&lt;p&gt;Another possible solution to add dynamic content to a GitHub website is to use &lt;a href="https://staticman.net/" rel="noopener noreferrer"&gt;staticman&lt;/a&gt;. On the opposite of the previous solutions using external databases, &lt;em&gt;staticman&lt;/em&gt; creates files in your repository, updating your website statically. It is free and open-source but not as straightforward to implement as &lt;em&gt;disqus&lt;/em&gt;. &lt;br&gt;
The nice thing is that it will store all your comments in your git repository, so there is no risk of losing them.&lt;/p&gt;

&lt;p&gt;As we are courageous, this is the solution we will implement for our website.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying Staticman on Heroku
&lt;/h2&gt;

&lt;p&gt;In order to use &lt;em&gt;staticman&lt;/em&gt;, we need to deploy it on a server. This app will be used as a proxy to receive our comments and create files in our repository.&lt;/p&gt;

&lt;p&gt;We will use &lt;a href="https://www.heroku.com" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; to deploy our staticman instance. You might need to create an account first if you don't have one.&lt;/p&gt;

&lt;p&gt;Hence this is done, go to the &lt;a href="https://github.com/eduardoboucas/staticman" rel="noopener noreferrer"&gt;staticman GitHub repository&lt;/a&gt; and fork it.&lt;/p&gt;

&lt;p&gt;On your Heroku dashboard, go to &lt;code&gt;New -&amp;gt; Create new app&lt;/code&gt;, type the name that you want for your app, and hit &lt;code&gt;Create app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;Deployment method&lt;/code&gt; of the &lt;code&gt;Deploy&lt;/code&gt; tab, click on &lt;code&gt;GitHub&lt;/code&gt;. Once connected, choose the &lt;code&gt;staticman&lt;/code&gt; repository and hit &lt;code&gt;Connect&lt;/code&gt;.&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%2Fozy2ihdvttm2zncnj0rx.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%2Fozy2ihdvttm2zncnj0rx.png" alt="Staticman Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this is done, you can go to the &lt;code&gt;Manual deploy&lt;/code&gt; section and click on &lt;code&gt;Deploy Branch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This will deploy an application to &lt;a href="https://sdosda-staticman.herokuapp.com/" rel="noopener noreferrer"&gt;&lt;code&gt;https://&amp;lt;app-name&amp;gt;.herokuapp.com/&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
There should be an error displayed. It is expected as we haven't configured it yet. We will take care of it now.&lt;/p&gt;

&lt;p&gt;The app will need to be able to access your GitHub repository. To do so, create a new application in GitHub &lt;code&gt;Settings → Developer settings → GitHub Apps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create an app with the following inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Homepage URL: &lt;code&gt;https://staticman.net/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Webhook URL: &lt;code&gt;https://&amp;lt;app-name&amp;gt;.herokuapp.com/v1/webhook&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Repository permissions / Contents: &lt;code&gt;Access: read and write&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Repository permissions / Pull Requests: &lt;code&gt;Access: read and write&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe to events: &lt;code&gt;Pull request&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once your app is created, go to &lt;code&gt;General → Private keys&lt;/code&gt; and hit &lt;code&gt;Generate a private key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, go to &lt;code&gt;Install App&lt;/code&gt; and hit &lt;code&gt;Install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Go back to Heroku. Now it is time to configure our app.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;Settings -&amp;gt; Config Vars&lt;/code&gt;, create the following variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GITHUB_APP_ID&lt;/code&gt;: the GitHub app id displayed at the beginning of the &lt;code&gt;General&lt;/code&gt; section&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GITHUB_PRIVATE_KEY&lt;/code&gt;: the private key we just generated&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RSA_PRIVATE_KEY&lt;/code&gt;: a private key that will be used to encrypt content, you can use the same as &lt;code&gt;GITHUB_PRIVATE_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go back to the deploy tab and run a manual deployment again.&lt;/p&gt;

&lt;p&gt;Once the deployment is over, you should now see a welcome message when you visit your app.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding Staticman to our project
&lt;/h2&gt;

&lt;p&gt;We now need to add staticman to our project.&lt;br&gt;
First, we will create a &lt;code&gt;staticman.yml&lt;/code&gt; file that defines its configuration as follow.&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="c1"&gt;# staticman.yml&lt;/span&gt;
&lt;span class="na"&gt;comments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# (*) REQUIRED&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Names of the fields the form is allowed to submit. If a field that is&lt;/span&gt;
  &lt;span class="c1"&gt;# not here is part of the request, an error will be thrown.&lt;/span&gt;
  &lt;span class="na"&gt;allowedFields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# (*) REQUIRED&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Name of the branch being used. Must match the one sent in the URL of the&lt;/span&gt;
  &lt;span class="c1"&gt;# request.&lt;/span&gt;
  &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;

  &lt;span class="c1"&gt;# Text to use as the commit message or pull request title. Accepts placeholders.&lt;/span&gt;
  &lt;span class="na"&gt;commitMessage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Comment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{fields.name}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{options.slug}"&lt;/span&gt;

  &lt;span class="c1"&gt;# (*) REQUIRED&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Destination path (filename) for the data files. Accepts placeholders.&lt;/span&gt;
  &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entry{@timestamp}"&lt;/span&gt;

  &lt;span class="c1"&gt;# The format of the generated data files. Accepted values are "json", "yaml"&lt;/span&gt;
  &lt;span class="c1"&gt;# or "frontmatter"&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yaml"&lt;/span&gt;

  &lt;span class="c1"&gt;# List of fields to be populated automatically by Staticman and included in&lt;/span&gt;
  &lt;span class="c1"&gt;# the data file. Keys are the name of the field. The value can be an object&lt;/span&gt;
  &lt;span class="c1"&gt;# with a `type` property, which configures the generated field, or any value&lt;/span&gt;
  &lt;span class="c1"&gt;# to be used directly (e.g. a string, number or array)&lt;/span&gt;
  &lt;span class="na"&gt;generatedFields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;date&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp-seconds"&lt;/span&gt;

  &lt;span class="c1"&gt;# Whether entries need to be approved before they are published to the main&lt;/span&gt;
  &lt;span class="c1"&gt;# branch. If set to `true`, a pull request will be created for your approval.&lt;/span&gt;
  &lt;span class="c1"&gt;# Otherwise, entries will be published to the main branch automatically.&lt;/span&gt;
  &lt;span class="na"&gt;moderation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# When allowedOrigins is defined, only requests sent from one of the listed domains will be accepted.&lt;/span&gt;
  &lt;span class="na"&gt;allowedOrigins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;simondosda.github.io"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# (*) REQUIRED&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Destination path (directory) for the data files. Accepts placeholders.&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_data/comments/{options.slug}"&lt;/span&gt;

  &lt;span class="c1"&gt;# Names of required fields. If any of these isn't in the request or is empty,&lt;/span&gt;
  &lt;span class="c1"&gt;# an error will be thrown.&lt;/span&gt;
  &lt;span class="na"&gt;requiredFields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might have to change the name of the &lt;code&gt;branch&lt;/code&gt; from which you are deploying and you will have to update the &lt;code&gt;allowedOrigins&lt;/code&gt; value with your domain name or remove it if you don't feel like you need this security.&lt;/p&gt;

&lt;p&gt;This post will focus on building a simple comment system where any user can enter a &lt;code&gt;name&lt;/code&gt; and a &lt;code&gt;message&lt;/code&gt; in markdown. We will allow them to respond to other messages as well.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Staticman&lt;/em&gt; allows you to check for spam automatically, send email notifications, and implement Recaptcha, but we won't cover this here.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;_config&lt;/code&gt; file, add the following entry, with your own &lt;code&gt;app name&lt;/code&gt;, github &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;repo&lt;/code&gt; and &lt;code&gt;branch&lt;/code&gt;.&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="c1"&gt;# _config.yml&lt;/span&gt;
&lt;span class="na"&gt;staticman_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;app-name&amp;gt;/v3/entry/github/&amp;lt;username&amp;gt;/&amp;lt;repo&amp;gt;/&amp;lt;branch&amp;gt;/comments&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the input form
&lt;/h2&gt;

&lt;p&gt;We will now build the form where our readers will be able to write their names and comments.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;_includes&lt;/code&gt; folder, add a new &lt;code&gt;comment-form.html&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comment-form.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ site.staticman_url }}"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"options[redirect]"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt;
    &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ page.url | absolute_url }}"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"options[slug]"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ page.slug }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-message"&lt;/span&gt;
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fields[message]"&lt;/span&gt;
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Comment (markdown accepted)"&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-bottom"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-name"&lt;/span&gt;
      &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"fields[name]"&lt;/span&gt;
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
      &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment-submit"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;SEND&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we just did is that we set up a form that will send data to the &lt;code&gt;staticman_url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As we defined in our &lt;code&gt;staticman.yml&lt;/code&gt; config file, our comments are based on 2 fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: the name of the sender, defined by &lt;code&gt;&amp;lt;input name="fields[name]" required&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;message&lt;/code&gt;: the content of the comment, define by &lt;code&gt;&amp;lt;textarea name="fields[message]" required&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also provide two options when we validate the form, defined by hidden inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;redirect&lt;/code&gt;: the URL to be redirected to once the submit call will complete. Here we come back to our page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;slug&lt;/code&gt;: the slug of the page that will be used to create the comment folder, as we defined our &lt;code&gt;path&lt;/code&gt; as &lt;code&gt;"_data/comments/{options.slug}"&lt;/code&gt; in the &lt;code&gt;staticman.yml&lt;/code&gt; configuration file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Displaying the list of comments
&lt;/h2&gt;

&lt;p&gt;Comments sent to our repository will be stored in the &lt;code&gt;_data/comments/&amp;lt;slug&amp;gt;&lt;/code&gt; directory.&lt;br&gt;
The way to access them in &lt;em&gt;Jekyll&lt;/em&gt; is with the variable &lt;code&gt;site.data.comments[page.slug]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's create a &lt;code&gt;comment-list.html&lt;/code&gt; file in our &lt;code&gt;_includes&lt;/code&gt; folder to display them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comment-list.html --&amp;gt;&lt;/span&gt;
{% assign comments = site.data.comments[page.slug] | where_exp: "item", "true" %} 
{% assign sorted_comments = comments | sort: 'date' %} 
{% for comment in sorted_comments %}
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comment"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;{{comment.name}}&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;time&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-meta dt-published"&lt;/span&gt;
    &lt;span class="na"&gt;datetime=&lt;/span&gt;&lt;span class="s"&gt;"{{ page.date | date_to_xmlschema }}"&lt;/span&gt;
    &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"datePublished"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {%- assign date_format = site.minima.date_format | default: "%b %-d, %Y" -%}
    {{ comment.date | date:"%H:%M - %b %-d, %Y, %Y" }}
  &lt;span class="nt"&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{comment.message | strip_html | markdownify }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we do here is looping over the page comments sorted by date. The first line with the &lt;code&gt;where_expression&lt;/code&gt; filter is required to get the comment values.&lt;/p&gt;

&lt;p&gt;For each comment, we display the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;date&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt; with the &lt;code&gt;strip_html&lt;/code&gt; filter to secure the message and the &lt;code&gt;markdownify&lt;/code&gt; filter to render markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the comments block
&lt;/h2&gt;

&lt;p&gt;We now have everything we need for our simple commenting feature.&lt;/p&gt;

&lt;p&gt;Let's add another file in our &lt;code&gt;_include&lt;/code&gt; folder named &lt;code&gt;comments.html&lt;/code&gt; that will wrap together our two comments snippets: posting a new comment and displaying received comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/comments.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"comments"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {% if site.data.comments[page.slug] %}
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Comments&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    {% include comment-list.html %}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  {% endif %}

  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Leave a Comment&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    {% include comment-form.html %}
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can update our &lt;code&gt;_layout/post.html&lt;/code&gt; file to display these comments instead of the &lt;code&gt;disqus&lt;/code&gt; comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _layout/post.html --&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-content e-content"&lt;/span&gt; &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"articleBody"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ content }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

{% include comments.html %}

&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"u-url"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ page.url | relative_url }}"&lt;/span&gt; &lt;span class="na"&gt;hidden&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Style our comments
&lt;/h2&gt;

&lt;p&gt;Before pushing our brand-new feature, let's add some style to our comments.&lt;/p&gt;

&lt;p&gt;As it is a separate feature, the best option is probably to create its own CSS file. Let's create a new &lt;code&gt;comments.scss&lt;/code&gt; in the sass file and add the import line &lt;code&gt;@import "comments";&lt;/code&gt; in &lt;code&gt;assets/main.scss&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _sass/comments.scss&lt;/span&gt;
&lt;span class="nc"&gt;.comment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;h3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;royalblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.comment-new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.comment-form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;25px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nc"&gt;.comment-message&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nc"&gt;.comment-name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.comment-message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.comment-bottom&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.comment-name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.comment-submit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#ddd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;royalblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now go ahead and deploy our comment system.&lt;/p&gt;

&lt;p&gt;When you send a comment, it should open a merge request that will be automatically validated if you set &lt;code&gt;moderation: false&lt;/code&gt; in the staticman configuration file.&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%2F6hxsen2yn4q4w4s1ts7w.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%2F6hxsen2yn4q4w4s1ts7w.png" alt="List of comments"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our comment system is now functional!&lt;/p&gt;

&lt;p&gt;You can find the code for this part &lt;a href="https://github.com/SimonDosda/gp-blog/tree/step-4.1-comment" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This comment system is still very basic, our next step will be to &lt;a href="https://simondosda.github.io/posts/2021-09-18-blog-github-pages-6-comment-2" rel="noopener noreferrer"&gt;add some features like allowing to reply to a comment or adding a markdown editor&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>portfolio</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Customize Your Jekyll Website</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Thu, 14 Oct 2021 06:09:24 +0000</pubDate>
      <link>https://dev.to/simondosda/customize-your-jekyll-website-2eob</link>
      <guid>https://dev.to/simondosda/customize-your-jekyll-website-2eob</guid>
      <description>&lt;p&gt;This article is part of a series showing you how to quickly and freely build and host your own &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; blog on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;. This series will also cover more advanced topics like adding a comment system directly in our code using &lt;a href="https://staticman.net/"&gt;Staticman&lt;/a&gt; and adding privacy-friendly but still free analytics using &lt;a href="https://umami.is/"&gt;Umami&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I divided the tutorial into several parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-13-blog-github-pages-1-introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-14-blog-github-pages-2-setup"&gt;Setting Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-15-blog-github-pages-3-content"&gt;Create Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Customize Display &lt;strong&gt;&amp;lt;- you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-17-blog-github-pages-5-comment-1"&gt;Commenting System - Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-18-blog-github-pages-6-comment-2"&gt;Commenting System - Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-19-blog-github-pages-7-analytics"&gt;Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have started adding content to our website, let's see how we can customize its appearance.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Jekyll generates pages
&lt;/h2&gt;

&lt;p&gt;One of the reasons I like &lt;em&gt;Jekyll&lt;/em&gt; is that it provides you a very clean directory as it hides most of the magic behind the website creation.&lt;br&gt;
However, the drawback is that it is easy to feel lost and not to know how to update your theme when it comes to customizing it.&lt;/p&gt;

&lt;p&gt;Indeed, layouts are defined by your theme and are not present in your local folder.&lt;/p&gt;

&lt;p&gt;So before doing anything, we need to look at our &lt;a href="https://github.com/jekyll/minima"&gt;minima theme repository&lt;/a&gt;. Please make sure you look at the branch of your current version.&lt;br&gt;
In my case, it is the 2.5 one that is installed (you can have this info in the &lt;code&gt;Gemfile&lt;/code&gt; file), so I am looking at the &lt;a href="https://github.com/jekyll/minima/tree/2.5-stable"&gt;2.5-stable branch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our blog structure mainly relies on two folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_layouts&lt;/code&gt;: contains the layouts which are used in the Front Matter metadata of each markdown file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_includes&lt;/code&gt;: contains snippets of HTML code that are used in layouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For instance, if we look at the code of the &lt;code&gt;post&lt;/code&gt; layout, we see the following (shortened for comprehension).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _layouts/post.html --&amp;gt;&lt;/span&gt;
---
layout: default
---
&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post h-entry"&lt;/span&gt;
  &lt;span class="na"&gt;itemscope&lt;/span&gt;
  &lt;span class="na"&gt;itemtype=&lt;/span&gt;&lt;span class="s"&gt;"http://schema.org/BlogPosting"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-title p-name"&lt;/span&gt; &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"name headline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{ page.title | escape }}
    &lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-content e-content"&lt;/span&gt; &lt;span class="na"&gt;itemprop=&lt;/span&gt;&lt;span class="s"&gt;"articleBody"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ content }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a lot to comment on here.&lt;/p&gt;

&lt;p&gt;The first thing we see is that the blog layout uses the &lt;code&gt;default&lt;/code&gt; layout.&lt;br&gt;
The way layout nesting works is that everything that you have below your Front Matter will be injected in the &lt;code&gt;{{ content }}&lt;/code&gt; variable of its parent layout.&lt;/p&gt;

&lt;p&gt;For instance when you write a blog post using this layout, its content is injected in the &lt;code&gt;&amp;lt;div class="post-content e-content" itemprop="articleBody"&amp;gt;{{ content }}&amp;lt;/div&amp;gt;&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;We can also see the use of Front Matter metadata with the title of the post.&lt;br&gt;
The title is displayed using the &lt;code&gt;page&lt;/code&gt; variable, which contains all the variables defined in the Front Matter of a template.&lt;/p&gt;

&lt;p&gt;These variables can be overridden by the children of the template. In this case, there is no title defined in the Front Matter of our template, it only makes sense for posts using this template.&lt;/p&gt;

&lt;p&gt;Now, if we look at the default layout, this is what we see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _layouts/default.html --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"{{ page.lang | default: site.lang | default: 'en' }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {% include head.html %}

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    {% include header.html %}

    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page-content"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ content }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

    {% include footer.html %}
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default layout represents the base structure of all our pages.&lt;br&gt;
Besides the &lt;code&gt;content&lt;/code&gt; variable where the child layout is injected, it uses several &lt;code&gt;include&lt;/code&gt; to inject HTML code.&lt;/p&gt;

&lt;p&gt;Let's now dive into them and start customizing our blog by adding a favicon.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding a favicon
&lt;/h2&gt;

&lt;p&gt;Let's start gently by adding a favicon to our website. If you are familiar with HTML, you know that the favicon is defined in the &lt;code&gt;head&lt;/code&gt; tag. For this theme, it is the &lt;code&gt;_includes/head.html&lt;/code&gt; file that contains this tag.&lt;/p&gt;

&lt;p&gt;You can override any file of your theme by putting it in your own project.&lt;br&gt;
In this case, we can create our own &lt;code&gt;_includes/head.html&lt;/code&gt;, copy the code from GitHub and modify it to add our favicon.&lt;/p&gt;

&lt;p&gt;For my favicon, I generated one using &lt;a href="https://favicon.io/emoji-favicons/lion"&gt;a lion emoji&lt;/a&gt; for the sake of the exercise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/head.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  {% seo %}
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/assets/main.css' | relative_url }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  {% feed_meta %} 
  {% if jekyll.environment == 'production' and site.google_analytics %} 
    {% include google-analytics.html %} 
  {% endif %}

  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
    &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"180x180"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/apple-touch-icon.png' | relative_url }}"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
    &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"32x32"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/favicon-32x32.png' | relative_url }}"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
    &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/png"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"16x16"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/favicon-16x16.png' | relative_url }}"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modifying the header
&lt;/h2&gt;

&lt;p&gt;The same way we overrode our &lt;code&gt;head.html&lt;/code&gt; fille, we can override any file to customize your website as we want.&lt;/p&gt;

&lt;p&gt;For instance, let's modify our header by adding our new logo.&lt;/p&gt;

&lt;p&gt;To do so we need to add and modify the &lt;code&gt;_includes/header.html&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _includes/header.html --&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"site-header"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"banner"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {% assign default_paths = site.pages | map: "path" %} 
    {% assign page_paths = site.header_pages | default: default_paths %}
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"site-title"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"author"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/' | relative_url }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ 'favicon-32x32.png' | relative_url }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      {{ site.title | escape }}
    &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Customizing our CSS
&lt;/h2&gt;

&lt;p&gt;There are several ways to modify our CSS. As you can see in the &lt;code&gt;head.html&lt;/code&gt; file, CSS is currently pulled from &lt;code&gt;assets/main.css&lt;/code&gt;.&lt;br&gt;
In the development file, it is actually a &lt;code&gt;scss&lt;/code&gt; file that is built from the &lt;code&gt;_sass&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;An option to customize our css is to copy/paste the &lt;code&gt;_sass&lt;/code&gt; directory and edit its files, the same we did for the head file.&lt;/p&gt;

&lt;p&gt;Another possibility is to generate a new file that will be loaded after the theme css, so that the rules we put in it will override the default ones. This is the solution I will show you now.&lt;/p&gt;

&lt;p&gt;First, let's create the &lt;code&gt;assets/main.scss&lt;/code&gt; file with the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// assets/main.scss&lt;/span&gt;
&lt;span class="nt"&gt;--------&lt;/span&gt;
&lt;span class="nt"&gt;--------&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nt"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;"minima"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"custom"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compared to the default file, I just added an import of the file &lt;code&gt;custom&lt;/code&gt;. We can now create this file in the &lt;code&gt;_sass&lt;/code&gt; folder and add some css.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _sass/custom.scss&lt;/span&gt;
&lt;span class="nc"&gt;.site-title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;orangered&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;:visited&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;orangered&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;Here I modify the site title color to match our lion. My main point is to show you how you can do it, it is up to you to decide how you want to style your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a featured image to our posts
&lt;/h2&gt;

&lt;p&gt;Something you will see in almost every blog is a featured image displayed in the blog list and at the top of an article.&lt;/p&gt;

&lt;p&gt;Unfortunately, this is not currently managed by &lt;em&gt;Jekyll&lt;/em&gt;, so let's implement this feature ourselves.&lt;/p&gt;

&lt;p&gt;This time we want to modify the blog layout directly. We can create our own version in &lt;code&gt;_layouts/post.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What we are going to do is to check for a &lt;code&gt;featured_image&lt;/code&gt; variable, and if it exists, we will display it on top of our title by adding the following snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% if page.featured_image %}
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"featured-image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/assets/' | append: page.featured_image | relative_url }}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
{% endif %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add a featured image to our last post. Put the image you want in your assets folder and add its name in the post metadata.&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;--------&lt;/span&gt;
&lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;post&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Post"&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2021-06-31&lt;/span&gt;
&lt;span class="na"&gt;categories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jekyll blogging&lt;/span&gt;
&lt;span class="na"&gt;featured_image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;featured-image.jpg&lt;/span&gt;
&lt;span class="s"&gt;--------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then add some CSS to our &lt;code&gt;custom.scss&lt;/code&gt; file to style it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _sass/custom.scss&lt;/span&gt;
&lt;span class="nc"&gt;...&lt;/span&gt; 
&lt;span class="nc"&gt;.featured-image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;250px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&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;Here is the result.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Updating the home to display the featured image
&lt;/h2&gt;

&lt;p&gt;Let's improve our home page.&lt;/p&gt;

&lt;p&gt;First, copy the &lt;code&gt;home.html&lt;/code&gt; in the &lt;code&gt;_layout&lt;/code&gt; folder. Following the same principle as for the post layout, we can add our featured images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- _layout/home.html --&amp;gt;&lt;/span&gt;
-------- 
layout: default 
--------

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"home"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {% if page.title %}
    &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ page.title }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  {% endif %} 
  {{ content }} 
  {% if site.posts.size &amp;gt; 0 %}
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-list-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {{ page.list_title | default: "Posts" }}
    &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {% for post in site.posts %}
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
          {% assign date_format = site.minima.date_format 
            | default: "%b %-d, %Y" %}
          &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-meta"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            {{ post.date | date: date_format }}
          &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"post-link"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ post.url | relative_url }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              {{ post.title | escape }}
            &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
          {% if site.show_excerpts %} 
            {{ post.excerpt }} {% endif %}
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        {% if post.featured_image %}
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"featured-image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
            &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/assets/' | append: post.featured_image | relative_url }}"&lt;/span&gt;
          &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        {% endif %}
      &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      {% endfor %}
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"rss-subscribe"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      subscribe &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ '/feed.xml' | relative_url }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;via RSS&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  {% endif %}
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And update our &lt;code&gt;custom.scss&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _sass/custom.scss&lt;/span&gt;
&lt;span class="nc"&gt;...&lt;/span&gt; &lt;span class="nc"&gt;.post-list&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap-reverse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.featured-image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="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;Here you are, you now have all the basics to create your personal blog at your own image.&lt;/p&gt;

&lt;p&gt;You can find the code for this part &lt;a href="https://github.com/SimonDosda/gp-blog/tree/step-3-custom"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our next step is now &lt;a href="https://simondosda.github.io/posts/2021-09-17-blog-github-pages-5-comment-1"&gt;to add a commenting system to our blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>tutorial</category>
      <category>portfolio</category>
      <category>programming</category>
    </item>
    <item>
      <title>Create Blog Posts And Static Pages With Jekyll and GitHub Pages</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Wed, 13 Oct 2021 05:19:36 +0000</pubDate>
      <link>https://dev.to/simondosda/create-blog-posts-and-static-pages-with-jekyll-and-github-pages-15gi</link>
      <guid>https://dev.to/simondosda/create-blog-posts-and-static-pages-with-jekyll-and-github-pages-15gi</guid>
      <description>&lt;p&gt;This article is part of a series showing you how to quickly and freely build and host your own &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; blog on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;. This series will also cover more advanced topics like adding a comment system directly in our code using &lt;a href="https://staticman.net/"&gt;Staticman&lt;/a&gt; and adding privacy-friendly but still free analytics using &lt;a href="https://umami.is/"&gt;Umami&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I divided the tutorial into several parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-13-blog-github-pages-1-introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-14-blog-github-pages-2-setup%20"&gt;Setting Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create Content &lt;strong&gt;&amp;lt;- you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-16-blog-github-pages-4-custom"&gt;Customize Display&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-17-blog-github-pages-5-comment-1"&gt;Commenting System - Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-18-blog-github-pages-6-comment-2"&gt;Commenting System - Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-19-blog-github-pages-7-analytics"&gt;Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have initialized our project, let's see how to manage our content by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;updating our home page to add some information before the list of blog posts&lt;/li&gt;
&lt;li&gt;adding a static page showcasing our best GitHub projects&lt;/li&gt;
&lt;li&gt;adding a new post to our brand new blog&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Updating our home page
&lt;/h2&gt;

&lt;p&gt;Our blog already has three pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.markdown&lt;/code&gt;: the home page of our site&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;about.markdown&lt;/code&gt;: an about page that we can access from the header&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_post/yyyy-mm-dd-welcome-to-jekyll.markdown&lt;/code&gt;: an example of a blog post&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The beauty of &lt;em&gt;Jekyll&lt;/em&gt; is that all pages are build using markdown, which is more readable than HTML and easy to reuse elsewhere.&lt;/p&gt;

&lt;p&gt;For instance, I often start writing my posts using &lt;a href="https://www.notion.so"&gt;Notion&lt;/a&gt; and then export them in markdown. And a lot of blogging platforms, like &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt; or &lt;a href="https://hashnode.com/"&gt;hashnode&lt;/a&gt;, use this language for their post editor.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Jekyll&lt;/em&gt; also creates the navigation of the website automatically, whether you are adding a static page or a blog post.&lt;/p&gt;

&lt;p&gt;The home page, which corresponds to the &lt;code&gt;index.markdown&lt;/code&gt; file, displays the list of blog posts by default. If you look at its content, you will see that this file is almost empty. Indeed, all the logic behind the display of the blog posts list is hidden in the home layout.&lt;/p&gt;

&lt;p&gt;We can update the file to custom our home page, for instance, by changing the title above the list of posts using the property &lt;code&gt;list_title&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Moreover, we can add a welcome message on top of our page. We just need to add some content to the file, and it will be automatically placed above the post list.&lt;/p&gt;

&lt;p&gt;Finally, I also added the property &lt;code&gt;title: ''&lt;/code&gt; as a trick to avoid &lt;em&gt;Jekyll&lt;/em&gt; using the first heading of my content as title and putting it in the header.&lt;/p&gt;

&lt;p&gt;Here is the updated code of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;&amp;lt;!-- index.markdown --&amp;gt;
---
&lt;/span&gt;layout: home
list_title: "Read Our Latest Posts"
&lt;span class="gh"&gt;title: ''
---
&lt;/span&gt;
&lt;span class="gh"&gt;# Github Pages Demo Blog&lt;/span&gt;

Welcome to this demo blog!

This website intends to show you how to easily build and
deploy a portfolio with a blog using _GitHub Pages_ and _Jekyll_.

You can find the sources of this project
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;here&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://github.com/SimonDosda/gp-blog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding a static page that showcases our best GitHub repositories
&lt;/h2&gt;

&lt;p&gt;Let's add another static page to our project. If you see this website as your portfolio, something interesting to add is a showcase of your public GitHub repositories.&lt;/p&gt;

&lt;p&gt;To do so, create a new file &lt;code&gt;projects.markdown&lt;/code&gt; in the root folder.&lt;/p&gt;

&lt;p&gt;The first thing to do is filling up the &lt;em&gt;Front Matter&lt;/em&gt; metadata (the part in between dashes).&lt;/p&gt;

&lt;p&gt;In our case, we indicate for the page a title and permalink of &lt;code&gt;projects&lt;/code&gt;.&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;--------&lt;/span&gt;
&lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;page&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Projects&lt;/span&gt;
&lt;span class="na"&gt;permalink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/projects/&lt;/span&gt;
&lt;span class="s"&gt;--------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub Pages allows you to easily loop on your public repositories, as they are accessible from the variable &lt;code&gt;site.github.public_repositories&lt;/code&gt;. &lt;em&gt;Jekyll&lt;/em&gt; uses &lt;a href="https://jekyllrb.com/docs/liquid/"&gt;Liquid templating language&lt;/a&gt; to manage variables and statements. It allows us to loop over our repositories.&lt;/p&gt;

&lt;p&gt;To filter on my most interesting repositories, I only display repositories that are not forked and contain topics. Feel free to use the conditions that suit you the best.&lt;/p&gt;

&lt;p&gt;I then display the repository name, description, list of topics, and last update date. The title also links to the repository URL.&lt;/p&gt;

&lt;p&gt;Here is the code and the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;{% for repo in site.github.public_repositories %}

{% if repo.fork == false and repo.topics.size &amp;gt; 0 %}

&lt;span class="gu"&gt;## [{{ repo.name }}]({{ repo.html_url }})&lt;/span&gt;

{{ repo.description }}

Topics: {{ repo.topics | array_to_sentence_string }}

Last updated: {{ repo.updated_at | date_to_string }}

{% endif %}

{% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Notice that the &lt;em&gt;Projects&lt;/em&gt; link was added automatically to the navigation bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a blog post
&lt;/h2&gt;

&lt;p&gt;Our blog already contains a welcome blog post created by &lt;em&gt;Jekyll&lt;/em&gt; when we initialized the project.&lt;/p&gt;

&lt;p&gt;Let's add another one to see the main functionalities you will probably use in your posts: titles, code, and images.&lt;/p&gt;

&lt;p&gt;As the welcome post states, creating a new post requires creating a new file with the template &lt;code&gt;yyyy-mm-dd-title.markdown&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's create a new blog post explaining furthermore how to write one. Go ahead and create a file &lt;code&gt;2021-06-01-write-a-post.markdown&lt;/code&gt; in the &lt;code&gt;_posts&lt;/code&gt; folder.&lt;/p&gt;

&lt;pre&gt;


```
--------
layout: post
title: "Write a Post"
date: 2021-06-31
categories: jekyll blogging
--------

The goal of this article is to add some extra info
about blog writing with _Jekyll_.

## Structure your posts

Use level 2 (`##`) and if necessary level 3 (`###`) titles
to structure your posts.

## Display code snippets

You can display a block of code like the following using triple backticks.
You can also specify the language after the first triple backticks.

```

python
def hello(name):
    return f'hello {name}'


```

## Add images

Create an `assets` folder where you can put all your images,
then display them with a link starting with an exclamative mark like this:
`![my inspiring image]({{ "/assets/sample-image.jpg" | relative_url }})`.

![my inspiring image]({{ "/assets/sample-image.jpg" | relative_url }})
_Photo by [Ian Schneider](https://unsplash.com/@goian)_
```


&lt;/pre&gt;

&lt;p&gt;The first things provided are the Front Matter metadata. Only &lt;code&gt;layout: post&lt;/code&gt; and &lt;code&gt;title&lt;/code&gt; are mandatory, but you can also add the date, categories, author, and so on. You can see more details &lt;a href="https://jekyllrb.com/docs/posts/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We then have our article written in &lt;em&gt;markdown&lt;/em&gt;, where I display a few extra tips.&lt;/p&gt;

&lt;p&gt;Below is the corresponding result.&lt;/p&gt;

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

&lt;p&gt;You now know the basics of content management for your blog.&lt;/p&gt;

&lt;p&gt;You can find the code for this part &lt;a href="https://github.com/SimonDosda/gp-blog/tree/step-2-content"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our next step is now to &lt;a href="https://simondosda.github.io/posts/2021-09-16-blog-github-pages-4-custom"&gt;customize the appearance of our website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>portfolio</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Setup Your Free Portfolio With A Blog Using GitHub Pages</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Tue, 12 Oct 2021 06:27:04 +0000</pubDate>
      <link>https://dev.to/simondosda/setup-your-free-portfolio-with-a-blog-using-github-pages-2f1d</link>
      <guid>https://dev.to/simondosda/setup-your-free-portfolio-with-a-blog-using-github-pages-2f1d</guid>
      <description>&lt;p&gt;This article is part of a series showing you how to quickly and freely build and host your own &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; blog on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;. This series will also cover more advanced topics like adding a comment system directly in our code using &lt;a href="https://staticman.net/"&gt;Staticman&lt;/a&gt; and adding privacy-friendly but still free analytics using &lt;a href="https://umami.is/"&gt;Umami&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I divided the tutorial into several parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-13-blog-github-pages-1-introduction"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Setting Up &lt;strong&gt;&amp;lt;- you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-15-blog-github-pages-3-content"&gt;Create Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-16-blog-github-pages-4-custom"&gt;Customize Display&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-17-blog-github-pages-5-comment-1"&gt;Commenting System - Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-18-blog-github-pages-6-comment-2"&gt;Commenting System - Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-19-blog-github-pages-7-analytics"&gt;Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's see how we can set up and deploy our website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your GitHub Pages repository
&lt;/h2&gt;

&lt;p&gt;To deploy your website using &lt;em&gt;GitHub Pages&lt;/em&gt;, you need to create a new public repository using the following convention for the name: &lt;code&gt;&amp;lt;username&amp;gt;.github.io&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Once you have done this, the content of your repository will be deployed on &lt;code&gt;https://&amp;lt;username&amp;gt;.github.io&lt;/code&gt;. You can try adding an &lt;code&gt;index.html&lt;/code&gt; file at the root of your repository with "Hello World" or anything you fancy written in it to check everything works as expected.&lt;/p&gt;

&lt;p&gt;Note that you can deploy a website from any repository, but in this case, the associated URL will be &lt;code&gt;https://&amp;lt;username&amp;gt;.github.io/&amp;lt;repository-name&amp;gt;&lt;/code&gt;. This feature is very convenient to deploy documentation of your projects, for instance.&lt;/p&gt;

&lt;p&gt;For this tutorial, I will use a specific repository called &lt;a href="https://github.com/SimonDosda/gp-blog"&gt;gp-blog&lt;/a&gt; (for Github Pages Blog), which will therefore deploy at &lt;a href="https://simondosda.github.io/gp-blog"&gt;https://simondosda.github.io/gp-blog&lt;/a&gt;, but you most probably want to deploy your portfolio at the root folder.&lt;/p&gt;

&lt;p&gt;Now that our git repository is ready, let's see how to set up &lt;em&gt;Jekyll&lt;/em&gt; in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your Jekyll static website
&lt;/h2&gt;

&lt;p&gt;Jekyll is a Ruby Gem, so make sure you have all the prerequisites installed to use it. You can find their list and the procedure to install them on your OS at &lt;a href="https://jekyllrb.com/docs/installation/"&gt;https://jekyllrb.com/docs/installation/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have done this, install &lt;em&gt;Jekyll&lt;/em&gt; and &lt;em&gt;Bundler&lt;/em&gt; gems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;jekyll bundler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now initialize our &lt;em&gt;Jekyll&lt;/em&gt; project in the repository we just created 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;jekyll new &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, open the &lt;em&gt;Gemfile&lt;/em&gt; file and follow the instruction to deploy on &lt;em&gt;GitHub Pages&lt;/em&gt; by commenting the &lt;code&gt;gem "jekyll"&lt;/code&gt; line and uncommenting the &lt;code&gt;gem "github-pages"&lt;/code&gt; one.&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;# This will help ensure the proper Jekyll version is running.&lt;/span&gt;
&lt;span class="c"&gt;# Happy Jekylling!&lt;/span&gt;
&lt;span class="c"&gt;# gem "jekyll", "~&amp;gt; 4.2.0"&lt;/span&gt;
&lt;span class="c"&gt;# If you want to use GitHub Pages, remove the "gem "jekyll"" above and&lt;/span&gt;
&lt;span class="c"&gt;# uncomment the line below. To upgrade, run `bundle update github-pages`.&lt;/span&gt;
gem &lt;span class="s2"&gt;"github-pages"&lt;/span&gt;, &lt;span class="s2"&gt;"~&amp;gt; 214"&lt;/span&gt;, group: :jekyll_plugins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the version (here 214), you can find the latest version &lt;a href="https://pages.github.com/versions/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can then type the command &lt;code&gt;bundle update&lt;/code&gt; to install your dependencies and serve your website locally with the command &lt;code&gt;bundle exec jekyll serve --livereload&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your new blog is now served on &lt;a href="http://localhost:4000"&gt;localhost:4000&lt;/a&gt;. As you can see, it needs some customization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit the global configuration
&lt;/h2&gt;

&lt;p&gt;We can now start customizing our new blog. The first thing to do is to update the &lt;code&gt;_config.yml&lt;/code&gt; file. There you can edit your blog title, its description, your email address, and &lt;em&gt;GitHub&lt;/em&gt; username.&lt;/p&gt;

&lt;p&gt;You can also enter your &lt;em&gt;Twitter&lt;/em&gt; username or comment the corresponding line if you don't have any.&lt;/p&gt;

&lt;p&gt;You can also add the property &lt;code&gt;show_excerpts: true&lt;/code&gt; to display posts' excerpts on the home page.&lt;/p&gt;

&lt;p&gt;More anecdotical, you can change the permalink generation for pages with the &lt;code&gt;permalink&lt;/code&gt; attribute. I like to use a less nested structure like the following.&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="na"&gt;permalink&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/:collection/:year-:month-:day-:title:output_ext&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though we use the &lt;code&gt;--livereload&lt;/code&gt; option, it will not consider changes done in the &lt;code&gt;_config.yml&lt;/code&gt; file. You need to kill your server and relaunch it, and you should see your changes appear in your browser.&lt;/p&gt;

&lt;p&gt;If everything is ok, you can commit your changes and push them.&lt;/p&gt;

&lt;p&gt;Note that you can edit the branch which deploys your website on your GitHub repository in &lt;em&gt;Settings → Pages → Source&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here we are. We now have our blog backbone up and running!&lt;/p&gt;

&lt;p&gt;You can find the code for this part &lt;a href="https://github.com/SimonDosda/gp-blog/tree/step-1-setup"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our next step is now to &lt;a href="https://simondosda.github.io/posts/2021-09-15-blog-github-pages-3-content"&gt;add some content to our website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>portfolio</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build A Portfolio With A Blog Using GitHub Pages</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Mon, 11 Oct 2021 06:28:55 +0000</pubDate>
      <link>https://dev.to/simondosda/build-a-portfolio-with-a-blog-using-github-pages-kfi</link>
      <guid>https://dev.to/simondosda/build-a-portfolio-with-a-blog-using-github-pages-kfi</guid>
      <description>&lt;p&gt;This series of articles will show you how to quickly and freely deploy a personal portfolio website with a blog.&lt;/p&gt;

&lt;p&gt;At the end of this tutorial, you will know how to build and host your own &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; blog on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;, how to create new pages or blog posts, and how to customize them.&lt;/p&gt;

&lt;p&gt;We will also cover more advanced topics like adding a comment system directly in our code using &lt;a href="https://staticman.net/"&gt;Staticman&lt;/a&gt; and integrating free privacy-friendly analytics using &lt;a href="https://umami.is/"&gt;Umami&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I divided this tutorial into several parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction &lt;strong&gt;&amp;lt;- you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-14-blog-github-pages-2-setup.html"&gt;Setting Up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-15-blog-github-pages-3-content.html"&gt;Create Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-16-blog-github-pages-4-custom.html"&gt;Customize Display&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="%7Bhttps://simondosda.github.io/posts/2021-09-17-blog-github-pages-5-comment-1.html"&gt;Commenting System - Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-18-blog-github-pages-6-comment-2.html"&gt;Commenting System - Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simondosda.github.io/posts/2021-09-19-blog-github-pages-7-analytics.html"&gt;Analytics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;When I started this blog, one piece of advice made a lot of sense for a developer wanting to start a blog: Even though you are a developer, don’t lose your time building your blog, focus on writing content and publish it on any blogging platform.&lt;/p&gt;

&lt;p&gt;I am totally in agreement with this advice. It is too easy for us to spend a lot of time creating a blog from scratch, missing the main point: a blog is about what you have to say. Spending your time to develop your blog is just disguised procrastination.&lt;/p&gt;

&lt;p&gt;But the “create your own blog” team had a point though: creating your blog allows you to own your content.&lt;/p&gt;

&lt;p&gt;I eventually went for a trade-off between these two approaches: setting my own blog very quickly using GitHub Pages while republishing my content on &lt;a href="https://dev.to/simondosda"&gt;dev.to&lt;/a&gt;, which allows me to reach a broader audience.&lt;/p&gt;

&lt;p&gt;After deploying my blog on &lt;em&gt;GitHub Pages&lt;/em&gt;, I realized it was also a perfect solution to host a portfolio. GitHub is a natural place to showcase your work as a developer, and this will be a big plus for recruiters or clients if you are a freelancer.&lt;/p&gt;

&lt;p&gt;So even if the blogging part does not appeal to you, you still might be interested in following this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Github Pages?
&lt;/h2&gt;

&lt;p&gt;There are many solutions to create your blog or portfolio, and I am far from trying them all.&lt;br&gt;
Using Github Pages is not the perfect solution, but it has some noticeable pros for developers.&lt;/p&gt;

&lt;p&gt;First, you are probably already quite familiar with the use of &lt;em&gt;Git&lt;/em&gt; and &lt;em&gt;GitHub&lt;/em&gt;. The fact that all your content will be in a Git folder is lovely and provides versioning for your articles and pages.&lt;/p&gt;

&lt;p&gt;It also makes sense to have a website link to your &lt;em&gt;GitHub&lt;/em&gt; account where you centralize all your development-related content: blog articles, portfolio, side projects, and so on. You can see this as your personal showcase.&lt;/p&gt;

&lt;p&gt;And last but not least, deploying and hosting your website on &lt;em&gt;GitHub Pages&lt;/em&gt; is really easy and totally free!&lt;/p&gt;

&lt;h2&gt;
  
  
  How will it work?
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we will use a static site generator named &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;. &lt;em&gt;Jekyll&lt;/em&gt; is the framework used by &lt;em&gt;GitHub&lt;/em&gt; to power &lt;em&gt;GitHub Pages&lt;/em&gt;, which gives us the significant advantage of having &lt;em&gt;GitHub&lt;/em&gt; building our pages without needing to do anything.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Jekyll&lt;/em&gt; builds HTML pages from markdown documents, which is excellent as this is the standard used by many blogging platforms and tools. Once you add an article in a markdown format, pushing it to your deployment branch will update your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the cons?
&lt;/h2&gt;

&lt;p&gt;There is no perfect solution to build a website, and it is essential to know the limitation of your stack when choosing it.&lt;/p&gt;

&lt;p&gt;As a static site generator, Jekyll will build all pages of our website at deployment. This is nice as it provides a fast navigation experience, compared to a website using a back-end for which pages would be build when the user navigates the website.&lt;/p&gt;

&lt;p&gt;But it also means that our pages will not be dynamic, with no interactivity with a database. Of course, you can still add some interactivity using javascript and AJAX calls to a service, but it means you will need a separated back-end for this.&lt;/p&gt;

&lt;p&gt;Hosting a &lt;em&gt;Jekyll&lt;/em&gt; website on &lt;em&gt;GitHub Pages&lt;/em&gt; is probably the easiest way, but it also comes with a significant drawback: the limited list of plugins that you can use. You can find the (small) list of available plugins &lt;a href="https://pages.github.com/versions/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready to start?
&lt;/h2&gt;

&lt;p&gt;Now that we have covered what we will achieve in this tutorial, it is time to start the work and &lt;a href="https://simondosda.github.io/posts/2021-09-14-blog-github-pages-2-setup.html"&gt;set up our project&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>portfolio</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting The Type Of An Interface Property In Typescript</title>
      <dc:creator>Simon</dc:creator>
      <pubDate>Thu, 17 Jun 2021 09:25:37 +0000</pubDate>
      <link>https://dev.to/simondosda/getting-the-type-of-an-interface-property-in-typescript-5bl5</link>
      <guid>https://dev.to/simondosda/getting-the-type-of-an-interface-property-in-typescript-5bl5</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;The problem is quite simple, but its solution is not obvious.&lt;/p&gt;

&lt;p&gt;Imagine that we are working on a &lt;em&gt;Typescript&lt;/em&gt; project, and we have defined an interface with several fields using different types.&lt;br&gt;
Let's take as an example a simple interface representing a blog post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;publishDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;isDraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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 say now that we would like to provide a simple setter that will allow us to update a blog post while running some custom logic. For instance, let's consider that our setter will allow updating a post only if it is a draft.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;Typescript&lt;/em&gt;, we would write something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;updatePost&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;BlogPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;isDraft&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="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="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// error: Type 'any' is not assignable to type 'never'.&lt;/span&gt;
  &lt;span class="k"&gt;return&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;While the types of &lt;code&gt;post&lt;/code&gt; and &lt;code&gt;property&lt;/code&gt; are easy to define, the one for value is a bit more tricky, as it will depend on the property to update.&lt;/p&gt;

&lt;p&gt;Currently, &lt;em&gt;Typescript&lt;/em&gt; considers it as &lt;code&gt;any&lt;/code&gt; as we don't provide any type hint, which gives us the error &lt;code&gt;Type 'any' is not assignable to type 'never'.&lt;/code&gt; when we try to update our property. But even if we put the error aside, we are losing the type checking capability of &lt;em&gt;Typescript&lt;/em&gt; here.&lt;/p&gt;

&lt;p&gt;What we would like to do is to set the type of &lt;code&gt;value&lt;/code&gt; to the type of the corresponding &lt;code&gt;property&lt;/code&gt; from &lt;code&gt;BlogPost&lt;/code&gt;. Let's see now how we can implement that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;In &lt;em&gt;Typescript&lt;/em&gt;, we can access the value of the property of an interface using brackets.&lt;/p&gt;

&lt;p&gt;For instance, the following code works perfectly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;updateDate&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;BlogPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publishDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;isDraft&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="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="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;updateDate&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="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// error: Argument of type '42' is not assignable to parameter of type 'Date | null'.&lt;/span&gt;

&lt;span class="nx"&gt;updateDate&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="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our case, we want a generic function that will work with any given property of our interface.&lt;/p&gt;

&lt;p&gt;To do so, we need to use a type variable for the property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;updatePost&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BlogPostKey&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&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;BlogPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPostKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BlogPostKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;isDraft&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="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="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BlogPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my awesome content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;publishDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isDraft&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="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;updatePost&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publishedDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// error: Argument of type '"publishedDate"' is not assignable to parameter of type 'keyof BlogPost'.&lt;/span&gt;

&lt;span class="nx"&gt;updatePost&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publishDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// error: Argument of type '42' is not assignable to parameter of type 'Date | null'.&lt;/span&gt;

&lt;span class="nx"&gt;updatePost&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publishDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding the type variable &lt;code&gt;BlogPostKey&lt;/code&gt; for the property type allows us to use it for the &lt;code&gt;value&lt;/code&gt; type while still ensuring that the property is a key of &lt;code&gt;BlogPost&lt;/code&gt; using &lt;code&gt;extends keyof BlogPost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I hope that trick can be helpful to you!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
