<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Static Site Generators - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Static Site Generators - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 27 Jun 2026 14:19:25 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/static-site-generators/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Lume SSG Handbook – How to Create a Static Blog with Lume ]]>
                </title>
                <description>
                    <![CDATA[ Lume is a new static site generator based on Deno. Deno is a JavaScript-based run-time environment that supports TypeScript.  Lume is not built around any specific language. It supports Markdown, Nunjucks, TypeScript, and JavaScript by default. Lume ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-static-blog-with-lume/</link>
                <guid isPermaLink="false">66d038af5ea8b15c90716651</guid>
                
                    <category>
                        <![CDATA[ Deno ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Fri, 18 Nov 2022 17:06:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/Create-a-Static-Blog-with-Lume.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Lume is a new static site generator based on Deno. Deno is a JavaScript-based run-time environment that supports TypeScript. </p>
<p>Lume is not built around any specific language. It supports Markdown, Nunjucks, TypeScript, and JavaScript by default. Lume also supports plugins. Some plugins come preinstalled by default. This is why Lume itself is template-language agnostic.</p>
<p>Before learning more about Lume, let's discuss Deno and consider some important Deno features.</p>
<h2 id="heading-what-is-deno">What is Deno?</h2>
<p>Deno is an alternative to Node.js built by <a target="_blank" href="https://en.wikipedia.org/wiki/Ryan_Dahl">Ryan Dahl</a> (who also developed Node). Deno is based on the Rust programming language, and the second main component in Deno is the JavaScript V8 engine for WebAssembly.</p>
<p>Deno has many cool features – it's fast, secure by default, is compatible with web assembly and has TypeScript support, has in-built development tools, and more. Deno also supports Node.js APIs so you can use all npm packaged with Deno.</p>
<p>In Deno, you do not need to create a configuration file to run a simple program. You simply deploy your website instantly with a second on-edge network. But my final favorite feature is the new <code>node_modules</code> folder in the workspace. Deno caches all the packages locally and uses them, which is very fast compared to Node.</p>
<p>You can check out the <a target="_blank" href="https://minimalist-blog.deno.dev/">demo blog website here,</a> and all the <a target="_blank" href="https://github.com/officialrajdeepsingh/Minimalist-blog">code is available on GitHub here</a>.</p>
<p>Now let's dive into the tutorial.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-lume-markdown">Lume + Markdown</a></li>
<li><a class="post-section-overview" href="#heading-why-is-lume-special">Why is Lume special?</a></li>
<li><a class="post-section-overview" href="#heading-how-does-lume-compare-to-other-static-site-generators">How does Lume compare to other static site generators?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-start-a-new-project-with-lume">How to start a new project with Lume</a></li>
<li><a class="post-section-overview" href="#heading-lume-folder-structure">Lume folder structure</a></li>
<li><a class="post-section-overview" href="#additional-folders">Additional folders</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-global-data">How to create global data</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-dynamic-page">How to create a dynamic page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-home-and-pagination-page">How to create a Home and Pagination page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-an-articles-page">How to build an articles page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-generate-a-category-page">How to generate a category page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-generate-a-tag-page">How to generate a tag page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-search-functionality">How to enable search functionality</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-page-find">How to install page find</a></li>
<li><a class="post-section-overview" href="#heading-lume-seo">Lume SEO</a></li>
<li><a class="post-section-overview" href="#heading-lume-sitemap">Lume Sitemap</a></li>
<li><a class="post-section-overview" href="#heading-lume-plugins">Lume plugins</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-comments">How to enable comments</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-netlify-cms-with-lume">How to use Netlify CMS with Lume</a></li>
<li><a class="post-section-overview" href="#heading-how-to-deploy-your-blog-with-deno-deploy">How to deploly your blog with Deno Deploy</a></li>
<li><a class="post-section-overview" href="#github-pages">Github pages</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-lume-markdown">Lume + Markdown</h2>
<p>Lume is a new static site generator based on Deno created and maintained by <a target="_blank" href="https://github.com/oscarotero">Óscar Otero</a>. Lume uses <strong>markdown-it</strong> as the default markdown. You can use the <a target="_blank" href="https://lume.land/plugins/remark/">remark plugin</a> to change the default markdown.  </p>
<p>Markdown is a language that helps to write documents, readme files, and blogs on the internet. <a target="_blank" href="https://en.wikipedia.org/wiki/John_Gruber">John Gruber</a> created markdown in 2004.</p>
<p><strong>Markdown-it</strong> is similar to <a target="_blank" href="https://github.github.com/gfm/">GitHub-flavored Markdown</a> (GFM) markdown. GFM and <a target="_blank" href="https://github.com/markdown-it/markdown-it">markdown-it</a> both follow the exact <a target="_blank" href="https://commonmark.org/">markdown specifications</a>. </p>
<p>If you've worked with GitHub and written README files, that means you are likely familiar with GFM markdown. If you don't like the default (markdown-it) markdown, you can change the markdown with the remark plugin.</p>
<p>There are tons of static site generators. So why is Lume special? What does it provide compared to other static site generators? Let's find out.</p>
<h2 id="heading-why-is-lume-special">Why is Lume special?</h2>
<p>As you know, Lume is built on Deno, and Deno is a Node.js alternative—that is why Lume provides lots of features out of the box. </p>
<p>Lume works similarly to a GitHub readme file. If you're familiar with writing one of those (and using markdown), you do not need to learn anything else to write articles and documentation with Lume.</p>
<p>Here are some benefits of Lume:</p>
<ol>
<li>Lume supports multiple template engines like Markdown, <a target="_blank" href="https://lume.land/plugins/nunjucks/">Nunjucks</a>, <a target="_blank" href="https://lume.land/plugins/eta/">Eta</a>, <a target="_blank" href="https://lume.land/plugins/jsx/">JSX</a>, <a target="_blank" href="https://lume.land/plugins/liquid/">Liquid</a>, or <a target="_blank" href="https://lume.land/plugins/pug/">Pug</a>.</li>
<li>It supports multiple authors</li>
<li>It has code syntax highlighting</li>
<li>There's great SEO support</li>
<li>Lume supports multiple languages</li>
<li>It has Windi CSS support</li>
<li>There's pagination and component support</li>
<li>It supports minifying JavaScript, HTML, CSS, and SASS</li>
<li>It has relations support</li>
<li>There is the built-in search functionality</li>
<li>It supports Netlify CMS</li>
<li>It supports images and SVGs</li>
<li>There's Remark.js plugin support</li>
<li>You can deploy with Netlify, Vercel, GitLab Pages, and the GitHub page.</li>
</ol>
<h2 id="heading-how-does-lume-compare-to-other-static-site-generators">How Does Lume Compare to Other Static Site Generators?</h2>
<p>Lume is a new static site generator compared to others, but it comes with many configuration options, and you can do anything with it. You don't even need to use any third-party plugins. </p>
<p>With Lume processors and preprocessors, you can easily manipulate the HTML code with the JavaScript DOM API. Other static site generators support a few template engines, but Lume supports many template engines like JavaScript, JSX, Nunjucks, Eta, JSX, Liquid, and Pug.</p>
<p>Note that Lume can seem tough to get started with for beginners. But if you're following my article, just make sure to <a target="_blank" href="https://github.com/officialrajdeepsingh/Minimalist-blog">open the code</a> which will make things much clearer for you.</p>
<h2 id="heading-how-to-start-a-new-project-with-lume">How to Start a New Project with Lume</h2>
<p>You can set up a new project with the Lume CLI with this command:</p>
<pre><code class="lang-bash">deno run -Ar https://deno.land/x/lume/init.ts
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/lume-installation-low.gif" alt="Lume installation demo" width="600" height="400" loading="lazy">
<em>Lume installation demo</em></p>
<h4 id="heading-follow-these-steps">Follow these steps:</h4>
<ol>
<li>First, create an empty  <code>mkdir lume-deno</code> folder project.</li>
<li>Then run the lume <code>init.ts</code> command.</li>
<li>Select an available plugin from the list.</li>
</ol>
<p>And you should be up and running.</p>
<h2 id="heading-lume-folder-structure">Lume Folder Structure</h2>
<p>After the installation finished, we saw three files:</p>
<ol>
<li><code>_config file</code> is used to configure Lume.</li>
<li><code>deno.json</code> is a defined script or task for Deno.</li>
<li><code>import_map.json</code> is to help you import a Deno package for the internet.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/folder-struture-1.png" alt="lume default folder structure " width="600" height="400" loading="lazy">
<em>lume default folder structure</em></p>
<h3 id="heading-how-to-run-the-lume-server">How to run the Lume server</h3>
<p>To run a local development server, you'll use the <code>deno task lume --serve</code> command. To build a website, run the <code>deno task build</code> command.</p>
<p>If you face a 404 - not found error, you can create a <code>index.njk</code> file within the root folder.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/lume404-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the <code>index.njk</code> file, paste the following code.</p>
<pre><code class="lang-nunjucks">---
title: "hello"
---
hello world
</code></pre>
<p>And you'll see the following output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/hello-world-lume-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Lume hello-world</em></p>
<h3 id="heading-additional-folders">Additional folders:</h3>
<ol>
<li><code>posts</code> folder is not a compulsory folder. It contains all your posts' markdown files.</li>
<li><code>pages</code> folder is not a compulsory folder. It has all your pages' markdown files.</li>
<li><code>author</code> folder is not a compulsory folder. It has all your author markdown files.</li>
<li><code>_components</code> folder is a <strong>compulsory</strong> folder. It has all your components.</li>
<li><code>_includes</code>  folder is a <strong>compulsory</strong> folder. It contains your layout and templates for your site.</li>
<li><code>images</code> folder is not a compulsory folder. It contains all your images.</li>
</ol>
<p>The posts, pages, authors, and images folders are optional. You can rename these folders according to your wishes. The <code>_components</code> and <code>_includes</code> folders are mandatory and you don't rename them.</p>
<p>The difference between components, layout, and template are as follows:</p>
<ul>
<li>The components are reusable code</li>
<li>The layout and template are not reusable like components.</li>
</ul>
<h2 id="heading-how-to-create-global-data">How to Create Global Data</h2>
<p>In Lume, you can create a data variable, which has access to the entire website by all template engines.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Set a variable</span>
site.data(<span class="hljs-string">"post_per_page"</span>, <span class="hljs-number">10</span>);

<span class="hljs-comment">// Set a function</span>
site.data(<span class="hljs-string">"randomNumber"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.random();
});
</code></pre>
<h4 id="heading-how-to-create-posts-pages-and-author-markdown-files">How to create posts, pages, and author markdown files</h4>
<p>You create posts, pages, and author folders in the root folder. Then, inside every folder, you write markdown files.</p>
<p>You can access all posts, pages, and authors by file name on the browser:</p>
<ol>
<li><code>localhost:3000/posts/your-title.html</code></li>
<li><code>localhost:3000/pages/your-pages.html</code></li>
<li><code>localhost:3000/author/your-author.html</code></li>
</ol>
<p>Suppose you need a demo post, pages, and author markdown for a project or template. Then, you can use <a target="_blank" href="https://github.com/officialrajdeepsingh/Demo-markdown-posts">demo-markdown posts</a> for your project. It is free and open source, and you can create your own template.</p>
<h3 id="heading-how-to-create-a-dynamic-page">How to create a dynamic page</h3>
<p>In Lume, <code>.tmpl.js</code> and <code>.tmpl.ts</code> extensions use JS and TS as <a target="_blank" href="https://lume.land/plugins/modules/">template engines</a>. You can use them with regular pages or dynamic pages to create categories, tags, pagination, and so on for your website.</p>
<h3 id="heading-how-to-create-a-home-and-pagination-page">How to create a home and pagination page</h3>
<p>The home page is based on pagination, and pagination is based on posts. Lume dynamically generates the pagination. </p>
<p>In Lume, I chose nunjucks and JavaScript to create my demo website. Nunjucks is the default template engine. You can easily change the default Nunjucks engine with another template engine with copy-paste code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/Home-page-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>home page</em></p>
<p>Lume provides a JavaScript template function that helps create dynamic web pages. If you create a home page for the site, you need to create an <code>index.tmpl.js</code> file in your root or src folder. Lume also supports an src folder to organize your project. In my demo project, I'm not using the <code>src</code> folder.</p>
<p>The  <code>*.tmpl.js</code> is an extension of a <a target="_blank" href="https://lume.land/plugins/modules/#creating-pages">JavaScript template</a> that helps create dynamic pages for websites. It comes pre-installed in Lume with the <a target="_blank" href="https://lume.land/plugins/modules/">modules plugin</a>.</p>
<p>For example, the following code creates pagination for your website. But the layout comes from the <code>_includes</code> folder.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// index.tmpl.js</span>

<span class="hljs-comment">// title for SEO</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> title = <span class="hljs-string">"Minimalist blog"</span>
<span class="hljs-comment">// description for SEO</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> description = <span class="hljs-string">"Minimalist blog theme is liteweight and work with lume."</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span>* (<span class="hljs-params">{ search, paginate }</span>) </span>{

<span class="hljs-comment">//  Get all posts of type article.</span>
  <span class="hljs-keyword">const</span> posts = search.pages(<span class="hljs-string">"type=article"</span>, <span class="hljs-string">"date=desc"</span>);

  <span class="hljs-comment">// Configation for pagination</span>
  <span class="hljs-keyword">const</span> options = {
    <span class="hljs-comment">// Page 1 is the homepage, set "/" as url</span>
    <span class="hljs-attr">url</span>: <span class="hljs-function">(<span class="hljs-params">n</span>) =&gt;</span> n === <span class="hljs-number">1</span> ? <span class="hljs-string">"/"</span> : <span class="hljs-string">`/page/<span class="hljs-subst">${n}</span>/`</span>,
    <span class="hljs-comment">// par page posts</span>
    <span class="hljs-attr">size</span>: <span class="hljs-number">7</span>,
  };

  <span class="hljs-comment">// Yield the pages, but the index needs a different layout</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> page <span class="hljs-keyword">of</span> paginate(posts, options)) {

    <span class="hljs-comment">//  if home page, use diffrent layout "/"</span>
    <span class="hljs-keyword">if</span> (page.pagination.page === <span class="hljs-number">1</span>) {
      page.menu = {<span class="hljs-attr">visible</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">order</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>:<span class="hljs-string">"Home"</span> }
      page.title = <span class="hljs-string">"Home page"</span>

      <span class="hljs-comment">//  comes from _includes folder</span>

      page.layout = <span class="hljs-string">"layouts/home.njk"</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Render diffrent layout if it is not home page page "/page/2","/page/3",etc</span>
      page.title = <span class="hljs-string">"Pagination page"</span>

      page.layout = <span class="hljs-string">"layouts/home.njk"</span>;
    }

    <span class="hljs-keyword">yield</span> page;
  }

}
</code></pre>
<p>‌Lume has a <a target="_blank" href="https://lume.land/plugins/search/">search plugin</a> that helps you search pages. In my demo blog, I search all pages base on the type. </p>
<p>In my all posts folder, all posts are defined in <code>type=article</code>, the author is described in <code>type=author</code>, and pages are defined in <code>type=page</code> . The search plugin is pre-installed with Lume.</p>
<p>On <code>index.tmpl.js</code> file, you can get all pages that have the type "article" (<code>type=article</code> ) using the following code:  <code>const posts = search.pages("type=article", "date=desc");</code>. The <code>search.pages("type=article", "date=desc")</code> function only returns those that have <code>type=article</code> .</p>
<p>The  <code>layouts/base.njk</code> layout file contains an HTML base and includes a header and footer for the website.</p>
<pre><code class="lang-nuckjunks">&lt;!doctype html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;{{ title }}&lt;/title&gt;
    &lt;meta name="description" content="{{ description or site.description }}"&gt;
   &lt;/head&gt;
  &lt;body&gt;

    {% include "layouts/header.njk" %}

    &lt;main class="{{ bodyClass }}"&gt;
      {{ content | safe }}
    &lt;/main&gt;

    {% include "layouts/footer.njk" %}

  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Inside the <code>{{ content | safe }}</code>, Lume renders other HTML, like cards, articles, home templates, Tag and category pages, and so on.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// rest code ...</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> page <span class="hljs-keyword">of</span> paginate(posts, options)) {
  }
<span class="hljs-comment">// rest code ...</span>
</code></pre>
<p>I used the for loop on the <code>index.tmp.js</code> file that helps get all the posts and send them to the <code>layouts/home.njk</code> file and the <code>layouts/home.njk</code> file. You get all posts from the result, and then pass them to the <code>card.njk</code> template.</p>
<pre><code class="lang-nunjucks">---
layout: layouts/base.njk
---

{% for post in results %}
    {% include "templates/card.njk" %}
{% else %}
    &lt;h2&gt; Posts is empty &lt;/h2&gt;
{% endfor %}

{% include "templates/pagnation.njk" %}
</code></pre>
<p>‌The <code>templates/card.njk</code>  file runs for all blogs and generates HTML for each blog. Your card looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/card.njk-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>card.njk</em></p>
<p>In <code>card.js</code> template, you can access it using <code>{{}}</code> curly brackets. Get the title using <code>{{post.data.title}}</code> and <code>{{post.data.description}}</code>.</p>
<p>In my demo blog, I'm getting only the first category to show inside the card. So I use a defined filter  <code>_config.ts</code> and use it with <code>|</code> symbols. Inside <code>card.njk</code> we get a zero index or first value in from categories with the following code: <code>{{ post.data.category | category }}</code>.</p>
<p>To get the author on <code>card.njk</code> I define the <a target="_blank" href="https://lume.land/plugins/relations/">relationship</a> between the article and the author type, which you can learn about from the docs.</p>
<pre><code class="lang-nuckjunks">&lt;div class="container px-6 py-10 mx-auto"&gt;

    &lt;div class="mt-8 lg:-mx-6 lg:flex lg:items-center"&gt;

        &lt;img class="object-cover border-none w-full lg:mx-6 lg:w-1/2 rounded-xl h-72 lg:h-96" src="{{ post.data.image }}" alt="{{ post.data.title }}"&gt;

        &lt;div class="mt-6 lg:w-1/2 lg:mt-0 lg:mx-6 "&gt;

            &lt;a class="text-sm text-blue-500 uppercase" href="/category/{{ post.data.category | category }}" &gt;
                {{ post.data.category | category }}
            &lt;/a&gt;

            &lt;a href="{{ post.data.url }}" class="block mt-4 text-2xl font-semibold text-gray-800 hover:text-gray-500 dark:text-white md:text-3xl"&gt;{{ post.data.title }}&lt;/a&gt;

            &lt;p class="mt-3 text-sm text-gray-500 dark:text-gray-300 md:text-sm"&gt;
                {{ post.data.description }}
            &lt;/p&gt;

            &lt;a href="{{  post.data.url }}" class="inline-block p-2 bg-blue-700 mt-4 text-white hover:bg-blue-500"&gt;Read more&lt;/a&gt;


            &lt;div class="flex items-center mt-6"&gt;

                {% if post.data.author.length &lt;= 2 %}

                    {% for author in post.data.author %}

                        &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ author.image}}" alt="{{ author.author_name}}"&gt;

                        &lt;div class="mx-4"&gt;
                            &lt;a href="{{author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
                                {{ author.author_name}}&lt;/a&gt;
                            &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
                                {{author.job}}
                            &lt;/p&gt;
                        &lt;/div&gt;
                    {% endfor %}
                {% else %}

                    &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ post.data.author.image}}" alt="{{ post.data.author.name}}"&gt;

                    &lt;div class="mx-4"&gt;
                        &lt;a href="{{ post.data.author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
                            {{ post.data.author.author_name}}&lt;/a&gt;
                        &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
                            {{post.data.author.job}}
                        &lt;/p&gt;
                    &lt;/div&gt;
                {% endif %}

            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>The <code>{{ title }}</code> and <code>{{description}}</code> both show the markdown file title and description. To show the category, I used a filter to show a single category on the article page and define the filter on <code>_config.ts</code> file. I also show single and multiple authors with For loop. Every card has its own <code>post.data.url</code> property, after the user clicks on the read more button user ago respected the article read page. To show the image, I used <code>{{ post.data.image }}</code> property. I also show single and multiple authors with For loop on <code>card.njk</code> file.</p>
<h2 id="heading-how-to-build-an-articles-page">How to Build an Articles Page</h2>
<p>I know the page containing the article content is one of the most important for a blog. It's where readers should spend most of their time rather than the website's home page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/random-blog-title-lume-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<pre><code class="lang-markdown">---
category:
<span class="hljs-bullet">  -</span> Blog
date: 2022-03-20T13:09:24Z
description: Dolor excepteur ad ad fugiat Lorem consectetur velit excepteur duis qui.
image: /images/dice.jpg
tags:
<span class="hljs-bullet">  -</span> npm
<span class="hljs-bullet">  -</span> npm cli
<span class="hljs-bullet">  -</span> npm install command
title: Random blog Title for markdown.
draft: false
author<span class="hljs-emphasis">_id: 1
type: article
layout: templates/article.njk
---

Laboris consequat elit ad excepteur. Ipsum duis amet dolore voluptate dolore consequat ullamco incididunt ullamco. Dolore laborum cupidatat dolor ipsum reprehenderit excepteur cupidatat dolore.

## First
Cupidatat non amet irure esse quis aute qui enim. Est qui ullamco proident consequat aute reprehenderit eiusmod nisi. Laboris ullamco fugiat sint occaecat.

## Second 
Irure fugiat officia non esse esse irure eu sint commodo quis amet. Dolor culpa non amet elit adipisicing exercitation ex anim velit ipsum.

## conclusion
Culpa irure eiusmod labore ut proident sit enim laborum nulla voluptate eu. Id tempor velit cillum pariatur est laboris ipsum ad. Sint nostrud nostrud laboris Lorem consequat tempor voluptate dolore velit. Commodo elit nulla commodo pariatur. Deserunt ipsum fugiat id ipsum pariatur cupidatat magna ex. Fugiat aliquip nisi laboris aliquip velit velit id quis eu reprehenderit excepteur fugiat.</span>
</code></pre>
<p>I created an article in the posts folder under <code>type=article</code> . The <code>author_id</code> defines the relation between the author and the article.</p>
<p>I used <code>templates/article.njk</code> as the layout for my articles page. You can design yours as per your requirements. You can design the article title, description, author card, and tags as well. </p>
<pre><code class="lang-nuckjunks">---
layout: layouts/base.njk
---
&lt;article class="container mx-auto p-2"&gt;
  &lt;div class="flex flex-col"&gt;

    &lt;h1 class="text-2xl text-black mt-3"&gt;{{ title }}&lt;/h1&gt;
    &lt;p class="text-xl mt-1 text-gray-600"&gt;{{ description }}&lt;/p&gt;

    {% if author %}
      &lt;div class="flex flex-row mt-4"&gt;


        {% if author.length &lt;= 2 %}

          {% for author in author %}

            &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ author.image}}" alt="{{ author.author_name}}"&gt;

            &lt;div class="mx-4"&gt;
              &lt;a href="{{author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
                {{ author.author_name}}&lt;/a&gt;
              &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
                {{author.job}}
              &lt;/p&gt;
            &lt;/div&gt;
          {% endfor %}

        {% else %}

          &lt;img class="border-none object-cover object-center w-10 h-10 rounded-full" src="{{ author.image}}" alt="{{ author.name}}"&gt;

          &lt;div class="mx-4"&gt;
            &lt;a href="{{ author.url}}" class="text-sm text-gray-700 dark:text-gray-200"&gt;
              {{ author.author_name}}&lt;/a&gt;
            &lt;p class="text-sm text-gray-500 dark:text-gray-400"&gt;
              {{ author.job}}
            &lt;/p&gt;
          &lt;/div&gt;
        {% endif %}

      &lt;/div&gt;

    {% endif %}

      &lt;nav class="flex flex-row my-5"&gt;
        {% for tag in tags %}
          &lt;a href="/tag/{{ tag.trim().toLowerCase().split(' ').join("-") }}/" class=" bg-blue-500 text-black p-2  mx-1"&gt;{{ tag }}&lt;/a&gt;
        {% endfor %}
      &lt;/nav&gt;

    &lt;time class="mt-2" datetime="{{ date | date('DATETIME') }}"&gt;
      {{ date | date('HUMAN_DATE') }}
    &lt;/time&gt;


  &lt;/div&gt;

  &lt;div class="mt-4"&gt;
    {{ content | safe }}
  &lt;/div&gt;
&lt;/article&gt;

{%- set previousPost = search.previousPage(url, "type=article") %}

{% if previousPost %}
  &lt;ul class="flex flex-row w-full mt-10 justify-between p-4"&gt;
    {%- if previousPost %}
      &lt;li class="w-6/12 text-left"&gt;
      ← Previous: &lt;a href="{{ previousPost.data.url }}" rel="prev"&gt;{{ previousPost.data.title }}&lt;/a&gt;
      &lt;/li&gt;
    {% endif %}

    {%- set nextPost = search.nextPage(url, "type=article") %}
    {%- if nextPost %}
      &lt;li class="w-6/12 text-right"&gt;
        &lt;strong&gt;Next: &lt;a href="{{ nextPost.data.url }}" rel="next"&gt;{{ nextPost.data.title }}&lt;/a&gt; →&lt;/strong&gt;
      &lt;/li&gt;
    {% endif %}
  &lt;/ul&gt;
{% endif %}

&lt;div class="container p-2 mx-auto mt-6"&gt; 

{# ==== #}
{#  Addding the utteranc Commenting script #}
{# ==== #}

&lt;h1 class="text-center text-2xl my-3"&gt; Comment &lt;/h1&gt; 

&lt;script src="https://utteranc.es/client.js"
        repo="officialrajdeepsingh/Minimalist-blog"
        issue-term="pathname"
        theme="github-light"
        crossorigin="anonymous"
        async&gt;
&lt;/script&gt;
&lt;/div&gt;
</code></pre>
<p>The <code>layouts/base.njk</code> file is the base file for our blog (which I've already explained). The <code>{{ title }}</code> and <code>{{description}}</code> both show the markdown file title and description. </p>
<p>To show tags on the article page, I used a for loop. I also showed single and multiple authors with the for loop. </p>
<p>To convert the date into a human-readable format, I used Lume date plugin and wrapped it with a date filter that looks like this: <code>{{ date | date('HUMAN_DATE') }}</code>. To show all markdown paragraphs, I used <code>{{ content | safe }}</code> . </p>
<p>For pagination, I used the Lume pagination plugin, and with the <code>search.previousPage(url, "type=article")</code> function, I showed the next and previous posts on the article page. For comments, I used <a class="post-section-overview" href="#heading-how-to-enable-comments">utteranc.es</a>.</p>
<h2 id="heading-how-to-generate-a-category-page">How to Generate a Category Page</h2>
<p>In Lume, you create a dynamic category based on article type. Lume also provides inbuilt functionality called a JavaScript template engine that helps you create a  dynamic page. It is similar to creating pagination functionality.</p>
<p>In Lume, there's a special file called <code>.tmpl.js</code> that helps you create a dynamic category.</p>
<pre><code class="lang-nunjucks">export const layout = "layouts/category.njk";

export default function* (props) {


  const { search }= props

  for (const category of search.values("category") ) {

    yield {
      url: `/category/${category}/`,
      title: `Categoryed ${category}`,
      type:"category",
      category,
    };

  }

}
</code></pre>
<p>In lume <code>search.values()</code> have a function that helps you find a category using markdown meta tags and sends data into the <code>layout/category.njk</code> file. It will generate all categories with the following URLs like  <code>/category/android/</code> , <code>/category/android-phone/</code> , <code>/category/human/</code> and so on.</p>
<h2 id="heading-how-to-generate-a-tag-page">How to Generate a Tag Page</h2>
<p>Generating a dynamic tags page is similar to a category. Lume provides a special <code>search.tags()</code> function to generate tags:</p>
<pre><code class="lang-nunjucks">export const layout = "layouts/tag.njk";

export default function* ({ search }) {

  for (const tag of search.tags()) {
    yield {
      url: `/tag/${tag}/`,
      title: `Tagged ${tag}`,
      type: "tag",
      tag,
    };
  }
}
</code></pre>
<p>The following code generates all tags with the following URLs like <code>/tag/android/</code>, <code>/tag/android-phone/</code>, <code>/tag/human/</code> and so on.</p>
<h2 id="heading-how-to-enable-search-functionality">How to Enable Search Functionality</h2>
<p>Lume has many in-built plugins which provide an excellent development experience. You can solve lots of problems with Lume plugins, and they allow you to add and remove features easily.</p>
<p>Lume provides inbuilt search functionality for the site. You enable it with the lume page find plugin.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/lume-serachbar-1.png" alt="Add a search bar in lume" width="600" height="400" loading="lazy">
<em>Add a search bar in lume</em></p>
<h3 id="heading-how-to-install-page-find">How to Install Page Find</h3>
<p>The Lume page finds plugin provides you with a search bar. Simply copy the following code and paste it into the <code>_config.ts</code> file and restart your server.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> pagefind <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/pagefind.ts"</span>;
</code></pre>
<h4 id="heading-how-to-configure-the-page-find-plugin">How to configure the page find plugin</h4>
<p>You configure the plugin in the <code>_config.ts</code> fil. You can also change the default config.</p>
<pre><code><span class="hljs-comment">// rest of code ...</span>
<span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;
<span class="hljs-keyword">import</span> pagefind <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/pagefind.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

<span class="hljs-comment">// config the pagefind plugin with default config</span>
site.use(pagefind());

 <span class="hljs-comment">// or </span>

<span class="hljs-comment">// change the default config in pagefind plugin</span>
site.use(pagefind({
  <span class="hljs-attr">ui</span>: {
    <span class="hljs-attr">containerId</span>: <span class="hljs-string">"search"</span>,
    <span class="hljs-attr">showImages</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">showEmptyFilters</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">resetStyles</span>: <span class="hljs-literal">true</span>,
  },
}));

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre><h2 id="heading-lume-seo">Lume SEO</h2>
<p>Lume has a plugin to help with SEO called metas. With the plugin, you can easily add various SEO-friendly configurations.</p>
<h3 id="heading-how-to-install-metas">How to install metas</h3>
<p>You install all plugins within the <code>config.ts</code> file. Copy the following code and paste it into the <code>config.ts</code> file, then restart the server.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> metas <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/metas.ts"</span>;
</code></pre>
<h4 id="heading-how-to-configure-metas">How to configure metas</h4>
<p>You can configure metas in various ways in the  <code>_config.ts</code> file. See the comments below:</p>
<pre><code><span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;

<span class="hljs-comment">// install metas plugin for SEO</span>
<span class="hljs-keyword">import</span> metas <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/metas.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

<span class="hljs-comment">// config the metas plugin with default config</span>
site.use(metas());

or

<span class="hljs-comment">// add custom config </span>
site.use(metas({
  <span class="hljs-attr">defaultPageData</span>: {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"title"</span>, <span class="hljs-comment">// Use the `title` value as fallback.</span>
  },
}));


<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre><h3 id="heading-how-to-use-the-metas-seo-plugin-in-lume">How to Use the Metas SEO Plugin in Lume</h3>
<p>To use the SEO metas plugin, you'll need to create a <code>_data.yml</code> file in the root of the project folder and paste the following code into it:</p>
<pre><code>metas:
  site: Minimalist blog
  <span class="hljs-attr">twitter</span>: <span class="hljs-string">"@Official_R_deep"</span>
  <span class="hljs-attr">icon</span>: <span class="hljs-regexp">/images/i</span>con.png
  <span class="hljs-attr">lang</span>: en
  <span class="hljs-attr">generator</span>: <span class="hljs-literal">true</span>

<span class="hljs-attr">mergedKeys</span>:
  metas: object
</code></pre><p>The following code helps you create all the various SEO tags for your website, and you can easily extend it with the <a target="_blank" href="https://lume.land/plugins/metas/">metas plugin</a> in Lume.</p>
<h3 id="heading-lume-sitemap">Lume Sitemap</h3>
<p>Lume has a plugin called <a target="_blank" href="https://lume.land/plugins/sitemap/">sitemap</a>. This plugin helps you create sitemaps for your blog. With Lume 13 you do not need to create a sitemap manually. </p>
<h4 id="heading-how-to-install-the-sitemap-plugin">How to install the sitemap plugin</h4>
<p>You install all plugins within the <code>config.ts</code> file. Copy the following code and paste it into the <code>config.ts</code> file, then restart the server.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> sitemap <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/sitemap.ts"</span>;
</code></pre>
<h4 id="heading-how-to-configure-the-sitemap-plugin">How to configure the sitemap plugin</h4>
<p>You can configure the sitemap plugin in various ways in the <code>_config.ts</code> file. See the comments below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;
<span class="hljs-keyword">import</span> sitemap <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/sitemap.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

site.use(sitemap());

<span class="hljs-comment">// or</span>

<span class="hljs-comment">// add custom config </span>
site.use(sitemap({
  <span class="hljs-attr">filename</span>: <span class="hljs-string">"my-sitemap.xml"</span>, <span class="hljs-comment">// to change the sitemap filename</span>
  <span class="hljs-attr">query</span>: <span class="hljs-string">"indexable=true"</span>, <span class="hljs-comment">// Select only pages with the indexable attribute as true</span>
  <span class="hljs-attr">sort</span>: <span class="hljs-string">"date=desc"</span>, <span class="hljs-comment">// To sort by data in ascendent order</span>
}));

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre>
<h3 id="heading-how-to-use-the-sitemap-plugin-in-lume">How to use the sitemap plugin in Lume</h3>
<p>You do not need any special file to use the site map plugin. Simply add the plugin after calling the plugin in <code>config.ts</code> and it'll start working on your site. This creates the <code>sitemap.xml</code> file and you can change the file name with a custom configuration in <code>_config.ts</code> file.</p>
<h3 id="heading-how-to-access-the-sitemap-on-the-website">How to access the sitemap on the website</h3>
<p>You can access the sitemap with the filename, for example by default in the localhost <code>[http://localhost:3000/sitemap.xml](http://localhost:3000/sitemap.xml)</code> and production <code>[http://my-domain-name/sitemap.xml](http://localhost:3000/sitemap.xml)</code> . </p>
<h2 id="heading-lume-plugins">Lume Plugins</h2>
<p>Lume comes with <a target="_blank" href="https://lume.land/plugins/?status=all">inbuilt plugins</a>, but you can easily add or remove features according to your requirements. You do not need all the stuff on your site – you can configure everything as you wish. </p>
<p>You can add more template engines, minify HTML, CSS, and JavaScript with plugins, enable code highlighting, date manipulation, image manipulation, SVG support, and more. </p>
<p>You can also easily create your own plugins with lume. <a target="_blank" href="https://lume.land/docs/advanced/plugins/">Lume also provides excellent documentation</a> where you can learn more.</p>
<h2 id="heading-how-to-enable-comments">How to Enable Comments</h2>
<p>To add comments on your Lume site, I think <a target="_blank" href="https://utteranc.es/">utteranc.es</a> is the best choice for all static site generators. utteranc.es is an open-source commenting system based on GitHub. It looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/enable-coments-on-minimalist-blog.deno-1.png" alt="Enable comment in lume" width="600" height="400" loading="lazy">
<em>Enable comment</em></p>
<p>If you want to enable comments on the site, the first step is to install an <a target="_blank" href="https://github.com/apps/utterances">utterances application</a> on GitHub. Then, copy and paste the following code into the article read file or where you show comments on the site. </p>
<pre><code class="lang-javascript">&lt;script src=<span class="hljs-string">"https://utteranc.es/client.js"</span>
        repo=<span class="hljs-string">"officialrajdeepsingh/Minimalist-blog"</span>
        issue-term=<span class="hljs-string">"pathname"</span>
        theme=<span class="hljs-string">"github-light"</span>
        crossorigin=<span class="hljs-string">"anonymous"</span>
        <span class="hljs-keyword">async</span>&gt;
&lt;/script&gt;
</code></pre>
<p>Next, you'll need to change the utterance comment script. The first change in the repo <code>repo="your-github-repo"</code> name is compulsory. The others are not. You can adjust according to your requirements – for example, changing the theme, issue term, and so on. </p>
<p>To read more about utterance, here's a <a target="_blank" href="https://joshcollinsworth.com/blog/add-blog-comments-static-site">great article written by Josh Collinsworth</a>.</p>
<p>The best approach is to add utterance comments in lume and then read the <a target="_blank" href="https://github.com/lumeland/lume/discussions/312">GitHub discussion</a>.</p>
<h2 id="heading-how-to-use-netlify-cms-with-lume">How to Use Netlify CMS with Lume</h2>
<p>Netlify CMS is an open-source content management system. You can easily integrate Netlify with Lume using the <a target="_blank" href="https://lume.land/plugins/netlify_cms/">netllify_cms</a> plugin. It is provided by Lume, and you just need to install it and copy/paste the code.</p>
<h3 id="heading-how-to-install-the-netlify-plugin">How to Install the Netlify Plugin</h3>
<p>Import the Netlify plugin in your <code>_config.ts</code> file to use it like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> lume <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/mod.ts"</span>;
<span class="hljs-keyword">import</span> netlifyCMS <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/plugins/netlify_cms.ts"</span>;

<span class="hljs-keyword">const</span> site = lume();

site.use(netlifyCMS());

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> site;
</code></pre>
<p>To configure it, you'll need to create a <code>/_data/netlify_cms.yml</code> file in the root level and then paste the following code after restarting your server:</p>
<pre><code class="lang-yml"><span class="hljs-attr">backend:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">git-gateway</span>
  <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span>

<span class="hljs-attr">media_folder:</span> <span class="hljs-string">statics</span>

<span class="hljs-attr">collections:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">label:</span> <span class="hljs-string">Posts</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">posts</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">List</span> <span class="hljs-string">of</span> <span class="hljs-string">posts</span>
    <span class="hljs-attr">folder:</span> <span class="hljs-string">posts</span>
    <span class="hljs-attr">extension:</span> <span class="hljs-string">md</span>
    <span class="hljs-attr">create:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">fields:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">label:</span> <span class="hljs-string">Title</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">title</span>
        <span class="hljs-attr">widget:</span> <span class="hljs-string">string</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">label:</span> <span class="hljs-string">Content</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">body</span>
        <span class="hljs-attr">widget:</span> <span class="hljs-string">markdown</span>
</code></pre>
<p>Netlify will ask you for permissions for the CMS proxy. Type <code>npx netlify-cms-proxy-server</code>  in a terminal, press enter or <code>type y</code>, and your Netlify CMS will start running locally on <a target="_blank" href="http://localhost:3000/admin">http://localhost:3000/admin</a> URL. Now your Lume blog is ready for deployment on Netlify. </p>
<h2 id="heading-how-to-deploy-your-blog-with-deno-deploy">How to Deploy Your Blog with Deno Deploy</h2>
<p>You can deploy Lume on various platforms such as Deno Deploy, GitHub Pages, Gitlab Pages, Netlify, Vercel, Fleek, AWS Amplify, and Cloudflare Pages. Lume also provides <a target="_blank" href="https://lume.land/docs/advanced/deployment/">excellent documentation on deployment</a>. </p>
<p>In this article, I'm deploying my Lume blog with Deno Deploy (and we'll also see how to do it with GitHub pages). Deno Deploy is an official platform built by the Deno team to deploy Deno-based applications.</p>
<p>Before deploying your Lume blog on Deno Deploy, make sure you create a <code>server.ts</code> file in the root level.</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">import</span> Server <span class="hljs-keyword">from</span> <span class="hljs-string">"lume/core/server.ts"</span>;

<span class="hljs-keyword">const</span> server = <span class="hljs-keyword">new</span> Server({
  <span class="hljs-attr">port</span>: <span class="hljs-number">8000</span>,
  <span class="hljs-attr">root</span>: <span class="hljs-string">`<span class="hljs-subst">${Deno.cwd()}</span>/_site`</span>,
});

server.start();

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Listening on http://localhost:8000"</span>);
</code></pre>
<h4 id="heading-deployment-steps">Deployment Steps:</h4>
<ol>
<li>Create an account on Deno Deploy.</li>
<li>Push your local code to GitHub and then select the <code>server.ts</code> file. Deno Deploy automatically creates a site based on the <code>server.ts</code> the file.</li>
<li>Make sure to first create a custom <code>server.ts</code> file. Then move to the next step.</li>
<li>The easiest way to deploy your site is with GitHub Actions. Create a new <code>.github/workflows/deno.yml</code> file in your project root level and paste the following code into it:</li>
</ol>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span> <span class="hljs-comment"># Needed for auth with Deno Deploy</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># Needed to clone the repository</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Clone</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> add a build step here</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">to</span> <span class="hljs-string">Deno</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">denoland/deployctl@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">project:</span> <span class="hljs-string">"minimalist-blog"</span>
          <span class="hljs-attr">entrypoint:</span> <span class="hljs-string">"./serve.ts"</span>
</code></pre>
<h2 id="heading-how-to-deploy-your-blog-with-github-pages">How to Deploy Your Blog with Github Pages</h2>
<p>GitHub Pages are free static sites you can use to host pages. You can also deploy your Lume blog it. The process of deployment is pretty easy. </p>
<p>To deploy Lume on GitHub pages you need to have GitHub Actions set up. </p>
<h4 id="heading-deployment-steps-1">Deployment Steps</h4>
<ol>
<li>It's best if you have a GitHub repository so you can convert your local website to GitHub Pages.</li>
<li>Create a new repo and push all your local code into it.</li>
<li>Create a new <code>.github/workflows/deno.yml</code> in your project root level, then paste the following code into it and push it into the GitHub repo. The GitHub action runs based on the <code>github.yml</code> action and it generates a GitHub page. </li>
</ol>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">Publish</span> <span class="hljs-string">on</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">main</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Clone</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Deno</span> <span class="hljs-string">environment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">denoland/setup-deno@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">deno-version:</span> <span class="hljs-string">v1.x</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">site</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">deno</span> <span class="hljs-string">task</span> <span class="hljs-string">build</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">crazy-max/ghaction-github-pages@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">build_dir:</span> <span class="hljs-string">_site</span>
        <span class="hljs-attr">env:</span>
          <span class="hljs-attr">GITHUB_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.MY_GITHUB_TOKEN_PAGE</span> <span class="hljs-string">}}</span>
</code></pre>
<p>You need a GitHub token to deploy your Lume website to GitHub pages. This is a required part of the setup. I found <a target="_blank" href="https://dev.to/github/the-githubtoken-in-github-actions-how-it-works-change-permissions-customizations-3cgp">a great article written by Davide</a> that can help you learn more about GitHub Actions and how to create one.</p>
<p>GitHub Actions takes two or three minutes to finish hosting your website on GitHub Pages. </p>
<p>Check out the <a target="_blank" href="https://github.com/officialrajdeepsingh/minimalist-blog-github-page">GitHub repository</a> to learn how to configure the GitHub workflow for GitHub pages. You can also see a live demo <a target="_blank" href="https://officialrajdeepsingh.github.io/minimalist-blog-github-page/">website on the GitHub page</a>.</p>
<p>A quick note: if you deploy your Lume site on GitHub pages and your image does not show on the website, there are two possible reasons for this:</p>
<ol>
<li>If all image names aren't in lowercase, you might get an error. To resolve the error, convert your image names into lowercase with this command: <code>your.github.com/your-reponame/images/my-image.png</code></li>
<li>If you're using the <code>base_path</code> and <code>relative_urls</code> Lume plugins in your project and <code>relative_urls</code> is redundant, and then you'll need to remove the <code>relative_urls</code> plugin in your project. Your image should now work fine.</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Lume is an easy-to-learn and feature-rich static site generator. You can do anything you imagine with it. Lume gives you a lot of freedom with the code.</p>
<p>The Lume community is not as big as those of Hugo, 11ty, Jekyll, and other tools. But, the Lume maintainers actively reply to everybody who comments in the GitHub discussion. Without a strong community, this tool should be able to create a strong impact.</p>
<p>One challenge with Lume is that it's tough to get started for beginners and is more suited to intermediate and advanced developers. If you're jumping right into using Lume as a beginner, you might struggle with a lack of background knowledge about how static site generators work. </p>
<p>Because of this, it's helpful to have a <strong>little bit of knowledge</strong> about Nuckjunks, JSX, and other template engines that work based on markdown. Once you gain this experience, then you'll easily be able to use Lume to design your markdown-based blog. </p>
<p>I recommend using the <a target="_blank" href="https://lume.land/plugins/mdx/">lume MDX plugin</a> for markdown. You can use JSX-based components inside the markdown file, and you can create beautiful code blocks, tip blocks, and so on.</p>
<p>I highly encourage all developers to try Lume out. If you have problems with Lume, you can reach out to its creator on the <a target="_blank" href="https://github.com/lumeland/lume/discussions">GitHub discussion</a> and the <a target="_blank" href="https://discord.com/invite/YbTmpACHWB">Discord server</a>. </p>
<p>If your course is about Computer science, Bioinformatics, and Biotechnology. You can join my free <a target="_blank" href="https://www.getrevue.co/profile/officialrajdeepsingh">newsletter</a>. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn the Eleventy Static Site Generator by Building and Deploying a Portfolio Website ]]>
                </title>
                <description>
                    <![CDATA[ What is Eleventy? Eleventy (also called 11ty) is a simple yet powerful static site generator. It uses JavaScript to transform data and templates into HTML pages. It’s beginner-friendly, has fast build times, and generates fast sites by default. It al... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-eleventy/</link>
                <guid isPermaLink="false">66d45ee1787a2a3b05af43aa</guid>
                
                    <category>
                        <![CDATA[ eleventy ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gerard Hynes ]]>
                </dc:creator>
                <pubDate>Tue, 06 Sep 2022 16:26:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/09/learn-eleventy.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="heading-what-is-eleventy">What is Eleventy?</h2>
<p>Eleventy (also called 11ty) is a simple yet powerful static site generator. It uses JavaScript to transform data and templates into HTML pages.</p>
<p>It’s beginner-friendly, has fast build times, and generates fast sites by default. It also has a very active and friendly community.</p>
<p>Eleventy excels at content-driven sites and is used by <a target="_blank" href="https://web.dev/">Google</a>, <a target="_blank" href="https://www.netlify.com/">Netlify</a>, <a target="_blank" href="https://digitalhumanities.mit.edu/">MIT</a>, <a target="_blank" href="https://worldwideweb.cern.ch/">CERN</a>, <a target="_blank" href="https://www.a11yproject.com/">the A11y Project</a>, <a target="_blank" href="https://eslint.org/">ESLint,</a> and more.</p>
<p>Since pages are generated ahead of time, they can be served as fast as possible from a Content Delivery Network (CDN). Eleventy also generates no client-side JavaScript, which helps your site to load faster.</p>
<p>In this tutorial, we're going to build a simple developer portfolio site to demonstrate some of the main features of Eleventy.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-29-at-17-46-26-Eleventy-Portfolio.png" alt="Eleventy portfolio homepage" width="600" height="400" loading="lazy"></p>
<p><em>Eleventy portfolio homepage</em></p>
<p>We’ll learn about:</p>
<ol>
<li><p>Setting up and configuring an Eleventy project</p>
</li>
<li><p>Templating and layouts</p>
</li>
<li><p>Handling CSS and images</p>
</li>
<li><p>Working with collections and data files</p>
</li>
<li><p>Shortcodes and Eleventy plugins</p>
</li>
<li><p>Deploying the site to Netlify</p>
</li>
</ol>
<p>The portfolio site will have:</p>
<ul>
<li><p>A Homepage</p>
</li>
<li><p>An About page</p>
</li>
<li><p>A Contact page (with contact form)</p>
</li>
<li><p>A Projects page</p>
</li>
<li><p>A page for each project (with case study)</p>
</li>
</ul>
<p>Eleventy can pull in data from APIs, a Content Management System (CMS), or from local files. To keep things simple, we’ll store our project data in Markdown files.</p>
<p>The full code for the finished portfolio is <a target="_blank" href="https://github.com/gerhynes/eleventy-portfolio">available on GitHub</a>. If you get stuck at any stage, please check your code against the finished site.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisite-install-nodejs">Prerequisite - Install Node.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-initial-project-setup">Initial Project Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-the-project">How to Configure the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-a-template">How to Add a Template</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-templates-in-eleventy">How to Use Templates in Eleventy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-layouts-in-eleventy">How to Use Layouts in Eleventy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-the-css-and-images">How to Configure the CSS and Images</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-partials-in-eleventy">How to Use Partials in Eleventy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-collections-in-eleventy">How to Use Collections in Eleventy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-directory-data-files">How to Use Directory Data Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-collections-in-templates">How to Use Collections in Templates</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-shortcodes">How to Use Shortcodes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-eleventy-image-plugin">How to Use the Eleventy Image Plugin</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-a-build-contact-form-with-netlify-forms">How to Build a Contact Form with Netlify Forms</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-deploy-to-netlify">How to Deploy to Netlify</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-where-to-take-it-from-here">Where to Take it from Here</a></p>
</li>
</ol>
<h3 id="heading-prerequisite-install-nodejs">Prerequisite - Install Node.js</h3>
<p>If you don’t already have Node.js installed, go to <a target="_blank" href="https://nodejs.org/en/">nodejs.org</a> and follow the instructions for your operating system.</p>
<p>Open a terminal and use <code>node --version</code> to make sure it’s installed. As long as it’s version 12 or newer, you’re good to go.</p>
<h2 id="heading-initial-project-setup">Initial Project Setup</h2>
<p>First, create a directory for your portfolio. You can call it <code>eleventy-portfolio</code> or whatever you want.</p>
<p>Open this directory in a terminal and run <code>npm init -y</code> to create a <code>package.json</code> file with the default settings.</p>
<p>Next, install Eleventy using <code>npm install --save-dev @11ty/eleventy</code>.</p>
<p>In the root directory of the project, create a <code>.gitignore</code> file with the following contents so that Git doesn’t track any unwanted files:</p>
<pre><code class="lang-python">node_modules
/public
</code></pre>
<h2 id="heading-how-to-configure-the-project">How to Configure the Project</h2>
<p>Eleventy is “zero-config” by default. If you don’t change anything, Eleventy will take all the files in your root directory, run a build process, and output the resulting files to a <code>_site</code> directory.</p>
<p>But Eleventy also has flexible configuration options that let you customize your build process, watch for changes in certain file types, and manipulate content with filters and shortcodes.</p>
<p>Your Eleventy configuration goes in a <code>.eleventy.js</code> file in the root of your project.</p>
<p>For example, the default input directory is the root directory of your project, while the default output directory is <code>_site</code>. Some people prefer to change this, with <code>src</code> and <code>public</code> being common choices.</p>
<p>If you’d like this structure, create <code>src</code> and <code>public</code> directories in the root of your project and then set them as the input and output directories in <code>.eleventy.js</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">eleventyConfig</span>) </span>{
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">dir</span>: {
      <span class="hljs-attr">input</span>: <span class="hljs-string">"src"</span>,
      <span class="hljs-attr">output</span>: <span class="hljs-string">"public"</span>
    }
  };
};
</code></pre>
<p>In case you’re wondering, the <code>eleventyConfig</code> argument that is passed to the function is the default configuration object that Eleventy provides. Soon we’ll use this object to customize our Eleventy build process.</p>
<h2 id="heading-how-to-add-a-template">How to Add a Template</h2>
<p>Let’s add our first template. We’ll keep things as simple as possible by using a Markdown file.</p>
<p>In the <code>src</code> directory, create an <code>index.md</code> with <code># Hello World from Eleventy</code> as its contents. This is your first Eleventy template.</p>
<p>To build and view the site we can use the development server that comes with Eleventy.</p>
<p>In your terminal, make sure you’re in the root directory of your project and run <code>eleventy --serve</code>. This starts the development server, which will watch your <code>src</code> directory and automatically reload your site whenever you change your code.</p>
<p>After a moment you’ll see:</p>
<pre><code class="lang-python">[Browsersync] Access URLs:
 ----------------------------------
    Local: http://localhost:<span class="hljs-number">8080</span>
 External: http://your_ip_address:<span class="hljs-number">8080</span>
 ----------------------------------
[Browsersync] Serving files <span class="hljs-keyword">from</span>: public
</code></pre>
<p>Open a web browser and go to <a target="_blank" href="http://localhost:8080"><code>http://localhost:8080</code></a>. Congratulations, you’ve made a (very simple) Eleventy site! 🥳🎉</p>
<p>At this stage, your project will have the following structure:</p>
<pre><code class="lang-python">node_modules/
public/
src/
.eleventy.js
.gitignore
package.lock.json
package.json
</code></pre>
<p>Most sites need more than one page, so we’re going to need to learn more about <strong>templates</strong>.</p>
<p>Before we do that, we can customize our build commands if we want to. This step is entirely optional.</p>
<h3 id="heading-optional-step-how-to-create-custom-build-commands">Optional Step – How to Create Custom Build Commands</h3>
<p>The default command to run the development server is <code>eleventy --serve</code>, while the default command to build the site is <code>eleventy</code>.</p>
<p>If you’d like to replace these with different commands, such as <code>start</code> and <code>build</code>, open <code>package.json</code> and under <code>scripts</code> replace the “test” command with your preferred commands:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"eleventy --serve"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"eleventy"</span>
  },
</code></pre>
<p>Now we can use <code>npm start</code> in the terminal to start the development server and <code>npm run build</code> to generate a build of our site.</p>
<p>You can use <code>ctrl/cmd</code> + <code>c</code> to stop the development server whenever you need to.</p>
<h2 id="heading-how-to-use-templates-in-eleventy">How to Use Templates in Eleventy</h2>
<p>Turning Markdown files into HTML is neat, but so far you aren’t really getting much benefit over just writing your site in plain HTML. This is where <strong>templates</strong> come in.</p>
<p>First, we need to clarify some terms:</p>
<ul>
<li><p><strong>Template –</strong> A content file that Eleventy will transform into a page, or pages, in the built site</p>
</li>
<li><p><strong>Layout –</strong> A template that wraps around another template, usually to provide a structure to present content in</p>
</li>
<li><p><strong>Partial –</strong> A template that makes up part of another template</p>
</li>
</ul>
<p>Templates let you combine content and data to generate whatever HTML your site needs.</p>
<p>Layouts let you give multiple templates the same basic structure.</p>
<p>Partials let you build small reusable components that you can use in larger templates.</p>
<p>Eleventy supports ten different languages for templating, including: HTML, Markdown, JavaScript, Liquid, Nunjucks, Handlebars, Mustache, EJS, Haml, and Pug. (In version 1.0 Eleventy added support for custom templates using any arbitrary file extension, but this is probably better reserved for more custom/advanced use cases).</p>
<p>You can even mix different templating languages in the same file, such as Markdown and Nunjucks, if you want to.</p>
<p>In this project, we’ll use <a target="_blank" href="https://mozilla.github.io/nunjucks/">Nunjucks</a>. It’s a templating language for JavaScript created by Mozilla, and is pretty popular in the Eleventy community.</p>
<p>In the <code>src</code> directory, delete <code>index.md</code> and create an <code>index.njk</code> file. If you’re using VS Code, type <code>!</code> + <code>tab</code> to generate the basic HTML structure for the page. Change the title to "Eleventy Portfolio" and in the <code>&lt;body&gt;</code> element, add <code>&lt;h1&gt;Home Page&lt;/h1&gt;</code>.</p>
<p>Your page should look like this:</p>
<pre><code class="lang-python">&lt;!DOCTYPE html&gt;
&lt;html lang=<span class="hljs-string">"en"</span>&gt;
&lt;head&gt;
  &lt;meta charset=<span class="hljs-string">"UTF-8"</span>&gt;
  &lt;meta http-equiv=<span class="hljs-string">"X-UA-Compatible"</span> content=<span class="hljs-string">"IE=edge"</span>&gt;
  &lt;meta name=<span class="hljs-string">"viewport"</span> content=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;
  &lt;title&gt;Eleventy Portfolio&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Home Page&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Next, still in <code>src</code>, create <code>about.njk</code> and <code>contact.njk</code> files. You can copy the contents of <code>index.njk</code> into them and replace the <code>&lt;h1&gt;</code> with <code>&lt;h1&gt;About Page&lt;/h1&gt;</code> and <code>&lt;h1&gt;Contact Page&lt;/h1&gt;</code> respectively.</p>
<p>Start your dev server. if it isn’t already running. Go to <a target="_blank" href="http://localhost:8080"><code>http://localhost:8080</code></a> to see the homepage, <code>http://localhost:8080/about</code> for the About page, and <code>http://localhost:8080/contact</code> for the Contact page.</p>
<p>In our portfolio site, each of these pages is going to have the same basic layout. So instead of writing the same code in each page template, we’re going to use Eleventy <strong>layouts</strong>.</p>
<h2 id="heading-how-to-use-layouts-in-eleventy">How to Use Layouts in Eleventy</h2>
<p>Layouts are templates that wrap around other templates, presenting the content in a consistent way.</p>
<p>Inside the <code>src</code> directory, create a <code>_includes</code> directory. This is going to contain all our layouts and partials.</p>
<p>Inside <code>_includes</code>, create a <code>base.njk</code> file. This will provide a standard layout for every page of our site.</p>
<p>Copy the following code into <code>base.njk</code>:</p>
<pre><code class="lang-python">&lt;!DOCTYPE html&gt;
&lt;html lang=<span class="hljs-string">"en"</span>&gt;
&lt;head&gt;
  &lt;meta charset=<span class="hljs-string">"UTF-8"</span>&gt;
  &lt;meta http-equiv=<span class="hljs-string">"X-UA-Compatible"</span> content=<span class="hljs-string">"IE=edge"</span>&gt;
  &lt;meta name=<span class="hljs-string">"viewport"</span> content=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;
  &lt;meta name=<span class="hljs-string">"description"</span> content=<span class="hljs-string">"I'm a Frontend software developer who builds sites and apps that help people reach their personal and professional goals."</span>/&gt;
  &lt;title&gt;{{ title }}&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">content</span>"&gt;
    &lt;<span class="hljs-title">header</span> <span class="hljs-title">class</span>="<span class="hljs-title">header</span> <span class="hljs-title">container</span>"&gt;
    &lt;<span class="hljs-title">h1</span> <span class="hljs-title">class</span>="<span class="hljs-title">header__title</span>"&gt;
      &lt;<span class="hljs-title">a</span> <span class="hljs-title">href</span>="/"&gt;<span class="hljs-title">Marie</span> <span class="hljs-title">Jackson</span>&lt;/<span class="hljs-title">a</span>&gt;
    &lt;/<span class="hljs-title">h1</span>&gt;
    &lt;<span class="hljs-title">ul</span> <span class="hljs-title">class</span>="<span class="hljs-title">header__links</span>"&gt;
      &lt;<span class="hljs-title">li</span>&gt;
        &lt;<span class="hljs-title">a</span> <span class="hljs-title">class</span>="<span class="hljs-title">header__link</span>" <span class="hljs-title">href</span>="/<span class="hljs-title">about</span>"&gt;<span class="hljs-title">About</span>&lt;/<span class="hljs-title">a</span>&gt;
      &lt;/<span class="hljs-title">li</span>&gt;
      &lt;<span class="hljs-title">li</span>&gt;
        &lt;<span class="hljs-title">a</span> <span class="hljs-title">class</span>="<span class="hljs-title">header__link</span>" <span class="hljs-title">href</span>="/<span class="hljs-title">projects</span>"&gt;<span class="hljs-title">Projects</span>&lt;/<span class="hljs-title">a</span>&gt;
      &lt;/<span class="hljs-title">li</span>&gt;
      &lt;<span class="hljs-title">li</span>&gt;
        &lt;<span class="hljs-title">a</span> <span class="hljs-title">class</span>="<span class="hljs-title">header__link</span>" <span class="hljs-title">href</span>="/<span class="hljs-title">contact</span>"&gt;<span class="hljs-title">Contact</span>&lt;/<span class="hljs-title">a</span>&gt;
      &lt;/<span class="hljs-title">li</span>&gt;
    &lt;/<span class="hljs-title">ul</span>&gt;
    &lt;/<span class="hljs-title">header</span>&gt;
    &lt;<span class="hljs-title">main</span> <span class="hljs-title">class</span>="<span class="hljs-title">main</span> <span class="hljs-title">container</span>"&gt;
      {{ <span class="hljs-title">content</span> | <span class="hljs-title">safe</span> }}
    &lt;/<span class="hljs-title">main</span>&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">footer</span> <span class="hljs-title">class</span>="<span class="hljs-title">footer</span>"&gt;
      &lt;<span class="hljs-title">p</span>&gt;&amp;<span class="hljs-title">copy</span>; <span class="hljs-title">Marie</span> <span class="hljs-title">Jackson</span> 2022&lt;/<span class="hljs-title">p</span>&gt;
  &lt;/<span class="hljs-title">footer</span>&gt;
&lt;/<span class="hljs-title">body</span>&gt;
&lt;/<span class="hljs-title">html</span>&gt;</span>
</code></pre>
<p>The <code>content</code> value will be the main content of whichever template we use with <code>base.njk</code> as its layout. <code>safe</code> is a filter that prevents this content from being escaped (having potentially unsafe characters replaced).</p>
<p>Now, change <code>index.njk</code> to be:</p>
<pre><code class="lang-python">---
title: <span class="hljs-string">"Eleventy Portfolio"</span>
layout: <span class="hljs-string">"base.njk"</span>
---

&lt;h1&gt;{{ title }} Home Page&lt;/h1&gt;
</code></pre>
<p>Notice how the template has frontmatter data at the top of the file. By default this is written in YAML, but you can use other languages too.</p>
<p>This frontmatter lets you set values for your templates. In this case, the <code>layout</code> value tells the template to use the <code>base.njk</code> layout and the <code>title</code> value provides a title that we are using in our template's <code>&lt;h1&gt;</code> tag.</p>
<p>Next, delete everything from <code>about.njk</code> and paste in the following content:</p>
<pre><code class="lang-python">---
title: <span class="hljs-string">"Eleventy Portfolio"</span>
layout: <span class="hljs-string">"base.njk"</span>
---

&lt;section <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">bio</span> <span class="hljs-title">prose</span>"&gt;
  &lt;<span class="hljs-title">h2</span> <span class="hljs-title">class</span>="<span class="hljs-title">heading</span>--<span class="hljs-title">main</span>"&gt;<span class="hljs-title">My</span> <span class="hljs-title">story</span>&lt;/<span class="hljs-title">h2</span>&gt;
  &lt;<span class="hljs-title">p</span>&gt;<span class="hljs-title">Lorem</span> <span class="hljs-title">ipsum</span> <span class="hljs-title">dolor</span> <span class="hljs-title">sit</span> <span class="hljs-title">amet</span>, <span class="hljs-title">consectetur</span> <span class="hljs-title">adipiscing</span> <span class="hljs-title">elit</span>, <span class="hljs-title">sed</span> <span class="hljs-title">do</span> <span class="hljs-title">eiusmod</span> <span class="hljs-title">tempor</span> <span class="hljs-title">incididunt</span> <span class="hljs-title">ut</span> <span class="hljs-title">labore</span> <span class="hljs-title">et</span> <span class="hljs-title">dolore</span> <span class="hljs-title">magna</span> <span class="hljs-title">aliqua</span>. <span class="hljs-title">Aliquet</span> <span class="hljs-title">risus</span> <span class="hljs-title">feugiat</span> <span class="hljs-title">in</span> <span class="hljs-title">ante</span> <span class="hljs-title">metus</span> <span class="hljs-title">dictum</span>.&lt;/<span class="hljs-title">p</span>&gt;

  &lt;<span class="hljs-title">p</span>&gt;<span class="hljs-title">Tellus</span> <span class="hljs-title">pellentesque</span> <span class="hljs-title">eu</span> <span class="hljs-title">tincidunt</span> <span class="hljs-title">tortor</span> <span class="hljs-title">aliquam</span> <span class="hljs-title">nulla</span> <span class="hljs-title">facilisi</span> <span class="hljs-title">cras</span> <span class="hljs-title">fermentum</span>. <span class="hljs-title">Turpis</span> <span class="hljs-title">egestas</span> <span class="hljs-title">integer</span> <span class="hljs-title">eget</span> <span class="hljs-title">aliquet</span>. <span class="hljs-title">Vestibulum</span> <span class="hljs-title">morbi</span> <span class="hljs-title">blandit</span> <span class="hljs-title">cursus</span> <span class="hljs-title">risus</span> <span class="hljs-title">at</span> <span class="hljs-title">ultrices</span> <span class="hljs-title">mi</span> <span class="hljs-title">tempus</span>. <span class="hljs-title">Ut</span> <span class="hljs-title">lectus</span> <span class="hljs-title">arcu</span> <span class="hljs-title">bibendum</span> <span class="hljs-title">at</span>. <span class="hljs-title">Integer</span> <span class="hljs-title">enim</span> <span class="hljs-title">neque</span> <span class="hljs-title">volutpat</span> <span class="hljs-title">ac</span> <span class="hljs-title">tincidunt</span>.&lt;/<span class="hljs-title">p</span>&gt;

  &lt;<span class="hljs-title">p</span>&gt;<span class="hljs-title">Commodo</span> <span class="hljs-title">ullamcorper</span> <span class="hljs-title">a</span> <span class="hljs-title">lacus</span> <span class="hljs-title">vestibulum</span> <span class="hljs-title">sed</span> <span class="hljs-title">arcu</span>. <span class="hljs-title">Et</span> <span class="hljs-title">tortor</span> <span class="hljs-title">consequat</span> <span class="hljs-title">id</span> <span class="hljs-title">porta</span> <span class="hljs-title">nibh</span> <span class="hljs-title">venenatis</span> <span class="hljs-title">cras</span> <span class="hljs-title">sed</span>. <span class="hljs-title">Nulla</span> <span class="hljs-title">pharetra</span> <span class="hljs-title">diam</span> <span class="hljs-title">sit</span> <span class="hljs-title">amet</span> <span class="hljs-title">nisl</span>. <span class="hljs-title">Ipsum</span> <span class="hljs-title">nunc</span> <span class="hljs-title">aliquet</span> <span class="hljs-title">bibendum</span> <span class="hljs-title">enim</span> <span class="hljs-title">facilisis</span> <span class="hljs-title">gravida</span> <span class="hljs-title">neque</span> <span class="hljs-title">convallis</span> <span class="hljs-title">a</span>. <span class="hljs-title">Nec</span> <span class="hljs-title">sagittis</span> <span class="hljs-title">aliquam</span> <span class="hljs-title">malesuada</span> <span class="hljs-title">bibendum</span>.&lt;/<span class="hljs-title">p</span>&gt;

  &lt;<span class="hljs-title">p</span>&gt;<span class="hljs-title">Tellus</span> <span class="hljs-title">pellentesque</span> <span class="hljs-title">eu</span> <span class="hljs-title">tincidunt</span> <span class="hljs-title">tortor</span> <span class="hljs-title">aliquam</span> <span class="hljs-title">nulla</span> <span class="hljs-title">facilisi</span> <span class="hljs-title">cras</span> <span class="hljs-title">fermentum</span>. <span class="hljs-title">Turpis</span> <span class="hljs-title">egestas</span> <span class="hljs-title">integer</span> <span class="hljs-title">eget</span> <span class="hljs-title">aliquet</span>. <span class="hljs-title">Vestibulum</span> <span class="hljs-title">morbi</span> <span class="hljs-title">blandit</span> <span class="hljs-title">cursus</span> <span class="hljs-title">risus</span> <span class="hljs-title">at</span> <span class="hljs-title">ultrices</span> <span class="hljs-title">mi</span> <span class="hljs-title">tempus</span>. <span class="hljs-title">Ut</span> <span class="hljs-title">lectus</span> <span class="hljs-title">arcu</span> <span class="hljs-title">bibendum</span> <span class="hljs-title">at</span>. <span class="hljs-title">Integer</span> <span class="hljs-title">enim</span> <span class="hljs-title">neque</span> <span class="hljs-title">volutpat</span> <span class="hljs-title">ac</span> <span class="hljs-title">tincidunt</span>.&lt;/<span class="hljs-title">p</span>&gt;

  &lt;<span class="hljs-title">p</span>&gt;<span class="hljs-title">Commodo</span> <span class="hljs-title">ullamcorper</span> <span class="hljs-title">a</span> <span class="hljs-title">lacus</span> <span class="hljs-title">vestibulum</span> <span class="hljs-title">sed</span> <span class="hljs-title">arcu</span>. <span class="hljs-title">Et</span> <span class="hljs-title">tortor</span> <span class="hljs-title">consequat</span> <span class="hljs-title">id</span> <span class="hljs-title">porta</span> <span class="hljs-title">nibh</span> <span class="hljs-title">venenatis</span> <span class="hljs-title">cras</span> <span class="hljs-title">sed</span>. <span class="hljs-title">Nulla</span> <span class="hljs-title">pharetra</span> <span class="hljs-title">diam</span> <span class="hljs-title">sit</span> <span class="hljs-title">amet</span> <span class="hljs-title">nisl</span>. <span class="hljs-title">Ipsum</span> <span class="hljs-title">nunc</span> <span class="hljs-title">aliquet</span> <span class="hljs-title">bibendum</span> <span class="hljs-title">enim</span> <span class="hljs-title">facilisis</span> <span class="hljs-title">gravida</span> <span class="hljs-title">neque</span> <span class="hljs-title">convallis</span> <span class="hljs-title">a</span>. <span class="hljs-title">Nec</span> <span class="hljs-title">sagittis</span> <span class="hljs-title">aliquam</span> <span class="hljs-title">malesuada</span> <span class="hljs-title">bibendum</span>.&lt;/<span class="hljs-title">p</span>&gt;
&lt;/<span class="hljs-title">section</span>&gt;</span>
</code></pre>
<p>Now delete everything from <code>contact.njk</code> and paste in this content:</p>
<pre><code class="lang-python">---
title: <span class="hljs-string">"Eleventy Portfolio"</span>
layout: <span class="hljs-string">"base.njk"</span>
---

&lt;h2 <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">heading</span>--<span class="hljs-title">main</span> <span class="hljs-title">text</span>-<span class="hljs-title">center</span>"&gt;<span class="hljs-title">Want</span> <span class="hljs-title">to</span> <span class="hljs-title">get</span> <span class="hljs-title">in</span> <span class="hljs-title">touch</span>?&lt;/<span class="hljs-title">h2</span>&gt;
&lt;<span class="hljs-title">p</span> <span class="hljs-title">class</span>="<span class="hljs-title">contact__sub</span>-<span class="hljs-title">heading</span> <span class="hljs-title">text</span>-<span class="hljs-title">center</span>"&gt;<span class="hljs-title">I</span>'<span class="hljs-title">m</span> <span class="hljs-title">always</span> <span class="hljs-title">open</span> <span class="hljs-title">to</span> <span class="hljs-title">new</span> <span class="hljs-title">opportunities</span> <span class="hljs-title">and</span> <span class="hljs-title">projects</span>. &lt;/<span class="hljs-title">p</span>&gt;

&lt;<span class="hljs-title">form</span> <span class="hljs-title">class</span>="<span class="hljs-title">form</span>" <span class="hljs-title">name</span>="<span class="hljs-title">contact</span>" <span class="hljs-title">action</span>="/<span class="hljs-title">success</span>" <span class="hljs-title">method</span>="<span class="hljs-title">POST</span>" <span class="hljs-title">data</span>-<span class="hljs-title">netlify</span>="<span class="hljs-title">true</span>"&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__section</span>"&gt;
    &lt;<span class="hljs-title">label</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__label</span>" <span class="hljs-title">for</span>="<span class="hljs-title">yourName</span>"&gt;<span class="hljs-title">Name</span>&lt;/<span class="hljs-title">label</span>&gt;
    &lt;<span class="hljs-title">input</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__input</span>" <span class="hljs-title">name</span>="<span class="hljs-title">name</span>" <span class="hljs-title">type</span>="<span class="hljs-title">text</span>" <span class="hljs-title">id</span>="<span class="hljs-title">yourName</span>" <span class="hljs-title">required</span>="<span class="hljs-title">true</span>"&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__section</span>"&gt;
    &lt;<span class="hljs-title">label</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__label</span>" <span class="hljs-title">for</span>="<span class="hljs-title">yourEmail</span>"&gt;<span class="hljs-title">Email</span>&lt;/<span class="hljs-title">label</span>&gt;
    &lt;<span class="hljs-title">input</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__input</span>" <span class="hljs-title">name</span>="<span class="hljs-title">email</span>" <span class="hljs-title">type</span>="<span class="hljs-title">email</span>"  <span class="hljs-title">id</span>="<span class="hljs-title">yourEmail</span>" <span class="hljs-title">required</span>="<span class="hljs-title">true</span>"&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__section</span>"&gt;
    &lt;<span class="hljs-title">label</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__label</span>" <span class="hljs-title">for</span>="<span class="hljs-title">message</span>"&gt;<span class="hljs-title">Message</span>&lt;/<span class="hljs-title">label</span>&gt;
    &lt;<span class="hljs-title">textarea</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__input</span>" <span class="hljs-title">name</span>="<span class="hljs-title">message</span>" <span class="hljs-title">id</span>="<span class="hljs-title">message</span>" <span class="hljs-title">rows</span>="4" <span class="hljs-title">required</span>="<span class="hljs-title">true</span>"&gt;&lt;/<span class="hljs-title">textarea</span>&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
    &lt;<span class="hljs-title">button</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__button</span>" <span class="hljs-title">type</span>="<span class="hljs-title">submit</span>"&gt;<span class="hljs-title">Let</span>'<span class="hljs-title">s</span> <span class="hljs-title">talk</span>&lt;/<span class="hljs-title">button</span>&gt;
&lt;/<span class="hljs-title">form</span>&gt;</span>
</code></pre>
<p>We’re going to learn about how this contact form will work later in the tutorial.</p>
<p>Our portfolio is starting to take shape, even though things still look really bare. Let's get our CSS and images working next.</p>
<h2 id="heading-how-to-configure-the-css-and-images">How to Configure the CSS and Images</h2>
<p>While Eleventy can understand supported templating languages out of the box, it needs to be configured to process CSS and image files. Fortunately, this doesn’t require much configuration. While we’re at it, we’re also going to add a favicon to the site.</p>
<p>Inside the <code>src</code> directory, create three folders: <code>css</code>, <code>images</code>, and <code>favicons</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/src.PNG" alt="src directory structure" width="600" height="400" loading="lazy"></p>
<p><em>src directory structure</em></p>
<p>Inside the <code>css</code> directory, make a <code>style.css</code> file. Since this isn’t a CSS tutorial, I’m going to provide the CSS in <a target="_blank" href="https://github.com/gerhynes/eleventy-portfolio/tree/main/src">the GitHub repo for the project</a>. You can copy and paste it from there, but I’m not going to cover the CSS in any depth.</p>
<p>The images for this portfolio are also available in the <code>images</code> directory in the GitHub repo. Copy these images into the <code>images</code> directory of your project.</p>
<p>Finally, copy the files from the <code>favicons</code> directory in the GitHub repo into the <code>favicons</code> directory of your project.</p>
<p>In <code>base.njk</code> add these lines to the <code>&lt;head&gt;</code> element:</p>
<pre><code class="lang-python">&lt;link rel=<span class="hljs-string">"icon"</span> href=<span class="hljs-string">"/favicon.ico"</span> sizes=<span class="hljs-string">"any"</span>&gt;
&lt;link rel=<span class="hljs-string">"apple-touch-icon"</span> href=<span class="hljs-string">"/apple-touch-icon.png"</span>&gt;
&lt;link rel=<span class="hljs-string">"preconnect"</span> href=<span class="hljs-string">"https://fonts.googleapis.com"</span>&gt;
&lt;link rel=<span class="hljs-string">"preconnect"</span> href=<span class="hljs-string">"https://fonts.gstatic.com"</span> crossorigin&gt;
&lt;link href=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&amp;display=swap"</span> rel=<span class="hljs-string">"stylesheet"</span>&gt;
&lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"{{'/css/style.css' | url | safe}}"</span>&gt;
</code></pre>
<p>The first two links include the favicon, the next three will fetch the Inter font from Google fonts, and the last one will connect <code>style.css</code> to <code>base.njk</code>.</p>
<p>Now, if we reload our homepage we’ll see that… absolutely nothing has changed.</p>
<p>By default, Eleventy will only process template files. To tell it to include CSS files and images, we need to add a few lines of configuration.</p>
<p>Add these lines to the configuration function in <code>.eleventy.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">eleventyConfig</span>) </span>{
  eleventyConfig.addPassthroughCopy(<span class="hljs-string">"./src/css/"</span>);
  eleventyConfig.addWatchTarget(<span class="hljs-string">"./src/css/"</span>);
  eleventyConfig.addPassthroughCopy(<span class="hljs-string">"./src/images/"</span>);
  eleventyConfig.addPassthroughCopy({ <span class="hljs-string">"./src/favicons"</span>: <span class="hljs-string">"/"</span> });

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">dir</span>: {
      <span class="hljs-attr">input</span>: <span class="hljs-string">"src"</span>,
      <span class="hljs-attr">output</span>: <span class="hljs-string">"public"</span>
    }
  };
};
</code></pre>
<p><code>addPassthroughCopy</code> tells Eleventy to pass the CSS, favicons, and image files through to the final build.</p>
<p><code>addWatchTarget</code> tells the Eleventy dev server to watch the <code>css</code> directory and reload the site if the files in this directory change.</p>
<p>With the favicons, we’re also telling Eleventy to output these files to the root of the generated content so that the links in <code>base.njk</code> will work.</p>
<p>Restart the server and you’ll see that the CSS is finally being applied and the favicon is showing up. We'll include the images shortly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-17-19-57-Eleventy-Portfolio.png" alt="Homepage with styles applied" width="600" height="400" loading="lazy"></p>
<p><em>Homepage with styles applied</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-18-16-28-Eleventy-Portfolio.png" alt="About page with styles applied." width="600" height="400" loading="lazy"></p>
<p><em>About page with styles applied</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-18-17-08-Eleventy-Portfolio.png" alt="Contact page with styles applied." width="600" height="400" loading="lazy"></p>
<p><em>Contact page with styles applied</em></p>
<p>The About page and Contact page are fairly self-contained. But the homepage for our site is going to have a couple of parts to it. It will consist of a header and footer, as well as a profile section, technologies section, and projects section. Each of these parts is going to use a <strong>partial</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/layout.png" alt="Layout of portfolio homepage." width="600" height="400" loading="lazy"></p>
<p><em>Layout of portfolio homepage</em></p>
<h2 id="heading-how-to-use-partials-in-eleventy">How to Use Partials in Eleventy</h2>
<p>Partials are templates that make up part of another template. Partials help us to think about our site in terms of reusable components that we can include whenever we need them.</p>
<p>In the <code>_includes</code> directory, create a <code>header.njk</code> and <code>footer.njk</code> file.</p>
<p>Cut the header element out of <code>base.njk</code> and paste it into <code>header.njk</code>.</p>
<p>Cut the footer element out of <code>base.njk</code> and paste it into <code>footer.njk</code>.</p>
<p>Back in <code>base.njk</code> add <code>{% include "header.njk" %}</code> where the header element used to be and <code>{% include "footer.njk" %}</code> where the footer element used to be.</p>
<p><code>base.njk</code> should now have this content inside its <code>&lt;body&gt;</code> tag:</p>
<pre><code class="lang-python">&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">content</span>"&gt;
    {% <span class="hljs-title">include</span> "<span class="hljs-title">header</span>.<span class="hljs-title">njk</span>" %}
    &lt;<span class="hljs-title">main</span> <span class="hljs-title">class</span>="<span class="hljs-title">main</span> <span class="hljs-title">container</span>"&gt;
      {{ <span class="hljs-title">content</span> | <span class="hljs-title">safe</span> }}
    &lt;/<span class="hljs-title">main</span>&gt;
&lt;/<span class="hljs-title">div</span>&gt;
{% <span class="hljs-title">include</span> "<span class="hljs-title">footer</span>.<span class="hljs-title">njk</span>" %}</span>
</code></pre>
<p>The site won’t look any different but our base layout is already becoming more modular.</p>
<p>Next, still in the <code>_includes</code> directory, create a <code>profile.njk</code> file with the following content:</p>
<pre><code class="lang-python">&lt;section <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">profile</span>"&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__image</span>-<span class="hljs-title">wrapper</span>"&gt;
    &lt;<span class="hljs-title">img</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__image</span>" <span class="hljs-title">src</span>="/<span class="hljs-title">images</span>/<span class="hljs-title">profile</span>.<span class="hljs-title">jpg</span>" <span class="hljs-title">alt</span>="<span class="hljs-title">Marie</span> <span class="hljs-title">Jackson</span>, <span class="hljs-title">Software</span> <span class="hljs-title">Developer</span>"&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__card</span>"&gt;
    &lt;<span class="hljs-title">p</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__text</span>"&gt;<span class="hljs-title">Hi</span>! <span class="hljs-title">I</span>'<span class="hljs-title">m</span> &lt;<span class="hljs-title">span</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__text</span>--<span class="hljs-title">highlight</span>"&gt;<span class="hljs-title">Marie</span>&lt;/<span class="hljs-title">span</span>&gt;, <span class="hljs-title">a</span> <span class="hljs-title">mathematician</span> <span class="hljs-title">turned</span> <span class="hljs-title">software</span> <span class="hljs-title">developer</span> <span class="hljs-title">from</span> <span class="hljs-title">Hampton</span>, <span class="hljs-title">Virginia</span>.&lt;/<span class="hljs-title">p</span>&gt;
    &lt;<span class="hljs-title">p</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__text</span>"&gt;<span class="hljs-title">As</span> <span class="hljs-title">a</span> &lt;<span class="hljs-title">span</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__text</span>--<span class="hljs-title">highlight</span>"&gt;<span class="hljs-title">Frontend</span> <span class="hljs-title">Developer</span>&lt;/<span class="hljs-title">span</span>&gt;, <span class="hljs-title">I</span> <span class="hljs-title">love</span> <span class="hljs-title">building</span> <span class="hljs-title">sites</span> <span class="hljs-title">and</span> <span class="hljs-title">apps</span> <span class="hljs-title">that</span> <span class="hljs-title">help</span> <span class="hljs-title">people</span> <span class="hljs-title">reach</span> <span class="hljs-title">their</span> <span class="hljs-title">personal</span> <span class="hljs-title">and</span> <span class="hljs-title">professional</span> <span class="hljs-title">goals</span>.&lt;/<span class="hljs-title">p</span>&gt;
    &lt;<span class="hljs-title">p</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__text</span>"&gt;<span class="hljs-title">I</span> <span class="hljs-title">focus</span> <span class="hljs-title">on</span> <span class="hljs-title">speed</span>, <span class="hljs-title">security</span> <span class="hljs-title">and</span> <span class="hljs-title">scalability</span>, <span class="hljs-title">using</span> <span class="hljs-title">React</span>.<span class="hljs-title">js</span> <span class="hljs-title">and</span> <span class="hljs-title">Firebase</span> <span class="hljs-title">to</span> <span class="hljs-title">create</span> <span class="hljs-title">rich</span>, <span class="hljs-title">dynamic</span> <span class="hljs-title">experiences</span>.&lt;/<span class="hljs-title">p</span>&gt;
    &lt;<span class="hljs-title">p</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__text</span>"&gt;<span class="hljs-title">I</span>'<span class="hljs-title">m</span> <span class="hljs-title">always</span> <span class="hljs-title">open</span> <span class="hljs-title">to</span> <span class="hljs-title">new</span> <span class="hljs-title">opportunities</span> <span class="hljs-title">and</span> <span class="hljs-title">projects</span>. <span class="hljs-title">So</span> <span class="hljs-title">don</span>'<span class="hljs-title">t</span> <span class="hljs-title">hesitate</span> <span class="hljs-title">to</span> &lt;<span class="hljs-title">a</span> <span class="hljs-title">class</span>="<span class="hljs-title">profile__link</span>" <span class="hljs-title">href</span>="/<span class="hljs-title">contact</span>"&gt;<span class="hljs-title">get</span> <span class="hljs-title">in</span> <span class="hljs-title">touch</span>&lt;/<span class="hljs-title">a</span>&gt;.&lt;<span class="hljs-title">p</span>&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
&lt;/<span class="hljs-title">section</span>&gt;</span>
</code></pre>
<p>Next, create a <code>technologies.njk</code> file with this content:</p>
<pre><code class="lang-python">&lt;section <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">technologies</span>"&gt;
  &lt;<span class="hljs-title">h2</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__heading</span>"&gt;<span class="hljs-title">Technologies</span> <span class="hljs-title">I</span> <span class="hljs-title">love</span> <span class="hljs-title">to</span> <span class="hljs-title">work</span> <span class="hljs-title">with</span>&lt;/<span class="hljs-title">h2</span>&gt;
  &lt;<span class="hljs-title">ul</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__list</span>"&gt;
    &lt;<span class="hljs-title">li</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__item</span>"&gt;
      &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__logo</span>"&gt;
      &lt;<span class="hljs-title">img</span> <span class="hljs-title">src</span>="/<span class="hljs-title">images</span>/<span class="hljs-title">javascript</span>.<span class="hljs-title">svg</span>" <span class="hljs-title">alt</span>="<span class="hljs-title">JavaScript</span> <span class="hljs-title">logo</span>"&gt;
      &lt;/<span class="hljs-title">div</span>&gt;
      &lt;<span class="hljs-title">h3</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__title</span>"&gt;<span class="hljs-title">JavaScript</span>&lt;/<span class="hljs-title">h3</span>&gt;
    &lt;/<span class="hljs-title">li</span>&gt;
    &lt;<span class="hljs-title">li</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__item</span>"&gt;
      &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__logo</span>"&gt;
      &lt;<span class="hljs-title">img</span> <span class="hljs-title">src</span>="/<span class="hljs-title">images</span>/<span class="hljs-title">react</span>.<span class="hljs-title">svg</span>" <span class="hljs-title">alt</span>="<span class="hljs-title">React</span>.<span class="hljs-title">js</span> <span class="hljs-title">logo</span>"&gt;
      &lt;/<span class="hljs-title">div</span>&gt;
      &lt;<span class="hljs-title">h3</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__title</span>"&gt;<span class="hljs-title">React</span>.<span class="hljs-title">js</span>&lt;/<span class="hljs-title">h3</span>&gt;
    &lt;/<span class="hljs-title">li</span>&gt;
    &lt;<span class="hljs-title">li</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__item</span>"&gt;
      &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__logo</span>"&gt;
        &lt;<span class="hljs-title">img</span> <span class="hljs-title">src</span>="/<span class="hljs-title">images</span>/<span class="hljs-title">tailwindcss</span>.<span class="hljs-title">svg</span>" <span class="hljs-title">alt</span>="<span class="hljs-title">Tailwind</span> <span class="hljs-title">CSS</span> <span class="hljs-title">logo</span>"&gt;
      &lt;/<span class="hljs-title">div</span>&gt;
      &lt;<span class="hljs-title">h3</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__title</span>"&gt;<span class="hljs-title">Tailwind</span> <span class="hljs-title">CSS</span>&lt;/<span class="hljs-title">h3</span>&gt;
    &lt;/<span class="hljs-title">li</span>&gt;
    &lt;<span class="hljs-title">li</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__item</span>"&gt;
      &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__logo</span>"&gt;
        &lt;<span class="hljs-title">img</span> <span class="hljs-title">src</span>="/<span class="hljs-title">images</span>/<span class="hljs-title">firebase</span>.<span class="hljs-title">svg</span>" <span class="hljs-title">alt</span>="<span class="hljs-title">Firebase</span> <span class="hljs-title">logo</span>"&gt;
      &lt;/<span class="hljs-title">div</span>&gt;
      &lt;<span class="hljs-title">h3</span> <span class="hljs-title">class</span>="<span class="hljs-title">technologies__title</span>"&gt;<span class="hljs-title">Firebase</span>&lt;/<span class="hljs-title">h3</span>&gt;
    &lt;/<span class="hljs-title">li</span>&gt;
  &lt;/<span class="hljs-title">ul</span>&gt;
&lt;/<span class="hljs-title">section</span>&gt;</span>
</code></pre>
<p>In <code>index.njk</code> replace the <code>&lt;h1&gt;</code> tag with:</p>
<pre><code class="lang-python">{% include <span class="hljs-string">"profile.njk"</span> %}
{% include <span class="hljs-string">"technologies.njk"</span> %}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-18-15-11-Eleventy-Portfolio.png" alt="Portfolio homepage with profile and technologies sections." width="600" height="400" loading="lazy"></p>
<p><em>Portfolio homepage with profile and technologies sections</em></p>
<p>Our homepage is starting to take shape but the site still needs the most important part of any portfolio: projects.</p>
<p>To keep the project data organized, we’re going to use <strong>collections</strong>.</p>
<h2 id="heading-how-to-use-collections-in-eleventy">How to Use Collections in Eleventy</h2>
<p>Collections let you group related content together. In our portfolio we’re going to create a <code>projects</code> collection using Markdown files to store information about each individual project.</p>
<p>Inside the <code>src</code> directory, create a <code>projects</code> directory. We’re going to need a Markdown file for each project. As placeholders, we’ll use three projects that I’ve been meaning to build.</p>
<p>Josh W Comeau has some great advice on <a target="_blank" href="https://www.joshwcomeau.com/effective-portfolio/">building an effective developer portfolio</a> and he strongly recommends describing your personal projects with detailed case studies. So for each of our projects we’re going to have a case study laying out:</p>
<ul>
<li><p>What problem we solved</p>
</li>
<li><p>Why we chose these specific technologies</p>
</li>
<li><p>What challenges we faced</p>
</li>
<li><p>What lessons we learned</p>
</li>
</ul>
<p>Copy the following three sample projects into the <code>projects</code> directory:</p>
<p><code>catch-up.md</code></p>
<pre><code class="lang-markdown">---
title: "Catch Up"
summary: "Sometimes it's hard to keep in touch with friends and family. I made this app to remind me to schedule a call if we haven't talked in a while."
image: /images/catch-up.jpg
imageAlt: "Screenshots of catch up app"
tech:
<span class="hljs-bullet">  -</span> "Next.js"
<span class="hljs-bullet">  -</span> "Firebase"
<span class="hljs-bullet">  -</span> "Tailwind CSS"
siteUrl: "#"
<span class="hljs-section">repoUrl: "#"
---</span>

<span class="hljs-section">### Problem Solved</span>

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tincidunt tortor aliquam nulla facilisi. Feugiat scelerisque varius morbi enim nunc faucibus a pellentesque sit. Condimentum lacinia quis vel eros donec ac odio tempor orci.

<span class="hljs-section">### Technologies Used</span>

Scelerisque eleifend donec pretium vulputate sapien nec sagittis aliquam. Diam sit amet nisl suscipit adipiscing bibendum est ultricies. Consequat ac felis donec et odio pellentesque diam volutpat commodo.

<span class="hljs-section">### Challenges Faced</span>

Eget mauris pharetra et ultrices. Molestie nunc non blandit massa enim nec. Ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Nulla at volutpat diam ut venenatis. Volutpat ac tincidunt vitae semper quis lectus nulla at.

<span class="hljs-section">### Lessons Learned</span>

Non blandit massa enim nec. Tempor commodo ullamcorper a lacus vestibulum sed. Et netus et malesuada fames ac turpis egestas integer eget. In ante metus dictum at tempor commodo. Eu scelerisque felis imperdiet proin fermentum leo.
</code></pre>
<p><code>sourdough-sensei.md</code></p>
<pre><code class="lang-markdown">---
title: "Sourdough Sensei"
summary: "Like a lot of people, I got really into sourdough in 2020. I made this app to help me bake delicious bread by putting all my recipes and schedules in one place."
image: /images/sourdough-sensei.jpg
imageAlt: "Screenshots of sourdough bread app"
tech:
<span class="hljs-bullet">  -</span> "React.js"
<span class="hljs-bullet">  -</span> "Firebase"
<span class="hljs-bullet">  -</span> "Tailwind CSS"
siteUrl: "#"
<span class="hljs-section">repoUrl: "#"
---</span>

<span class="hljs-section">### Problem Solved</span>

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tincidunt tortor aliquam nulla facilisi. Feugiat scelerisque varius morbi enim nunc faucibus a pellentesque sit. Condimentum lacinia quis vel eros donec ac odio tempor orci.

<span class="hljs-section">### Technologies Used</span>

Scelerisque eleifend donec pretium vulputate sapien nec sagittis aliquam. Diam sit amet nisl suscipit adipiscing bibendum est ultricies. Consequat ac felis donec et odio pellentesque diam volutpat commodo.

<span class="hljs-section">### Challenges Faced</span>

Eget mauris pharetra et ultrices. Molestie nunc non blandit massa enim nec. Ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Nulla at volutpat diam ut venenatis. Volutpat ac tincidunt vitae semper quis lectus nulla at.

<span class="hljs-section">### Lessons Learned</span>

Non blandit massa enim nec. Tempor commodo ullamcorper a lacus vestibulum sed. Et netus et malesuada fames ac turpis egestas integer eget. In ante metus dictum at tempor commodo. Eu scelerisque felis imperdiet proin fermentum leo.
</code></pre>
<p><code>spellbook.md</code></p>
<pre><code class="lang-markdown">---
title: "Spellbook"
summary: "I'm a huge Dungeons and Dragons fan, but keeping my spells straight has always been a challenge. I built this app to put all the information I need at my fingertips."
image: /images/spellbook.jpg
imageAlt: "Screenshots of DnD project"
tech:
<span class="hljs-bullet">  -</span> "Next.js"
<span class="hljs-bullet">  -</span> "Firebase"
<span class="hljs-bullet">  -</span> "Tailwind CSS"
siteUrl: "#"
<span class="hljs-section">repoUrl: "#"
---</span>

<span class="hljs-section">### Problem Solved</span>

Yes, I could have just used DnD Beyond. But where's the fun in that? Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tincidunt tortor aliquam nulla facilisi. Feugiat scelerisque varius morbi enim nunc faucibus a pellentesque sit. Condimentum lacinia quis vel eros donec ac odio tempor orci.

<span class="hljs-section">### Technologies Used</span>

Scelerisque eleifend donec pretium vulputate sapien nec sagittis aliquam. Diam sit amet nisl suscipit adipiscing bibendum est ultricies. Consequat ac felis donec et odio pellentesque diam volutpat commodo.

<span class="hljs-section">### Challenges Faced</span>

Eget mauris pharetra et ultrices. Molestie nunc non blandit massa enim nec. Ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Nulla at volutpat diam ut venenatis. Volutpat ac tincidunt vitae semper quis lectus nulla at.

<span class="hljs-section">### Lessons Learned</span>

Non blandit massa enim nec. Tempor commodo ullamcorper a lacus vestibulum sed. Et netus et malesuada fames ac turpis egestas integer eget. In ante metus dictum at tempor commodo. Eu scelerisque felis imperdiet proin fermentum leo.
</code></pre>
<p>Just like with templates, the frontmatter at the top of these files makes values available that you can inject into your templates.</p>
<p>Since these Markdown files are ultimately inside the <code>src</code> directory, Eleventy will treat them as templates and create a HTML page from each file. Their URL will be in the format <code>/subdirectory_name/filename</code>, for example <code>/projects/sourdough-sensei</code>.</p>
<p>But Eleventy won't know what layout to use for these pages since they don't yet have a <code>layout</code> value in their frontmatter.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-19-08-18-Screenshot.png" alt="sourdough-sensei page without layout or frontmatter data" width="600" height="400" loading="lazy"></p>
<p><em>sourdough-sensei page without layout or frontmatter data</em></p>
<p>Right now these files aren’t a collection. Collections are defined by sharing a <code>tags</code> value, such as <code>“tags”: “projects”</code>.</p>
<p>Every file with the <code>projects</code> tag will be included in the <code>projects</code> collection.</p>
<p>Since we only have three projects, we could include a <code>tags</code> value in the frontmatter of our three Markdown files.</p>
<p>But if we had a site with a lot of content – for example dozens of blog posts, recorded talks, and tutorials that all shared dozens of tags between them – this could become difficult to manage. This is where <strong>directory data files</strong> are useful.</p>
<h3 id="heading-how-to-use-directory-data-files">How to Use Directory Data Files</h3>
<p>If you have certain values that are shared by every file in a folder, you can put these values in a directory data file.</p>
<p>Inside the <code>projects</code> directory, create a <code>projects.json</code> file. A directory data file should have the same name as the collection it’s attached to.</p>
<p>Any frontmatter fields that are shared by all the projects files should go in the <code>projects.json</code> directory data file:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"layout"</span>: <span class="hljs-string">"project.njk"</span>,
  <span class="hljs-attr">"tags"</span>: <span class="hljs-string">"projects"</span>
}
</code></pre>
<p>The <code>layout</code> value means that every project will use the same layout (we’re going to create this <code>project.njk</code> file in a moment). The <code>tags</code> value is what turns them into the <code>projects</code> collection that we can use in our templates.</p>
<h2 id="heading-how-to-use-collections-in-templates">How to Use Collections in Templates</h2>
<p>We’re now going to use the <code>projects</code> collection to:</p>
<ul>
<li><p>Add a projects section to our homepage</p>
</li>
<li><p>Create a Projects page</p>
</li>
<li><p>Create a case study page for each project</p>
</li>
</ul>
<p>To include data from a collection on a page of your site, you need to reference the <code>collections</code> object in a template.</p>
<p>We can use Nunjucks to loop over the collection and output its contents. To access a frontmatter value from a <code>project</code> in the <code>projects</code> collection, we use <code>project.data</code>.</p>
<p>For example:</p>
<pre><code class="lang-python">{% <span class="hljs-keyword">for</span> project <span class="hljs-keyword">in</span> collections.projects %}
{{ project.data.title }}
{% endfor %}
</code></pre>
<p>In the <code>_includes</code> directory, create <code>project.njk</code>, <code>project-card.njk</code> and <code>project-grid.njk</code> files.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/includes.PNG" alt="_includes directory structure." width="600" height="400" loading="lazy"></p>
<p>_<em>includes directory structure</em></p>
<p>We’ll use <code>project.njk</code> to create a page for each of our projects.</p>
<p>Since these pages are generated from templates, we can access their frontmatter values directly, such as <code>title</code>, <code>image</code>, <code>imageAlt</code>, and <code>content</code> for the main content of the Markdown file.</p>
<pre><code class="lang-python">---
layout: <span class="hljs-string">"base.njk"</span>
---

&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">project</span>"&gt;
  &lt;<span class="hljs-title">h2</span> <span class="hljs-title">class</span>="<span class="hljs-title">project__heading</span>"&gt;{{ <span class="hljs-title">title</span> }}&lt;/<span class="hljs-title">h2</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">project__image</span>-<span class="hljs-title">wrapper</span>"&gt;
      &lt;<span class="hljs-title">img</span> <span class="hljs-title">class</span>="<span class="hljs-title">project__image</span>" <span class="hljs-title">src</span>="{{ <span class="hljs-title">image</span> }}" <span class="hljs-title">alt</span>="{{ <span class="hljs-title">imageAlt</span> }}"&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">project__content</span> <span class="hljs-title">prose</span>"&gt;
    {{ <span class="hljs-title">content</span> | <span class="hljs-title">safe</span> }}
  &lt;/<span class="hljs-title">div</span>&gt;
&lt;/<span class="hljs-title">div</span>&gt;</span>
</code></pre>
<p><code>project-grid.njk</code> and <code>project-card.njk</code> will form the list of projects on our portfolio homepage and Projects page.</p>
<p><code>project-grid.njk</code> will loop over the <code>projects</code> collection and insert a <code>project-card</code> partial for each project in the collection.</p>
<p>Add the following content to <code>project-grid.njk</code>:</p>
<pre><code class="lang-python">&lt;section <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">projects</span>"&gt;
  &lt;<span class="hljs-title">h2</span> <span class="hljs-title">class</span>="<span class="hljs-title">project__heading</span>"&gt;<span class="hljs-title">Recent</span> <span class="hljs-title">projects</span>&lt;/<span class="hljs-title">h2</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">grid</span>"&gt;
    {% <span class="hljs-title">for</span> <span class="hljs-title">project</span> <span class="hljs-title">in</span> <span class="hljs-title">collections</span>.<span class="hljs-title">projects</span> %}
      {% <span class="hljs-title">include</span> "<span class="hljs-title">project</span>-<span class="hljs-title">card</span>.<span class="hljs-title">njk</span>" %}
    {% <span class="hljs-title">endfor</span> %}
  &lt;/<span class="hljs-title">div</span>&gt;
&lt;/<span class="hljs-title">section</span>&gt;</span>
</code></pre>
<p>Add the following content to <code>project-card.njk</code>:</p>
<pre><code class="lang-python">&lt;article <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card</span>"&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__image</span>-<span class="hljs-title">wrapper</span>"&gt;
    &lt;<span class="hljs-title">img</span> <span class="hljs-title">class</span>="<span class="hljs-title">project__image</span>" <span class="hljs-title">src</span>="{{ <span class="hljs-title">project</span>.<span class="hljs-title">data</span>.<span class="hljs-title">image</span> }}" <span class="hljs-title">alt</span>="{{ <span class="hljs-title">project</span>.<span class="hljs-title">data</span>.<span class="hljs-title">imageAlt</span> }}"&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__body</span>"&gt;
    &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__tags</span>"&gt;
      {% <span class="hljs-title">for</span> <span class="hljs-title">tag</span> <span class="hljs-title">in</span> <span class="hljs-title">project</span>.<span class="hljs-title">data</span>.<span class="hljs-title">tech</span> %}
        &lt;<span class="hljs-title">span</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__tag</span>"&gt;{{ <span class="hljs-title">tag</span> }}&lt;/<span class="hljs-title">span</span>&gt;
      {% <span class="hljs-title">endfor</span> %}
    &lt;/<span class="hljs-title">div</span>&gt;
    &lt;<span class="hljs-title">h3</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__title</span>"&gt;
      &lt;<span class="hljs-title">a</span> <span class="hljs-title">href</span>="{{ <span class="hljs-title">project</span>.<span class="hljs-title">url</span> }}"&gt;{{ <span class="hljs-title">project</span>.<span class="hljs-title">data</span>.<span class="hljs-title">title</span> }}&lt;/<span class="hljs-title">a</span>&gt;
    &lt;/<span class="hljs-title">h3</span>&gt;
    &lt;<span class="hljs-title">p</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__summary</span>"&gt;{{ <span class="hljs-title">project</span>.<span class="hljs-title">data</span>.<span class="hljs-title">summary</span> }}&lt;/<span class="hljs-title">p</span>&gt;
    &lt;<span class="hljs-title">a</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__link</span>" <span class="hljs-title">href</span>="{{ <span class="hljs-title">project</span>.<span class="hljs-title">url</span> }}"&gt;<span class="hljs-title">Read</span> <span class="hljs-title">project</span> <span class="hljs-title">case</span> <span class="hljs-title">study</span> 
      &lt;<span class="hljs-title">svg</span> <span class="hljs-title">xmlns</span>="<span class="hljs-title">http</span>:</span>//www.w3.org/<span class="hljs-number">2000</span>/svg<span class="hljs-string">" class="</span>project-card__link-icon<span class="hljs-string">" viewBox="</span><span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">20</span> <span class="hljs-number">20</span><span class="hljs-string">" fill="</span>currentColo<span class="hljs-string">r"&gt;
        &lt;path fill-rule="</span>evenodd<span class="hljs-string">" d="</span>M7<span class="hljs-number">.293</span> <span class="hljs-number">14.707</span>a1 <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">010</span><span class="hljs-number">-1.414L</span>10<span class="hljs-number">.586</span> <span class="hljs-number">10</span> <span class="hljs-number">7.293</span> <span class="hljs-number">6.707</span>a1 <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">011.414</span><span class="hljs-number">-1.414l</span>4 <span class="hljs-number">4</span>a1 <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">010</span> <span class="hljs-number">1.414l</span><span class="hljs-number">-4</span> <span class="hljs-number">4</span>a1 <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">01</span><span class="hljs-number">-1.414</span> <span class="hljs-number">0</span>z<span class="hljs-string">" clip-rule="</span>evenodd<span class="hljs-string">" /&gt;
      &lt;/svg&gt;
    &lt;/a&gt;
  &lt;/div&gt;
&lt;/article&gt;</span>
</code></pre>
<p>Since <code>project-card.njk</code> is accessing frontmatter values from a member of a collection, we need to use <code>project.data</code> to access these values in the template. Eleventy also generates a <code>project.url</code> value that we can use to link to the project's generated page.</p>
<p>In <code>index.njk</code>, add <code>{% include "project-grid.njk" %}</code> underneath the profile and technologies partials.</p>
<pre><code class="lang-python">---
title: <span class="hljs-string">"Eleventy Portfolio"</span>
layout: <span class="hljs-string">"base.njk"</span>
---

{% include <span class="hljs-string">"profile.njk"</span> %}
{% include <span class="hljs-string">"technologies.njk"</span> %}
{% include <span class="hljs-string">"project-grid.njk"</span> %}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-19-41-45-Eleventy-Portfolio.png" alt="Grid of project cards on homepage." width="600" height="400" loading="lazy"></p>
<p><em>Grid of project cards on homepage</em></p>
<p>Next we're going to create a Projects page. In the <code>src</code> directory, create a <code>projects.njk</code> file with the following contents:</p>
<pre><code class="lang-python">---
title: <span class="hljs-string">"Eleventy Portfolio"</span>
layout: <span class="hljs-string">"base.njk"</span>
---

&lt;h2 <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">projects__heading</span>"&gt;<span class="hljs-title">Recent</span> <span class="hljs-title">projects</span>&lt;/<span class="hljs-title">h2</span>&gt;
&lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">list</span>"&gt;
  {% <span class="hljs-title">for</span> <span class="hljs-title">project</span> <span class="hljs-title">in</span> <span class="hljs-title">collections</span>.<span class="hljs-title">projects</span> %}
    {% <span class="hljs-title">include</span> "<span class="hljs-title">project</span>-<span class="hljs-title">card</span>.<span class="hljs-title">njk</span>" %}
  {% <span class="hljs-title">endfor</span> %}
&lt;/<span class="hljs-title">div</span>&gt;</span>
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-19-25-32-Eleventy-Portfolio.png" alt="Projects page." width="600" height="400" loading="lazy"></p>
<p><em>Projects page</em></p>
<p>The projects are now being displayed on our homepage as well as the projects page, and each project now has its own page with its case study.</p>
<p>We could stop here, but there are a few more Eleventy features that will make our portfolio site even better, namely <strong>shortcodes</strong> and <strong>plugins</strong>.</p>
<h3 id="heading-how-to-use-shortcodes">How to Use Shortcodes</h3>
<p>A shortcode is a way to inject reusable content (often a JavaScript string template literal) into your templates.</p>
<p>We’re going to create a simple <code>year</code> shortcode that outputs the current year so that the footer in our portfolio site is always up to date.</p>
<p>Add the following line to the configuration function in <code>.eleventy.js</code>.</p>
<pre><code class="lang-javascript">eleventyConfig.addShortcode(<span class="hljs-string">"year"</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear()}</span>`</span>);
</code></pre>
<p>When you use the shortcode in a template the function will be run and the <code>year</code> value will be injected into the template.</p>
<p>In <code>footer.njk</code> use <code>{% year %}</code> to access the <code>year</code> shortcode.</p>
<pre><code class="lang-python">&lt;footer <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">footer</span>"&gt;
  &lt;<span class="hljs-title">p</span>&gt;&amp;<span class="hljs-title">copy</span>; <span class="hljs-title">Marie</span> <span class="hljs-title">Jackson</span> {% <span class="hljs-title">year</span> %}&lt;/<span class="hljs-title">p</span>&gt;
&lt;/<span class="hljs-title">footer</span>&gt;</span>
</code></pre>
<p>You might need to restart your development server for it to recognize the shortcode.</p>
<p>Now whenever you trigger a build of your site in the future, your footer will always show the correct year.</p>
<p>Shortcodes can do a lot more than this. Next, we’re going to use the Eleventy Image plugin, which uses shortcodes, to optimize our site’s images and improve our page load speed.</p>
<h3 id="heading-how-to-use-the-eleventy-image-plugin">How to Use the Eleventy Image Plugin</h3>
<p>Eleventy has a number of official plugins, from ones to check your writing for inclusive language to others that let you leverage Serverless functions.</p>
<p>The Image plugin is particularly useful since images are often the largest resource your site loads. It optimizes your images so your site uses the appropriate size and format for the user's browser, saving bandwidth for your user and making your site load faster.</p>
<p>First we need to install the Image plugin from npm. In the root of your project, run:</p>
<pre><code class="lang-python">npm install @<span class="hljs-number">11</span>ty/eleventy-img
</code></pre>
<p>At the top of <code>.eleventy.js</code>, we’re going to import the Image plugin and configure the shortcode that the plugin will use to optimize our images.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Image = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@11ty/eleventy-img"</span>);

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">imageShortcode</span>(<span class="hljs-params">src, alt, sizes</span>) </span>{
  <span class="hljs-keyword">let</span> metadata = <span class="hljs-keyword">await</span> Image(<span class="hljs-string">`./src<span class="hljs-subst">${src}</span>`</span>, {
    <span class="hljs-attr">widths</span>: [<span class="hljs-number">300</span>, <span class="hljs-number">800</span>, <span class="hljs-literal">null</span>],
    <span class="hljs-attr">formats</span>: [<span class="hljs-string">"avif"</span>, <span class="hljs-string">"jpeg"</span>],
    <span class="hljs-attr">urlPath</span>: <span class="hljs-string">"/images/"</span>,
    <span class="hljs-attr">outputDir</span>: <span class="hljs-string">"./public/images/"</span>
  });

  <span class="hljs-keyword">let</span> imageAttributes = {
    alt,
    sizes,
    <span class="hljs-attr">loading</span>: <span class="hljs-string">"lazy"</span>,
    <span class="hljs-attr">decoding</span>: <span class="hljs-string">"async"</span>
  };

  <span class="hljs-keyword">return</span> Image.generateHTML(metadata, imageAttributes);
}
</code></pre>
<p>The image shortcode takes in arguments for <code>src</code>, <code>alt</code> and <code>sizes</code>. These will be the URL for the image, the text for the image’s alt tag, and the sizes that are used to display different sized images at different screen sizes.</p>
<p>The <code>widths</code> property specifies what size images the plugin will generate. In this case, 300px, 800px, and the image's original size.</p>
<p>The <code>formats</code> property specifies which image formats to generate. Here we're using avif (which produces high quality images at low file sizes) with jpeg as a fallback for browsers that don't support avif.</p>
<p><code>urlPath</code> and <code>outputDir</code> tell the plugin where to get the images from and where to output the optimized images to.</p>
<p>The plugin adds <code>loading</code> and <code>decoding</code> attributes to the generated HTML to lazy-load the images and decode them asynchronously, both of which will help with page load times.</p>
<p>Next, we’re going to include the shortcode in our configuration function. We'll call it <code>EleventyImage</code> for the sake of clarity.</p>
<pre><code class="lang-javascript">eleventyConfig.addNunjucksAsyncShortcode(<span class="hljs-string">"EleventyImage"</span>, imageShortcode);
</code></pre>
<p>Notice that we’re using <code>addNunjucksAsyncShortcode</code> rather than <code>addShortcode</code>. This is because the image generation process is asynchronous. It will take some amount of time to generate different image sizes and formats and we want our shortcode to wait until these have all been generated before it injects the finished HTML into our templates.</p>
<p>Since our shortcode is asynchronous, we’re going to run into an issue with using this shortcode inside a Nunjucks for loop. We need to use <code>asyncEach</code>, the asynchronous version of Nunjuck’s <code>for</code>.</p>
<p>In <code>projects.njk</code> and <code>project-grid.njk</code>, replace this:</p>
<pre><code class="lang-python">{% <span class="hljs-keyword">for</span> project <span class="hljs-keyword">in</span> collections.projects %}
{% include <span class="hljs-string">"project-card.njk"</span> %}
{% endfor %}
</code></pre>
<p>with this:</p>
<pre><code class="lang-python">{% asyncEach project <span class="hljs-keyword">in</span> collections.projects %}
{% include <span class="hljs-string">"project-card.njk"</span> %}
{% endeach %}
</code></pre>
<p>Now, in <code>project.njk</code> we can replace this:</p>
<pre><code class="lang-python">&lt;img <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">project__image</span>" <span class="hljs-title">src</span>="{{ <span class="hljs-title">image</span> }}" <span class="hljs-title">alt</span>="{{ <span class="hljs-title">imageAlt</span> }}"&gt;</span>
</code></pre>
<p>with this:</p>
<pre><code class="lang-python">{% EleventyImage image, imageAlt, <span class="hljs-string">"(min-width: 30em) 50vw, 100vw"</span> %}
</code></pre>
<p>The <code>image</code>, <code>imageAlt</code> and <code>"(min-width: 30em) 50vw, 100vw"</code> values are the <code>src</code>, <code>alt</code> and <code>sizes</code> parameters for the Image shortcode.</p>
<p>Next, in <code>project-card.njk</code>, we can replace this:</p>
<pre><code class="lang-python">&lt;img <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">project</span>-<span class="hljs-title">card__image</span>" <span class="hljs-title">src</span>="{{ <span class="hljs-title">project</span>.<span class="hljs-title">data</span>.<span class="hljs-title">image</span> }}" <span class="hljs-title">alt</span>="{{ <span class="hljs-title">project</span>.<span class="hljs-title">data</span>.<span class="hljs-title">imageAlt</span> }}"&gt;</span>
</code></pre>
<p>with this:</p>
<pre><code class="lang-python">{% EleventyImage project.data.image, project.data.imageAlt, <span class="hljs-string">"(min-width: 30em) 50vw, 100vw"</span> %}
</code></pre>
<p>Finally, in <code>profile.njk</code> we can replace this:</p>
<pre><code class="lang-python">&lt;img <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">profile__image</span>" <span class="hljs-title">src</span>="/<span class="hljs-title">images</span>/<span class="hljs-title">profile</span>.<span class="hljs-title">jpg</span>" <span class="hljs-title">alt</span>="<span class="hljs-title">Marie</span> <span class="hljs-title">Jackson</span>, <span class="hljs-title">Software</span> <span class="hljs-title">Developer</span>"&gt;</span>
</code></pre>
<p>with this:</p>
<pre><code class="lang-python">{% EleventyImage <span class="hljs-string">"/images/profile.jpg"</span>, <span class="hljs-string">"Marie Jackson, Software Developer"</span>, <span class="hljs-string">"(min-width: 16em) 50vw, 100vw"</span> %}
</code></pre>
<p>When our site builds, the Eleventy Image plugin will do a couple of things:</p>
<ul>
<li><p>there will be multiple formats and sizes for every image in <code>public/images</code></p>
</li>
<li><p>our generated HTML will now use the <code>&lt;picture&gt;</code> element</p>
</li>
<li><p>the <code>&lt;img&gt;</code> tags will have <code>loading="lazy"</code> and <code>decode="async"</code> attributes</p>
</li>
</ul>
<p>Now our site will serve the optimal image format and size depending on the browser and screen size of the site visitor. And images will be lazy loaded as they are about to enter the viewport.</p>
<p>If we use the network tab in a browser's developer tools, we can test the difference. On an iPhone 12, the unoptimized image on one of our project pages would be 30.37KB, while the image optimized by the Image plugin is just 6.01KB, an 80% saving!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/unoptimized.PNG" alt="Unoptimized image on mobile - 30.37KB." width="600" height="400" loading="lazy"></p>
<p><em>Unoptimized image on mobile - 30.37KB</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/optimized.PNG" alt="Optimized image on mobile 6.01KB." width="600" height="400" loading="lazy"></p>
<p><em>Optimized image on mobile 6.01KB</em></p>
<p>We’re almost ready to deploy our site. But before we do that, we need to complete our contact form.</p>
<h2 id="heading-how-to-a-build-contact-form-with-netlify-forms">How to a Build Contact Form with Netlify Forms</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-18-17-08-Eleventy-Portfolio-1.png" alt="Contact page" width="600" height="400" loading="lazy"></p>
<p><em>Contact page</em></p>
<p>Eleventy is a <strong>static</strong> site generator. But Eleventy works really well with the Jamstack architecture, where you statically generate as much of a site as possible in advance and use APIs and third-party services to add dynamic content and functionality.</p>
<p>In the past, if you wanted to have a contact form on your website, you would need some sort of server, such as a PHP app, to process the form submission.</p>
<p>We’re going to use Netlify Forms to add a contact form to our portfolio without needing to manage a server to handle the submitted forms.</p>
<p>To make this work, we need to make sure our form has two attributes. The most important one is <code>data-netlify="true"</code>. The other is <code>action="/success"</code>.</p>
<pre><code class="lang-python">&lt;form <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">form</span>" <span class="hljs-title">name</span>="<span class="hljs-title">contact</span>" <span class="hljs-title">action</span>="/<span class="hljs-title">success</span>" <span class="hljs-title">method</span>="<span class="hljs-title">POST</span>" <span class="hljs-title">data</span>-<span class="hljs-title">netlify</span>="<span class="hljs-title">true</span>"&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__section</span>"&gt;
    &lt;<span class="hljs-title">label</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__label</span>" <span class="hljs-title">for</span>="<span class="hljs-title">yourName</span>"&gt;<span class="hljs-title">Name</span>&lt;/<span class="hljs-title">label</span>&gt;
    &lt;<span class="hljs-title">input</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__input</span>" <span class="hljs-title">name</span>="<span class="hljs-title">name</span>" <span class="hljs-title">type</span>="<span class="hljs-title">text</span>" <span class="hljs-title">id</span>="<span class="hljs-title">yourName</span>" <span class="hljs-title">required</span>="<span class="hljs-title">true</span>"&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__section</span>"&gt;
    &lt;<span class="hljs-title">label</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__label</span>" <span class="hljs-title">for</span>="<span class="hljs-title">yourEmail</span>"&gt;<span class="hljs-title">Email</span>&lt;/<span class="hljs-title">label</span>&gt;
    &lt;<span class="hljs-title">input</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__input</span>" <span class="hljs-title">name</span>="<span class="hljs-title">email</span>" <span class="hljs-title">type</span>="<span class="hljs-title">email</span>"  <span class="hljs-title">id</span>="<span class="hljs-title">yourEmail</span>" <span class="hljs-title">required</span>="<span class="hljs-title">true</span>"&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
  &lt;<span class="hljs-title">div</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__section</span>"&gt;
    &lt;<span class="hljs-title">label</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__label</span>" <span class="hljs-title">for</span>="<span class="hljs-title">message</span>"&gt;<span class="hljs-title">Message</span>&lt;/<span class="hljs-title">label</span>&gt;
    &lt;<span class="hljs-title">textarea</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__input</span>" <span class="hljs-title">name</span>="<span class="hljs-title">message</span>" <span class="hljs-title">id</span>="<span class="hljs-title">message</span>" <span class="hljs-title">rows</span>="4" <span class="hljs-title">required</span>="<span class="hljs-title">true</span>"&gt;&lt;/<span class="hljs-title">textarea</span>&gt;
  &lt;/<span class="hljs-title">div</span>&gt;
    &lt;<span class="hljs-title">button</span> <span class="hljs-title">class</span>="<span class="hljs-title">form__button</span>" <span class="hljs-title">type</span>="<span class="hljs-title">submit</span>"&gt;<span class="hljs-title">Let</span>'<span class="hljs-title">s</span> <span class="hljs-title">talk</span>&lt;/<span class="hljs-title">button</span>&gt;
&lt;/<span class="hljs-title">form</span>&gt;</span>
</code></pre>
<p>By having a <code>data-netlify="true"</code> attribute on our contact form, when the site is deployed to Netlify, Netlify will recognize this and take over handling the form submission.</p>
<p>By default, when someone completes a Netlify form, they will get a generically styled success message with a link back to the form page. But we can direct them to a custom page by including an <code>action</code> attribute on our form.</p>
<p>The <code>action="/success"</code> attribute means that when the form submits, the user will be redirected to a "success" page on your site (you can give this page a different name if you like). So we had better build that page now.</p>
<p>In the <code>src</code> directory, create a <code>success.njk</code> file with the following content:</p>
<pre><code class="lang-python">---
title: <span class="hljs-string">"Eleventy Portfolio"</span>
layout: <span class="hljs-string">"base.njk"</span>
---

&lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">container</span> <span class="hljs-title">text</span>-<span class="hljs-title">center</span>"&gt;
  &lt;<span class="hljs-title">h2</span> <span class="hljs-title">class</span>="<span class="hljs-title">heading</span>--<span class="hljs-title">main</span>"&gt;<span class="hljs-title">Thanks</span> <span class="hljs-title">for</span> <span class="hljs-title">getting</span> <span class="hljs-title">in</span> <span class="hljs-title">touch</span>!&lt;/<span class="hljs-title">h2</span>&gt;
  &lt;<span class="hljs-title">p</span>&gt;<span class="hljs-title">I</span>'<span class="hljs-title">ll</span> <span class="hljs-title">respond</span> <span class="hljs-title">as</span> <span class="hljs-title">soon</span> <span class="hljs-title">as</span> <span class="hljs-title">I</span> <span class="hljs-title">can</span>.&lt;<span class="hljs-title">p</span>&gt;
&lt;/<span class="hljs-title">div</span>&gt;</span>
</code></pre>
<p>Once we deploy the site to Netlify, any submitted forms will show up in the Netlify interface. So let’s finally deploy our portfolio site.</p>
<h2 id="heading-how-to-deploy-to-netlify">How to Deploy to Netlify</h2>
<p>You can deploy an Eleventy site on any static hosting platform: Netlify, Vercel, GitHub Pages, even an AWS S3 bucket.</p>
<p>I'll show you how to deploy to Netlify since we're using Netlify Forms for our contact form. On another hosting platform, you could use a Serverless function to handle submitting the form and sending an email.</p>
<p>If you don't already have a Netlify account, go to <a target="_blank" href="https://www.netlify.com/">netlify.com</a> and create a free one.</p>
<p>Netlify will give you the option to:</p>
<ol>
<li><p>Import an existing project</p>
</li>
<li><p>Start from a template</p>
</li>
<li><p>Deploy manually</p>
</li>
</ol>
<p>We already have our portfolio site, so we don't need a template.</p>
<p>I'll walk you through the two other options.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-21-25-15-Deploy-your-first-project-Netlify.png" alt="Netlify project start screen." width="600" height="400" loading="lazy"></p>
<p><em>Netlify project start screen</em></p>
<h3 id="heading-option-1-how-to-deploy-manually">Option 1 – How to Deploy Manually</h3>
<p>If you're not comfortable with Git and GitHub, Netlify lets you drag and drop to upload a project into their interface.</p>
<p>On your command line, run <code>npm run build</code> or <code>eleventy</code> to build your site.</p>
<p>Now upload the site's <code>public</code> directory to Netlify's file upload interface. In a few moments Netlify will have the site live on a URL that you can visit.</p>
<p>If you want to make future changes to your deployed site, click on "Deploys" and scroll down to find the file uploader.</p>
<p>You can rebuild your site locally and upload the new version of your <code>public</code> folder to Netlify whenever you want.</p>
<h3 id="heading-option-2-how-to-import-a-project-from-git">Option 2 – How to Import a Project from Git</h3>
<p>If you are familiar with Git and GitHub, commit your code and push it to GitHub. Then click on the "Import from Git" button.</p>
<p>Netlify will ask you to connect a Git provider. Choose GitHub and authorize Netlify to access your GitHub repositories.</p>
<p>Choose the repository that holds your portfolio site. You can search for "eleventy", or whatever name you gave it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-21-44-56-Import-an-existing-project-from-a-Git-repository-Netlify.png" alt="Netlify import project interface." width="600" height="400" loading="lazy"></p>
<p><em>Netlify import project interface</em></p>
<p>Netlify will detect that this is an Eleventy project and will ask you to confirm the basic build settings.</p>
<p>Make sure the build command is either <code>npm run build</code> or <code>eleventy</code>.</p>
<p>Under "Publish directory", enter <code>public</code> instead of <code>_site</code>.</p>
<p>Now click the "Deploy site" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-21-46-55-Import-an-existing-project-from-a-Git-repository-Netlify.png" alt="Netlify build settings page." width="600" height="400" loading="lazy"></p>
<p><em>Netlify build settings page</em></p>
<p>In a few moments Netlify will tell you that your site is live and give you a URL for it.</p>
<p>Once your site is live, if go to the Contact page, fill out the form, and submit it. You'll be redirected to the custom success page that you made.</p>
<p>If you click on "Forms" in the Netlify interface, you'll be brought to the Netlify Forms dashboard.</p>
<p>The form will have whatever name you used in the <code>name</code> attribute on your contact form, in this case "contact".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/08/Screenshot-2022-08-30-at-21-55-09-Forms-remarkable-blini-2319ee.png" alt="Netlify forms dashboard." width="600" height="400" loading="lazy"></p>
<p><em>Netlify forms dashboard</em></p>
<p>Congratulations, you've built and deployed an Eleventy portfolio site. 🥳🎉🎉🎉</p>
<p>Feel free to use this project as a template for your own portfolio and customize it any way you'd like. So many portfolios look similar, so it's always good when a portfolio shows off your personality and passions.</p>
<h2 id="heading-where-to-take-it-from-here">Where to Take it from Here</h2>
<p>This tutorial has hopefully taught you the basics of Eleventy, and how to combine data and templates to make fast sites without a whole lot of tooling or configuration.</p>
<p>If you'd like to take your Eleventy journey further, the <a target="_blank" href="https://www.11ty.dev/docs/">Eleventy docs</a> are very good. There's lots more to learn about manipulating data, not to mention adding personalized content and dynamic interactivity with Serverless and Edge Functions.</p>
<p><a target="_blank" href="https://11ty.rocks/">11ty.rocks</a> by Stephanie Eckles is also a great resource, with practical tips and helpful tutorials on all sorts of Eleventy features.</p>
<p>I hope this guide has been helpful and made you excited to learn more about Eleventy, static site generators, and the Jamstack.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ From WordPress to Hugo – How I Migrated a 250+ Page Site and the Scripts I Used ]]>
                </title>
                <description>
                    <![CDATA[ I recently decided to switch Boot.dev's blog from WordPress to a static site generator. I decided on Hugo, partly because I’m a huge fan of the Go programming language, but also because it just seemed like the best option from a dev-experience perspe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/wordpress-to-hugo-scripts/</link>
                <guid isPermaLink="false">66b9e9f3fbdfcf519c5e1a68</guid>
                
                    <category>
                        <![CDATA[ Hugo ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                    <category>
                        <![CDATA[ WordPress ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lane Wagner ]]>
                </dc:creator>
                <pubDate>Tue, 30 Aug 2022 23:25:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/1__D5sbQUd4XwTy9yt67BCCA-1.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I recently decided to switch <a target="_blank" href="https://boot.dev">Boot.dev's</a> blog from WordPress to a static site generator.</p>
<p>I decided on <a target="_blank" href="https://gohugo.io/">Hugo</a>, partly because I’m a huge fan of the Go programming language, but also because it just seemed like the best option from a dev-experience perspective. </p>
<p>Here’s why I decided to move away from WordPress:</p>
<ul>
<li>I wanted to write and store all my posts in Markdown.</li>
<li>I wanted to version control all my posts in Git/Github.</li>
<li>I wanted to be able to use ctrl+shift+f to find and edit the content on my blog.</li>
<li>I wanted a less buggy way to do “global blocks”</li>
<li>I didn’t want to spend hours fine-tuning performance. My posts are just text and images, so page speed scores should be 100 by default!</li>
<li>I wanted to use straight CSS for styling, making it easier for my blog and app to have identical styling.</li>
<li>I wanted to host the site for free</li>
</ul>
<p>With all those goals in mind, Hugo was a perfect choice. I’ve been super happy since making the switch, but there were some hiccups along the way. </p>
<p>WordPress takes care of some of the nuances of SEO-tuning for you, and if you forget to redo them yourself, you’ll end up sacrificing rankings.</p>
<h2 id="heading-how-to-export-wordpress-posts-as-markdown">How to Export WordPress Posts as Markdown</h2>
<p>Manually copying and pasting all of your posts out of the WordPress GUI into Markdown files could take a <em>very</em> long time. Especially if you have hundreds of posts like I do. </p>
<p>In order to speed up the process, I used <a target="_blank" href="https://wordpress.org/plugins/wp-gatsby-markdown-exporter/">this Markdown plugin</a> to export all of my posts as markdown at once. It’s called “Gatsby Markdown Exporter”, but it works for Hugo just as well.</p>
<p>I’m not going to go over the basics of getting up and running with Hugo, as I’m going to assume you are already familiar with it. But if you’re not, you can read the <a target="_blank" href="https://gohugo.io/getting-started/quick-start/">quick start guide here</a>.</p>
<h2 id="heading-how-to-use-hugos-built-in-seo-templates">How to Use Hugo’s Built-in SEO Templates</h2>
<p>WordPress and its various SEO plugins take care of a lot of SEO-related hocus-pocus for you. So when you move to a static site generator you often have to do some of that stuff yourself.</p>
<p>As it turns out, Hugo has some turn-key solutions for most of this, so using the right <a target="_blank" href="https://gohugo.io/templates/internal/">internal templates</a> can get you all the open-graph, schema, and Twitter meta tags out of the box.</p>
<p>For me, that meant simply adding these 3 lines to my <code>layouts/partials/header.html</code> file within the <code>&lt;head&gt;</code> tag:</p>
<pre><code class="lang-html">{{ template "_internal/opengraph.html" . }}
{{ template "_internal/twitter_cards.html" . }}
{{ template "_internal/schema.html" . }}
</code></pre>
<p>Now we'll get into the scripts I wrote to help me with this process.</p>
<h2 id="heading-script-1-checking-for-broken-links">Script 1 — Checking for Broken Links</h2>
<p>You can solve a lot of your WordPress woes by installing plugins for various tasks. Well, assuming those plugins don’t break your entire WP installation.</p>
<p>With Hugo, I just write little text-manipulation or HTTP scripts to do my bidding.</p>
<p>This first script is just a simple iterator that crawls the site and reports any broken links. You can see the <a target="_blank" href="https://github.com/bootdotdev/blog/blob/main/scripts/linkcheck/main.go">full source code here</a>. It's small edit of the script the Go team uses to check for broken links on the Go programming language's website.</p>
<h2 id="heading-script-2-minifying-images">Script 2 — Minifying Images</h2>
<p>When working with WordPress, you would typically upload a blog post image, and some SEO plugin would optimize that image’s size for you. </p>
<p>In Hugo, no such optimizations are made for you. It simply serves the exact image you add to your “static” folder.</p>
<p>I wrote a little script to optimize my images. It does the following:</p>
<ul>
<li>Takes an input image of nearly any type (.png, .jpeg, .gif, etc).</li>
<li>Converts it to the <code>.webp</code> format (a performant format).</li>
<li>Shrinks the image to a max image size I've configured if it’s too large.</li>
</ul>
<p>This script is written in Node.js and <a target="_blank" href="https://github.com/bootdotdev/blog/blob/main/scripts/image-min.js">the source code can be found here</a>.</p>
<h2 id="heading-script-3-managing-global-shortcodes">Script 3 — Managing Global Shortcodes</h2>
<p>WordPress had a feature called “global blocks”. They're super useful for things like newsletter signup boxes that show up in the middle of your articles, but that you want to be able to update in a single place. </p>
<p>In Hugo, you can use <a target="_blank" href="https://gohugo.io/content-management/shortcodes/">shortcodes</a> to achieve a similar purpose.</p>
<p>Unfortunately, it’s the inserting and removing of the shortcodes <em>themselves</em> that actually becomes tedious at scale. </p>
<p>For example, let’s say I added my newsletter signup shortcode after the 5th paragraph in all my articles, but now I want it to come after the 7th paragraph. I have to go manually copy/paste the short code!</p>
<p>Well, not anymore. Again, this is the great thing about storing all your blog posts in a text format — we can script some simple solutions. I wrote two scripts, and their source code can be found here:</p>
<ul>
<li><a target="_blank" href="https://github.com/bootdotdev/blog/blob/main/scripts/rmshorts/main.go">rmshorts</a></li>
<li><a target="_blank" href="https://github.com/bootdotdev/blog/blob/main/scripts/addshorts/main.go">addshorts</a></li>
</ul>
<p>These two scripts allow me move my global blocks with ease. For example, to remove all instances of <code>myshortcode</code> from the blog:</p>
<pre><code class="lang-bash">rmshorts myshortcode
</code></pre>
<p>Then to add <code>myshortcode</code> as its own paragraph after the 2nd section of every blog post across the site:</p>
<pre><code class="lang-bash">addshorts myshortcode 2
</code></pre>
<p>This has been a huge time saver.</p>
<h2 id="heading-script-4-converting-docx-to-markdown">Script 4 — Converting .docx to Markdown</h2>
<p>Last but not least, I work with some non-technical writers. My writers aren’t used to writing Markdown. Google docs is their tool of choice. </p>
<p>I have no problem with that, as I want them to be effective. My solution was to write a <a target="_blank" href="https://github.com/bootdotdev/blog/blob/main/scripts/docxmd.sh">little bash script</a> that uses <a target="_blank" href="https://pandoc.org/">pandoc</a> under the hood to convert the Google Docs to Markdown files.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>I hope some of these scripts are valuable to you, and I really can’t recommend enough making the switch to a static site generator. If you’re willing to put in a few hours to get a great working environment up and running for your blog it will save you a lot of time and money in the long run.</p>
<p>Remember, always automate the boring stuff!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Theme-able Static Website ]]>
                </title>
                <description>
                    <![CDATA[ A while ago I wanted to create a dark theme for my personal site. So I did some clicking around to find out the most suitable and clean way to do this. I read Max Bock's article on creating a custom theme, where he explained the process quite clearly... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/design-a-themeable-static-website/</link>
                <guid isPermaLink="false">66d461523a8352b6c5a2ab1d</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ux design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Spruce Emmanuel ]]>
                </dc:creator>
                <pubDate>Mon, 08 Feb 2021 21:00:38 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/02/20210207_172754_0000-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A while ago I wanted to create a dark theme for my <a target="_blank" href="https://spruce.com.ng">personal site</a>. So I did some clicking around to find out the most suitable and <strong>clean</strong> way to do this.</p>
<p>I read <a target="_blank" href="https://mxb.dev/blog/color-theme-switcher">Max Bock's article on creating a custom theme</a>, where he explained the process quite clearly. He also really went super pro (with TEN different color schemes).</p>
<p>But for my case I wanted more. I wanted users to be able to change the color scheme to the different options provided.</p>
<p>I also wanted them to be able to change the font size. This is because I had a fixed header on my site which was kind of great, but on small mobile devices it took up quiet a lot of space – not great for UX design, right? So I also gave users the ability to turn off that fixed header.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/02/spruce-theme.png" alt="spruce.com.ng customizable static website preview with dark theme" width="600" height="400" loading="lazy"></p>
<p><em>Spruce.com.ng Theme-able static site</em></p>
<p>You can find a live preview of this on my personal site <a target="_blank" href="https://spruce.com.ng">spruce.com.ng</a>. You can also copy the source code <a class="post-section-overview" href="#not-much-of-a-tutorial-person-you-can-copy-the-complete-source-code-here">here</a> to save you some read time.</p>
<h2 id="heading-what-i-wanted-to-do">What I Wanted to Do</h2>
<ol>
<li><p>Ask users their preferred color scheme, font size, and header type (fixed or static)</p>
</li>
<li><p>Collect user choices</p>
</li>
<li><p>Save them in localStorage</p>
</li>
<li><p>Get them from localStorage and show them to the user immediately on page reload, if they switch tabs and come back, and if they close their browser and come back after a week or month, until they clear their browser storage</p>
</li>
</ol>
<h2 id="heading-how-i-created-the-theme">How I Created the Theme</h2>
<p>In 11ty (the static site generator I'm using) you can create a JSON file in the <code>_data</code> folder. You can access the data globally in your template (Jekyll does this too). It's likely that your preferred static site generator (SSG) can do the same.</p>
<pre><code class="lang-json">_data/themes.json file

[
    {
        <span class="hljs-attr">"id"</span>: <span class="hljs-string">"default"</span>,
        <span class="hljs-attr">"colors"</span>: {
            <span class="hljs-attr">"text"</span>: <span class="hljs-string">"#222126"</span>,
            <span class="hljs-attr">"text-dark"</span>: <span class="hljs-string">"#777;"</span>,
            <span class="hljs-attr">"border"</span>: <span class="hljs-string">"rgba(0,0,0,.1)"</span>,
            <span class="hljs-attr">"primary"</span>: <span class="hljs-string">"#665df5"</span>,
            <span class="hljs-attr">"secondary"</span>: <span class="hljs-string">"#6ad1e0"</span>,
            <span class="hljs-attr">"primary-dark"</span>: <span class="hljs-string">"#382cf1"</span>,
            <span class="hljs-attr">"bg"</span>: <span class="hljs-string">"#ffffff"</span>,
            <span class="hljs-attr">"bg-alt"</span>: <span class="hljs-string">"#f8f8f8"</span>,
            <span class="hljs-attr">"overlay"</span>: <span class="hljs-string">"rgba(255, 255, 255, .4)"</span>
        }
                }, 
    ... other color schemes
]
</code></pre>
<h2 id="heading-how-to-generate-the-css">How to Generate the CSS</h2>
<p>To use the data file, create a file called <code>theme.css.liquid</code> and give it a permalink where you want the CSS file to output to.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">css</span>/<span class="hljs-selector-tag">theme</span><span class="hljs-selector-class">.css</span><span class="hljs-selector-class">.liquid</span> <span class="hljs-selector-tag">file</span>
<span class="hljs-selector-tag">---</span>
<span class="hljs-selector-tag">permalink</span>: /<span class="hljs-selector-tag">css</span>/<span class="hljs-selector-tag">theme</span><span class="hljs-selector-class">.css</span>
<span class="hljs-selector-tag">---</span>
// <span class="hljs-selector-tag">when</span> <span class="hljs-selector-tag">no</span> <span class="hljs-selector-tag">theme</span> <span class="hljs-selector-tag">is</span> <span class="hljs-selector-tag">selected</span>
// <span class="hljs-selector-tag">use</span> <span class="hljs-selector-tag">default</span> <span class="hljs-selector-tag">theme</span>
<span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--text</span>: {{ themes[<span class="hljs-number">0</span>].colors.text }};
    <span class="hljs-selector-tag">--text-dark</span>: {{ themes[0].colors.text-dark }};
    <span class="hljs-selector-tag">--border</span>: {{ themes[0].colors.border }};
    <span class="hljs-selector-tag">--primary</span>: {{ themes[0].colors.primary }};
    <span class="hljs-selector-tag">--secondary</span>: {{ themes[0].colors.secondary }};
    <span class="hljs-selector-tag">--primary-dark</span>: {{ themes[0].colors.primary-dark }};
    <span class="hljs-selector-tag">--bg</span>: {{ themes[0].colors.bg }};
    <span class="hljs-selector-tag">--bg-alt</span>: {{ themes[0].colors.bg-alt }};
}  
// <span class="hljs-selector-tag">if</span> <span class="hljs-selector-tag">user</span> <span class="hljs-selector-tag">preferred</span> <span class="hljs-selector-tag">color</span> <span class="hljs-selector-tag">scheme</span> <span class="hljs-selector-tag">is</span> <span class="hljs-selector-tag">dark</span>
// <span class="hljs-selector-tag">use</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">dark</span> <span class="hljs-selector-tag">theme</span>

<span class="hljs-keyword">@media</span>(prefers-color-scheme: dark) {
    <span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--text</span>: {{ themes[<span class="hljs-number">1</span>].colors.text }};
    <span class="hljs-selector-tag">--text-dark</span>: {{ themes[1].colors.text-dark }};
    <span class="hljs-selector-tag">--border</span>: {{ themes[1].colors.border }};
    <span class="hljs-selector-tag">--primary</span>: {{ themes[1].colors.primary }};
    <span class="hljs-selector-tag">--secondary</span>: {{ themes[1].colors.secondary }};
    <span class="hljs-selector-tag">--primary-dark</span>: {{ themes[1].colors.primary-dark }};
    <span class="hljs-selector-tag">--bg</span>: {{ themes[1].colors.bg }};
    <span class="hljs-selector-tag">--bg-alt</span>: {{ themes[1].colors.bg-alt }};
    }
}
// <span class="hljs-selector-tag">generate</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">theme</span> <span class="hljs-selector-tag">css</span> <span class="hljs-selector-tag">from</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">data</span> <span class="hljs-selector-tag">file</span>
// <span class="hljs-selector-tag">here</span> <span class="hljs-selector-tag">we</span> <span class="hljs-selector-tag">use</span> <span class="hljs-selector-tag">a</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">loop</span>
// <span class="hljs-selector-tag">to</span> <span class="hljs-selector-tag">iterate</span> <span class="hljs-selector-tag">over</span> <span class="hljs-selector-tag">all</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">themes</span> <span class="hljs-selector-tag">in</span> <span class="hljs-selector-tag">our</span> _<span class="hljs-selector-tag">data</span>/<span class="hljs-selector-tag">themes</span><span class="hljs-selector-class">.json</span>
// <span class="hljs-selector-tag">and</span> <span class="hljs-selector-tag">output</span> <span class="hljs-selector-tag">them</span> <span class="hljs-selector-tag">as</span> <span class="hljs-selector-tag">plain</span> <span class="hljs-selector-tag">css</span>


{% for theme in themes %}
 <span class="hljs-selector-attr">[data-theme=<span class="hljs-string">"{{ theme.id }}"</span>]</span> {
    <span class="hljs-attribute">--text</span>: {{ theme.colors.text }};
    <span class="hljs-selector-tag">--text-dark</span>: {{ theme.colors.text-dark }};
    <span class="hljs-selector-tag">--border</span>: {{ theme.colors.border }};
    <span class="hljs-selector-tag">--primary</span>: {{ theme.colors.primary }};
    <span class="hljs-selector-tag">--secondary</span>: {{ theme.colors.secondary }};
    <span class="hljs-selector-tag">--primary-dark</span>: {{ theme.colors.primary-dark }};
    <span class="hljs-selector-tag">--bg</span>: {{ theme.colors.bg }};
    <span class="hljs-selector-tag">--bg-alt</span>: {{ theme.colors.bg-alt }};
 }
{% endfor %}
</code></pre>
<p>Notice that I'm using <strong>themes[0].colors.text</strong> because my default theme is the first one on the list. It has an index of 0, so also my dark theme has an index of 1.</p>
<p>In <strong>Jekyll</strong> you can output liquid in CSS by just adding empty front matter at the top of the file.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">css</span>/<span class="hljs-selector-tag">theme</span><span class="hljs-selector-class">.css</span> <span class="hljs-selector-tag">file</span>
<span class="hljs-selector-tag">---</span>
<span class="hljs-selector-tag">---</span>

// <span class="hljs-selector-tag">your</span> <span class="hljs-selector-tag">liquid</span> <span class="hljs-selector-tag">in</span> <span class="hljs-selector-tag">css</span> <span class="hljs-selector-tag">goes</span> <span class="hljs-selector-tag">here</span>
</code></pre>
<p>I'm sure your favourite static site generator provides a similar way to output liquid in a CSS file. You can also handcode all this if you are just writing plain HTML and CSS without a SSG.</p>
<h2 id="heading-how-to-use-the-css-in-your-site">How to Use the CSS in Your Site</h2>
<p>If you are reading this, then I assume that you already know how to work with CSS custom properties. So I won't go in depth into that here.</p>
<pre><code class="lang-css">// <span class="hljs-selector-tag">css</span> <span class="hljs-selector-tag">custom</span> <span class="hljs-selector-tag">properties</span> <span class="hljs-selector-tag">are</span> <span class="hljs-selector-tag">declared</span> <span class="hljs-selector-tag">using</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-tag">keyword</span> **<span class="hljs-selector-tag">var</span>**
// <span class="hljs-selector-tag">color</span>: <span class="hljs-selector-tag">var</span>(<span class="hljs-selector-tag">--text</span>);
<span class="hljs-selector-tag">body</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">var</span>(--bg);
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text);
}
<span class="hljs-selector-tag">h1</span>,<span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text-dark)
}
// <span class="hljs-selector-tag">i</span> <span class="hljs-selector-tag">also</span> <span class="hljs-selector-tag">had</span> <span class="hljs-selector-tag">default</span> <span class="hljs-selector-tag">font-size</span> <span class="hljs-selector-tag">and</span> <span class="hljs-selector-tag">margin-top</span> <span class="hljs-selector-tag">properties</span> <span class="hljs-selector-tag">set</span>
// <span class="hljs-selector-tag">i</span> <span class="hljs-selector-tag">added</span> <span class="hljs-selector-tag">this</span> <span class="hljs-selector-tag">to</span> <span class="hljs-selector-tag">the</span> <span class="hljs-selector-pseudo">:root</span> <span class="hljs-selector-tag">in</span> <span class="hljs-selector-tag">css</span>
<span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--font-size</span>: <span class="hljs-number">18px</span>;
    <span class="hljs-attribute">--position</span>: fixed;
    <span class="hljs-attribute">--top-margin</span>: <span class="hljs-number">96px</span>;
}
</code></pre>
<p>You just have to change every bit of color on your site to the custom properties you have generated.</p>
<h2 id="heading-how-to-generate-the-html">How to Generate the HTML</h2>
<p>Now let's provide a UI to allow users to change the font size, header type, and color scheme of our site. Mine is a bit simple, but you can take yours further. I'm just explaining the concept here.</p>
<pre><code class="lang-html">theme.html file
// create the font buttons
// I gave each button a value
// I want to get the value and save it in local storage 

<span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"theme-section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"theme-btn-wrapper"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn--small btn--border js-font-btn"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"16"</span>&gt;</span>16px<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn--small btn--border js-font-btn"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"18"</span>&gt;</span>18px<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn--small btn--border js-font-btn"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"20"</span>&gt;</span>20px<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn--small btn--border js-font-btn"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"22"</span>&gt;</span>22px<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

// Create the toggle button
// To turn On &amp; Off
// The fixed header
// The **sr-only** is used to hide the text visually 
// while keeping accessibilty in mind
// note the **role="switch"** nd aria-checked
// they are what turns the button to a On and Off switch
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"check-wrapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"btn-label"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sr-only"</span>&gt;</span>Fixed or static header<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"switch"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">aria-checked</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">aria-labelledby</span>=<span class="hljs-string">"btn-label"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"js-theme-toggle btn btn--border btn--rounded btn--toggle"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>On<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Off<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>That's pretty much the HTML for my use case. Again you can do more if you want, and there is some CSS styling involved (which would be left out in our case).</p>
<h2 id="heading-the-fun-part-how-to-create-the-javascript">The Fun Part: How to Create the JavaScript</h2>
<pre><code class="lang-js">/assets/js/theme.js file
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomTheme</span> </span>{
    <span class="hljs-keyword">constructor</span>() {
        <span class="hljs-comment">// part A: check if localStorage works</span>
        <span class="hljs-built_in">this</span>.islocalStorage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
            <span class="hljs-keyword">try</span> {
                <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"test"</span>, <span class="hljs-string">"testing"</span>);
                <span class="hljs-built_in">localStorage</span>.removeItem(<span class="hljs-string">"test"</span>);
                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
            } <span class="hljs-keyword">catch</span> (error) {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
            }

        };
        <span class="hljs-comment">// part B: Get the value from the buttons</span>
        <span class="hljs-built_in">this</span>.schemeBtns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'.js-theme-color'</span>);
        <span class="hljs-built_in">this</span>.schemeBtns.forEach(<span class="hljs-function">(<span class="hljs-params">btn</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> btnVal = btn.value;
            btn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.themeScheme(btnVal))
        });

        <span class="hljs-built_in">this</span>.fontBtns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'.js-font-btn'</span>);
        <span class="hljs-built_in">this</span>.fontBtns.forEach(<span class="hljs-function">(<span class="hljs-params">btn</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> btnVal = btn.value;
            <span class="hljs-keyword">const</span> btnTag = btn;
            btn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.themeFont(btnVal, btnTag))
        });

        <span class="hljs-comment">// part C: get the html button element</span>
        <span class="hljs-built_in">this</span>.switchBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.js-theme-toggle'</span>);
        <span class="hljs-keyword">const</span> clicked = <span class="hljs-built_in">this</span>.switchBtn;
        <span class="hljs-built_in">this</span>.switchBtn.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">this</span>.themePosition(clicked))
    }

    <span class="hljs-comment">// part D: Save the data in localStorage</span>
    themeScheme(btnVal) {
        <span class="hljs-built_in">document</span>.documentElement.setAttribute(<span class="hljs-string">'data-theme'</span>, btnVal);
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.islocalStorage) {
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'theme-name'</span>, btnVal);
        }
    };

    themeFont(btnVal,btnTag) {
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--font-size'</span>, <span class="hljs-string">`<span class="hljs-subst">${btnVal}</span>px`</span>);
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.islocalStorage) {
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'font-size'</span>, btnVal);
        }
        ;
        <span class="hljs-keyword">if</span> (btnVal == <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'font-size'</span>)) {
            removeActive();
            btnTag.classList.add(<span class="hljs-string">'active'</span>);
    }
};

    themePosition(clicked) {
    <span class="hljs-keyword">if</span> (clicked.getAttribute(<span class="hljs-string">'aria-checked'</span>) == <span class="hljs-string">'true'</span>) {
        clicked.setAttribute(<span class="hljs-string">'aria-checked'</span>, <span class="hljs-string">'false'</span>);
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--position'</span>, <span class="hljs-string">'static'</span>);
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--top-margin'</span>, <span class="hljs-string">'0px'</span>);
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.islocalStorage) {
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'position'</span>, <span class="hljs-string">'static'</span>);
        }

    } <span class="hljs-keyword">else</span> {
        clicked.setAttribute(<span class="hljs-string">'aria-checked'</span>, <span class="hljs-string">'true'</span>);
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--position'</span>, <span class="hljs-string">'fixed'</span>);
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--top-margin'</span>, <span class="hljs-string">'96px'</span>);
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.islocalStorage) {
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'position'</span>, <span class="hljs-string">'fixed'</span>);
        }
    }

    }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeActive</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> btns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'.js-font-btn'</span>);
    btns.forEach(<span class="hljs-function">(<span class="hljs-params">btn</span>) =&gt;</span> {
        btn.classList.remove(<span class="hljs-string">'active'</span>);
    })
}

<span class="hljs-comment">// part E: Only use our class if css custom properties are supported</span>
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.CSS &amp;&amp; CSS.supports(<span class="hljs-string">'color'</span>, <span class="hljs-string">'var(--i-support'</span>)) {
    <span class="hljs-keyword">new</span> CustomTheme()
};

<span class="hljs-comment">// part E: Add an active class to selected font size button</span>

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'load'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> fontBtns = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'.js-font-btn'</span>);
    fontBtns.forEach(<span class="hljs-function">(<span class="hljs-params">btn</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> btnVal = btn.value;
        <span class="hljs-keyword">const</span> btnTag = btn;
        <span class="hljs-keyword">if</span> (btnVal == <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'font-size'</span>)) {
            btnTag.classList.add(<span class="hljs-string">'active'</span>);
    }
    });   
})
</code></pre>
<p>I know that's a big chunk of JavaScript code, but it basically only does a few things:</p>
<ul>
<li><p>it collects and checks if localStorage is supported</p>
</li>
<li><p>then it saves the data in localStorage</p>
</li>
</ul>
<p>Also notice that I used <strong>Javascript Classes</strong>, but you could use functions as well.</p>
<h3 id="heading-checking-for-local-storage">Checking for local storage</h3>
<p>A lot of browsers support localStorage these days, but why do we still need to check?</p>
<p>Some users may be browsing your site in <strong>incognito mode (private browsing mode)</strong>. And sometimes localStorage is turned off by default so it doesn't save anything on the users device.</p>
<p>So instead of saving it directly and sometimes getting an error on browsers that don't support it, we can check if the browser does support it. If it does, great – and if it doesn't then we're also cool.</p>
<p>Now if you notice, everything seems to work just fine. But if you change the theme or font size and you reload your browser, everything is going to revert to default. This is because we haven't used the data we stored in <strong>localStorage</strong></p>
<p>So go ahead and add this piece of code to the top of your head file before any CSS files. We're doing this to eliminate the flash you get when you reload your browser.</p>
<pre><code class="lang-js">&lt;script&gt;
    <span class="hljs-keyword">const</span> scheme = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'theme-name'</span>);
      <span class="hljs-built_in">document</span>.documentElement.setAttribute(<span class="hljs-string">'data-theme'</span>, scheme);

      <span class="hljs-keyword">const</span> fontSize = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'font-size'</span>);
    <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--font-size'</span>,  <span class="hljs-string">`<span class="hljs-subst">${fontSize}</span>px`</span>);


    <span class="hljs-keyword">const</span> position = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'position'</span>);
    <span class="hljs-keyword">if</span> (position == <span class="hljs-string">'fixed'</span>) {
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--position'</span>, <span class="hljs-string">'fixed'</span>);
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--top-margin'</span>, <span class="hljs-string">'96px'</span>);

    } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--position'</span>, <span class="hljs-string">'static'</span>);
        <span class="hljs-built_in">document</span>.documentElement.style.setProperty(<span class="hljs-string">'--top-margin'</span>, <span class="hljs-string">'0px'</span>);

    }    

  &lt;/script&gt;
</code></pre>
<h2 id="heading-wrapping-up">Wrapping up</h2>
<p>And that's it! You now have a simple and customizable static site.</p>
<p>The main purpose of this guide was to show you the endless possibilities of creating a user-customizable website. So go ahead and play around with it – there are a lot of things you can do, like:</p>
<ol>
<li><p>Show users specific content based on their choices</p>
</li>
<li><p>Display notification messages based on user's visits</p>
</li>
<li><p>Display ads in the least annoying way by showing users ads based on user choices</p>
</li>
</ol>
<p>You can do these things and a lot more with our SSG's. Just imagine the endless possibilities.</p>
<p>Not much of a tutorial person? You can copy the complete source code <a target="_blank" href="https://spruce.com.ng/blog/on-designing-a-themeable-static-website">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Blog Using a Static Site Generator and a CDN ]]>
                </title>
                <description>
                    <![CDATA[ By Aaron Katz I wanted to set up a fun project for myself to learn some new technologies. And this time I decided I wanted to learn a bit about Static Site Generators (SSGs). My goal was to build a blog using an SSG and have it deploy any time ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-blog-using-a-static-site-generator-and-a-cdn/</link>
                <guid isPermaLink="false">66d45d5fbd438296f45cd387</guid>
                
                    <category>
                        <![CDATA[ Blogging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ content delivery network  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 29 Jan 2021 16:46:23 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c95e0740569d1a4ca0ebe.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Aaron Katz</p>
<p>I wanted to set up a fun project for myself to learn some new technologies. And this time I decided I wanted to learn a bit about <strong>Static Site Generators</strong> (SSGs).</p>
<p>My goal was to build a blog using an SSG and have it deploy any time the code repository changed. You can see the result at <a target="_blank" href="https://caliburnsecurity.com">https://caliburnsecurity.com</a>.</p>
<h2 id="heading-blog-requirements">Blog Requirements</h2>
<p>Below are the requirements I put together when determining what I wanted my blog to support.</p>
<ul>
<li>Support Markdown for content generation</li>
<li>Syntax highlighting</li>
<li>Code line numbering</li>
<li>"Serverless"</li>
<li>Ready made themes/plugins - I am so NOT a frontend developer</li>
<li>SEO capabilities</li>
<li>Browse by keyword/category</li>
<li>Search support (because this is statically generated, searching is through Google's index - there are other articles that discuss how to add a dynamic JavaScript powered search)</li>
<li>RSS support</li>
<li>Version controlled</li>
<li>Static - no dynamic content (with a neat side effect of shrinking the site's attack surface and improving security)</li>
</ul>
<h2 id="heading-so-what-actually-is-a-static-site-generator">So what actually is a Static Site Generator?</h2>
<p>In short, an SSG is a framework designed to manage your website and transform it into a site serving only static pages.</p>
<h2 id="heading-why-use-a-ssg-to-build-the-blog">Why use a SSG to build the blog?</h2>
<p>One thing that took me some time to comprehend was why I would use a static site, if I already had a CMS. There were so many articles online about using an SSG with a headless CMS...but why?  </p>
<p>Best I can tell, the benefits are simply that you have more flexibility using familiar frameworks such as React or Vue, while using a CMS to handle all content. </p>
<p>However, as I am by no means a frontend developer, I was close to scrapping this project. I thought "Oh well, I should just use Ghost - it's only $5/month if hosted on a platform like DigitalOcean, and is an all-in-one platform to serve content as well as manage the content".</p>
<p>However, I really wanted to try to learn something new, and see if I could just deploy a blog for free using only Markdown.</p>
<p>As always ends up happening, what I was hoping would take a few hours took quite a bit more time as I went down the rabbit hole of research into various technologies. </p>
<p>I played around with several different technologies, such as (not all of these are SSGs, more on that later):</p>
<ul>
<li>Pelican (Python)</li>
<li>Hugo (Go)</li>
<li>Hexo</li>
<li>Gridsome (Vue - JS)</li>
<li>VuePress (Vue - JS)</li>
<li>Ghost</li>
<li>Gatsby (React - JS)</li>
<li>Jekyll (Ruby)</li>
</ul>
<h3 id="heading-why-i-chose-hugo">Why I Chose Hugo</h3>
<p>I won't go in too much detail on all of the technologies I looked at. But in general, I found Hugo to be super quick to set up and build, and just the <strong>SIMPLEST</strong> of all the options.  </p>
<p>While I know this is similar to Jekyll, I really just didn't want to deal with configuring a Ruby environment, and the speed of Hugo left everything else in the dust.</p>
<h2 id="heading-how-to-start-building-the-blog">How to Start Building the Blog</h2>
<p>For this exercise, let's build a static blog hosted by Netlify (free!).</p>
<p>Note: I'll be using PowerShell on my Windows box for this tutorial, so please recall that if copy/pasting.</p>
<h3 id="heading-make-sure-you-have-these-dependencies">Make sure you have these dependencies:</h3>
<ul>
<li>Git</li>
<li>Visual Studio Code (or your preferred editor)</li>
<li>Hugo binary</li>
</ul>
<h3 id="heading-heres-a-high-level-overview-of-our-workflow">Here's a high level overview of our workflow:</h3>
<ol>
<li>Download/Install Hugo</li>
<li>Create a Hugo project</li>
<li>Add and configure a theme</li>
<li>Add to Git</li>
<li>Deploy to Netlify</li>
<li>(Optional) Configure the free Netlify CMS</li>
</ol>
<h3 id="heading-download-or-install-hugo">Download or install Hugo</h3>
<p>To <a target="_blank" href="https://gohugo.io/getting-started/installing/">install Hugo</a>, I went over to their <a target="_blank" href="https://github.com/gohugoio/hugo/releases">GitHub Releases</a> page and downloaded their standalone Windows x64 binary. I placed it in my Projects directory, where we will be creating our site (you can always install it properly/add the binary to your PATH, but I wanted quick).</p>
<h2 id="heading-how-to-create-the-hugo-site">How to Create the Hugo Site</h2>
<p>To create a new site, simply run the below commands:</p>
<pre><code class="lang-powershell">.\hugo.exe new site hugo<span class="hljs-literal">-blog</span>
<span class="hljs-built_in">mv</span> .\hugo.exe .\hugo<span class="hljs-literal">-blog</span>
<span class="hljs-built_in">cd</span> .\hugo<span class="hljs-literal">-blog</span>
.\hugo.exe server <span class="hljs-literal">-D</span> -<span class="hljs-literal">-gc</span>
</code></pre>
<p>We now have our project created, and have just started the Hugo server. We used the <strong>-D</strong> flag to tell Hugo to show draft content, and I typically add in <strong>--gc</strong> to ensure that cleanup is run each time by clearing the cache.  </p>
<p>You can access your site at http://localhost:1313.</p>
<h3 id="heading-understanding-the-directory-structure">Understanding the directory structure</h3>
<p>You should now see the following directory structure:</p>
<pre><code class="lang-shell">|__archetypes
|__assets *this will not show up by default
|__config *this will not show up by default
|__content
|__data
|__layouts
|__static
|__themes
|__config.toml
</code></pre>
<ul>
<li><strong>archetypes</strong>: Content template files with pre-configured front matter. We won't really be touching this.</li>
<li><strong>assets</strong>: Store any files processed by <a target="_blank" href="https://gohugo.io/hugo-pipes/">Hugo Pipes</a>. This is out of scope for this tutorial.</li>
<li><strong>config</strong>: Optional directory to store configuration files. We won't be doing anything too crazy, so we will just use the default config.toml file.</li>
<li><strong>content</strong>: This is where your content lives - your posts and pages. The top level folders within this directory are treated as a <em>section</em>.</li>
<li><strong>data</strong>: Contains configuration files used by Hugo. I never had a need to touch this directory.</li>
<li><strong>layouts</strong>: Stores partial or full page HTML templates for the site. Anything in here can overwrite a corresponding entry from your theme, allowing you to customize the theme without modifying the theme's actual files.</li>
<li><strong>static</strong>: Store any static content, such as images, CSS, or scripts. Anything in this folder is copied as-is, without any modification or interpretation by Hugo. This is where you will store your media, such as images, for reference in your blog posts.</li>
<li><strong>themes</strong>: The directory where you will install any Hugo themes.</li>
<li><strong>config.toml</strong>: The default site configuration. You can use a separate directory if you want to separate different environments.</li>
</ul>
<h2 id="heading-how-to-add-your-first-theme">How to Add Your First Theme</h2>
<p>For this blog, we will use the <a target="_blank" href="https://github.com/EmielH/tale-hugo">tale</a> theme for Hugo. Run the following commands from the root of the project:</p>
<pre><code class="lang-powershell">git submodule add https://github.com/EmielH/tale<span class="hljs-literal">-hugo</span>.git .\themes\tale
</code></pre>
<p>We will <strong>NOT</strong> be editing any files from the theme, but will make all modifications in the <strong>layouts</strong> folder discussed above. This will let us always update the submodule to update our theme without worry that we will overwrite any changes we have made.</p>
<p>To initialize the theme, edit the <strong>config.toml</strong> in your root directory and add the following lines (while also editing the defaults):</p>
<pre><code class="lang-toml"><span class="hljs-comment"># Theme Settings</span>
<span class="hljs-attr">theme</span> = <span class="hljs-string">"tale"</span>
<span class="hljs-section">[params]</span>
  <span class="hljs-attr">Author</span> = <span class="hljs-string">"Aaron Katz"</span> <span class="hljs-comment"># Add the name of the author (this theme only supports one author)</span>
<span class="hljs-section">[author]</span>
  <span class="hljs-attr">name</span> = <span class="hljs-string">"Caliburn Security"</span> <span class="hljs-comment"># Used by the foot copyright</span>
</code></pre>
<p>There we go, the theme is now active! (Note that in many cases, themes will require you to copy and paste the theme's <strong>theme.toml</strong> file into your <strong>config.toml</strong>)</p>
<p>Go ahead and check out your page – every time you save a file Hugo rebuils the site live.</p>
<h3 id="heading-how-to-modify-theme-files">How to modify theme files</h3>
<p>One issue with the current theme is that non-post content will be displayed in the homepage list.  </p>
<p>To change this, let's copy the <strong>.\themes\tale\layouts\index.html</strong> page to <strong>.\layouts\index.html</strong>. </p>
<p>Once there, replace: <code>{{range (.Paginate .Site.RegularPages).Pages}}</code> with <code>{{ range where .Paginator.Pages "Section" "post" }}</code>. This will ensure only the "post" section will be displayed in the list.</p>
<p>I also wanted to add a brief footer, so go ahead and create a new file at <strong>.\layouts\footer.html</strong> and add the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-symbol">&amp;copy;</span> <span class="hljs-tag">&lt;<span class="hljs-name">time</span> <span class="hljs-attr">datetime</span>=<span class="hljs-string">"{{ now }}"</span>&gt;</span>{{ now.Format "2006" }}<span class="hljs-tag">&lt;/<span class="hljs-name">time</span>&gt;</span> {{ .Site.Author.name }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-add-google-analytics">How to Add Google Analytics</h3>
<p>I also wanted to add some Google Analytics to my blog, and I noticed the theme didn't incorporate this functionality.  </p>
<p>Luckily, Hugo makes adding analytics extremely simple. Open up the <strong>config.toml</strong> file and add the following line:</p>
<pre><code class="lang-toml"><span class="hljs-attr">googleAnalytics</span> = <span class="hljs-string">""</span> <span class="hljs-comment"># The UA-XXX number from Google Analytics</span>
</code></pre>
<p>Once the configuration is saved, copy the <strong>.\themes\tale\layouts\partial\head.html</strong> file to <strong>.\layouts\partial\head.html</strong> and add the following code right below the <em>head</em> tag:</p>
<pre><code class="lang-go">{{ template <span class="hljs-string">"_internal/google_analytics_async.html"</span> . }}
</code></pre>
<p>There we go, now we have Google Analytics working. Cool!</p>
<h2 id="heading-how-to-write-content">How to Write Content</h2>
<p>Let's add a nice About page so people know everything there is about me. :)</p>
<pre><code class="lang-powershell">.\hugo.exe new about.md
</code></pre>
<p>To ensure that this page is added to the main menu, add the following line to the page's front matter: <code>menu: main</code>.</p>
<p><em>Note: To build Hugo, which will generate the content under the <strong>.\public</strong> folder, simply run <code>.\hugo.exe</code></em></p>
<h3 id="heading-what-is-front-matter">What is front matter?</h3>
<p>This was a new term for me. Essentially, front matter is just structured metadata for your content. </p>
<p>By default, your template will add the following metadata fields to each page or post you create:</p>
<ul>
<li>title</li>
<li>date</li>
<li>draft</li>
</ul>
<p>Other potentially useful front matter elements are:</p>
<ul>
<li>description - This allows you to enter a description for the content.</li>
<li>expiryDate - Set a datetime for when the content should no longer be published.</li>
<li>keywords - The meta keywords for the content.</li>
<li>lastmod - The datetime for when the content was last modified (if you are using the enableGitInfo configuration command, this will be automatically set as the last time the file was mofieid in Git)</li>
<li>markup - When set to "rst", you can use reStructuredText instead of Markdown (this feature is experimental)</li>
<li>publishDate - Set a date in the future for the content to be displayed.</li>
<li>slug - The tail of the output URL. Defaults to the filename if not specified.</li>
<li>summary - The text used when providing a summary of the article. I find this useful if I don't want the first paragraph to appear in the summary, which is the typical default.</li>
<li> - Use the plural form of the taxonomy index, such as <em>tags</em> or <em>categories</em>.</li>
</ul>
<h4 id="heading-how-to-create-an-archetype-for-blog-posts">How to create an archetype for blog posts</h4>
<p>Let's go ahead and change the default front matter we see for blog posts. In the archetypes folder, create a new file called <strong>posts.md</strong> and add the following:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ replace .Name "-" " " | title }}</span>"</span>
<span class="hljs-attr">date:</span> {{ <span class="hljs-string">.Date</span> }}
<span class="hljs-attr">draft:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">slug:</span> {{ <span class="hljs-string">.File.BaseFileName</span> }} <span class="hljs-comment"># Will take the filename as the slug. Feel free to change this to any format you like.  I like including this, so that I remind myself I have the option to change if I want.</span>

<span class="hljs-attr">summary:</span> <span class="hljs-string">""</span> <span class="hljs-comment"># Remove this if you want Hugo to just use the first 70 (configurable) characters of the post as the summary.</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">""</span>

<span class="hljs-comment"># Lists</span>
<span class="hljs-attr">keywords:</span>
<span class="hljs-attr">tags:</span>
<span class="hljs-attr">categories:</span>
<span class="hljs-meta">---</span>
</code></pre>
<p>Now let's do one final build with <code>.\hugo.exe</code> and get ready to configure our Git repository.</p>
<h2 id="heading-how-to-configure-git">How to Configure Git</h2>
<p>Time to configure the project for Git:</p>
<pre><code class="lang-powershell">git init
git remote add origin &lt;YOUR GIT URL&gt;
git push <span class="hljs-literal">-u</span> origin master
</code></pre>
<p>Great, we now have our code stored within our repository, and we are ready for deployment!</p>
<h2 id="heading-how-to-deploy-your-blog">How to Deploy Your Blog</h2>
<p>Now that we have our site stored in Git, it's time to deploy! Almost done - I'll now show how to deploy to Netlify in under 10 minutes.</p>
<p>First, we need to create the Netlify application (feel free to create an account using any method available):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-192.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, we need to create the site and tell it where our Git repository is for our Hugo content:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-193.png" alt="Image" width="600" height="400" loading="lazy">
<em>Tell Netlify where your site is located</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-194.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select the repository</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-195.png" alt="Image" width="600" height="400" loading="lazy">
<em>These can be left at the default settings</em></p>
<p>Next up, we will set up a custom domain for our site:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-196.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select the custom domain option</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-197.png" alt="Image" width="600" height="400" loading="lazy">
<em>Enter your custom domain</em></p>
<p>You should now see your domain with a message saying to <strong>Check DNS configuration</strong>. Click on that, and enter the provided DNS record information into whichever service provider manages your DNS records:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-198.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-200.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-201.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example configuration within my Cloudflare account</em></p>
<p>Once complete, wait a few minutes for the DNS settings to propagate, and then select <strong>Verify DNS configuration:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-202.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Behold, your site is now live!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-203.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example of my new blog hosted on Netlify!</em></p>
<p>Last, we should set up SSL for our site as a best practice. Netlify offers the option to use <a target="_blank" href="http://letsencrypt.org/howitworks/">Let's Encrypt</a> to automatically provision a certificate for your application. To do so, simply select <strong>Provision certificate:</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-204.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Note: It can take quite some time for the certificate to be generated, so just be patient.</em></p>
<h3 id="heading-netlify-deployment-settings">Netlify deployment settings</h3>
<p>We have one final step before we are truly ready to use Netlify. Unfortunately, the version of Hugo used by Netlify is somewhat outdated by default. However, we can fix this by creating our own configuration for Netlify to follow when deploying our site.</p>
<p>First, create a file called <code>netlify.toml</code> in the root of your repository, and then add the following configuration:</p>
<pre><code class="lang-toml"><span class="hljs-section">[build]</span>
<span class="hljs-attr">publish</span> = <span class="hljs-string">"public"</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify"</span>

<span class="hljs-section">[context.production.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>
<span class="hljs-attr">HUGO_ENV</span> = <span class="hljs-string">"production"</span>
<span class="hljs-attr">HUGO_ENABLEGITINFO</span> = <span class="hljs-string">"true"</span>

<span class="hljs-section">[context.split1]</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify --enableGitInfo"</span>

<span class="hljs-section">[context.split1.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>
<span class="hljs-attr">HUGO_ENV</span> = <span class="hljs-string">"production"</span>

<span class="hljs-section">[context.deploy-preview]</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL"</span>

<span class="hljs-section">[context.deploy-preview.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>

<span class="hljs-section">[context.branch-deploy]</span>
<span class="hljs-attr">command</span> = <span class="hljs-string">"hugo --gc --minify -b $DEPLOY_PRIME_URL"</span>

<span class="hljs-section">[context.branch-deploy.environment]</span>
<span class="hljs-attr">HUGO_VERSION</span> = <span class="hljs-string">"0.74.3"</span>

<span class="hljs-section">[context.next.environment]</span>
<span class="hljs-attr">HUGO_ENABLEGITINFO</span> = <span class="hljs-string">"true"</span>
</code></pre>
<p>All that's left to do is select <strong>Deploy site</strong> within the Netlify console, and your site is now live on a custom domain with SSL!</p>
<h2 id="heading-wrapup">Wrapup</h2>
<p>Whew! That was a lengthy blog post, but hopefully this shows how quick it is to get up and running with a "serverless" blog. Let's see what I learned :)</p>
<h3 id="heading-what-i-loved">What I loved</h3>
<ul>
<li>Super simple to build, just run <code>hugo serve</code></li>
<li>Live reload - make a change, save, and the page will reload</li>
<li>Just simple in general - I didn't need to deal with grunt, gulp, webpack, or others</li>
<li>Customizable output formats let you generate your static site, as well as a Google AMP site, JSON files, and so on.</li>
<li>FAST. Did I mention fast?</li>
<li>Can be deployed just about anywhere - whether using Netlify (my current choice), Amazon S3 &amp; Cloudfront, Heroku, GitHub Pages, and more.</li>
<li>Shortcodes are available if Markdown isn't enough</li>
<li>Continuous deployment - everything is version controlled, and deployed when I publish to the master branch</li>
<li>Allow commenting and sharing of posts</li>
</ul>
<h3 id="heading-challenges">Challenges</h3>
<ul>
<li>Hugo is sometimes too simple.  No plugins or extensions at all, etc.</li>
<li>Using Go is less intuitive and the shortcode feels messier than something like Vue</li>
<li>Not too many themes available, but I expect the library to keep growing, as there is a very active user base</li>
</ul>
<h3 id="heading-so-do-i-need-a-cms">So do I need a CMS?</h3>
<p>After all of this, I still had this question in the back of my mind.  And the answer is, "it depends".  </p>
<p>If I were to incorporate a lot of media, such as images or videos that I need to upload, it would certainly get tedious adding and organizing them all to the images folder in <strong>static</strong>. </p>
<p>At that point, I would look into a headless CMS such as Ghost, Netlify, or Sanity to manage the content, as long as I could still write my posts using Markdown.</p>
<h3 id="heading-references">References</h3>
<ul>
<li>https://medium.com/backticks-tildes/hugo101-getting-started-with-hugo-and-deploying-to-netlify-9a813fe23b94</li>
<li>https://blog.risingstack.com/static-site-generator-hugo-netlify/</li>
<li>http://cloudywithachanceofdevops.com/posts/2018/05/17/setting-up-google-analytics-on-hugo/</li>
<li>https://www.sitepoint.com/premium/books/a-beginner-s-guide-to-creating-a-static-website-with-hugo/read/1</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Static Site Generation? How Next.js Uses SSG for Dynamic Web Apps ]]>
                </title>
                <description>
                    <![CDATA[ Static websites are as old as the web itself. But the rise of JavaScript has opened the door to make those static sites more dynamic.  While that can include building an HTML file by hand, how can we leverage static generation to build apps with mode... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/static-site-generation-with-nextjs/</link>
                <guid isPermaLink="false">66b8e3886a98b2a27ee1f350</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 18 Nov 2020 17:49:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/11/static-generation.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Static websites are as old as the web itself. But the rise of JavaScript has opened the door to make those static sites more dynamic. </p>
<p>While that can include building an HTML file by hand, how can we leverage static generation to build apps with modern tools?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-static-generation">What is Static Generation?</a></li>
<li><a class="post-section-overview" href="#heading-what-happens-during-static-generation">What happens during Static Generation?</a></li>
<li><a class="post-section-overview" href="#heading-how-does-nextjs-use-static-generation">How does Next.js use Static Generation?</a></li>
<li><a class="post-section-overview" href="#statically-generating-an-app-with-next-js">Statically generating an app with Next.js</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/6ElI2ZJ4Uro" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-is-static-generation">What is Static Generation?</h2>
<p>Static Generation describes the process of compiling and rendering a website or app at build time. The output is a bunch of static files, including the HTML file itself and assets like JavaScript and CSS.</p>
<p>If you haven’t heard of Static Generation but that concept sounds familiar, you might have heard of it by its longer name Static Site Generation or its acronym SSG.</p>
<h2 id="heading-what-happens-during-static-generation">What happens during Static Generation?</h2>
<p>JavaScript-based web apps as we traditionally know them work by running libraries like React or scripts at run time in the browser. </p>
<p>When the browser receives the page, it’s usually simple HTML without a lot of content. This then loads the scripts to pull the content into the page, a process also known as hydration.</p>
<p>With Static Generation, tools like Next.js try to render that page mostly like it would in the browser but at compile time. This gives us the ability to serve the entire content on first load. The scripts still hydrate the page during this process, but ideally with fewer changes or no changes at all.</p>
<h2 id="heading-how-does-nextjs-use-static-generation">How does Next.js use Static Generation?</h2>
<p>Out of the box, Next.js will attempt to statically generate any pages that it can. It does this by detecting how an app is fetching its data.</p>
<p>Next.js provides a <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching">few different APIs to fetch data</a> including <code>getStaticProps</code> and <code>getServerSideProps</code>, which, depending on how they’re used, determines how Next.js will build your app.</p>
<p>If you only use <code>getStaticProps</code> to fetch data, Next.js will fetch that data at build time, leaving you with a completely static page. </p>
<p>If you use <code>getServerSideProps</code>, Next.js will know that the app requires a server to render those pages. </p>
<p>Alongside a deployment solution like Vercel that <a target="_blank" href="https://vercel.com/solutions/nextjs">will automatically handle configuring a server</a>, Next.js will load any of the data when someone requests the page from the server.</p>
<p>While it doesn’t do it by default, Next.js also provides the ability to export the app into static files into a separate directory after the app has been built.</p>
<p>First, you would run the <code>next build</code> command to build the app, then you would run <code>next export</code> which, by default, makes the app available as static files in the <code>out</code> directory.</p>
<h2 id="heading-how-to-statically-generate-an-app-with-nextjs">How to statically generate an app with Next.js</h2>
<p>To get an idea of how this works, we can quickly create a new Next.js app.</p>
<p>The only requirements for this is that you have <a target="_blank" href="https://nodejs.org/en/">Node</a> installed with npm and the ability to use a terminal to run commands.</p>
<h3 id="heading-how-to-create-a-nextjs-app">How to create a Next.js app</h3>
<p>Getting started is as simple as running a single line in the terminal.</p>
<p>Open up the directory you’d like to create your project in and run:</p>
<pre><code>npx create-next-app my-<span class="hljs-keyword">static</span>-nextjs-app
</code></pre><p>After the installation is complete, you can navigate to your new project directory:</p>
<pre><code>cd my-<span class="hljs-keyword">static</span>-nextjs-app
</code></pre><p>Once there, start your development server:</p>
<pre><code>npm run dev
</code></pre><p>And once the server is ready, you can open up <a target="_blank" href="http://localhost:3000">http://localhost:3000</a> in your browser where you can now see your new Next.js app!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/new-nextjs-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New Next.js app</em></p>
<h3 id="heading-how-to-build-a-nextjs-app">How to build a Next.js app</h3>
<p>Now that we have our application available, let’s try to build it.</p>
<p>In the same directory, run the command:</p>
<pre><code>npm run build
</code></pre><p>If you look at the output inside of the terminal, we see a few important things happen.</p>
<p>First, Next.js lets us know that it’s running through its build process, including optimizing the app for performance, compiling the app, and collecting data.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/nextjs-build.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Building with Next.js</em></p>
<p>Next, we see that Next.js shows us a breakdown of how it’s built each page.</p>
<p>The default Next.js starting template includes a few static pages and an example API route. </p>
<p>Using the legend at the bottom, we can see that all of the pages and assets were statically generated with one route tagged as requiring a server, which would be our API route.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/nextjs-static-generation.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Next.js generating pages</em></p>
<p><em>Note: For the purposes of this walkthrough, we can ignore the API route, but Next.js along with Vercel provides the ability to build lambda functions as part of the Next.js API.</em></p>
<h3 id="heading-how-to-build-a-static-nextjs-app">How to build a static Next.js app</h3>
<p>With our Next.js build output, we know that we just built some static pages, but we might have trouble finding them. If we look at the folders and files in our project, it’s not immediately clear where those files are.</p>
<p>When Next.js builds an app, by default, it only outputs that app inside the <code>.next</code> directory. This includes configuration files that tools like Vercel can use and understand to deploy the app.</p>
<p>Technically, that directory includes our entire app, but this isn’t something we can easily deploy to static hosting.</p>
<p>Next.js also provides the ability to export an app. This will take the app that we built and produce a set of static files which we can then use to deploy our app.</p>
<p>Inside of the <code>package.json</code> file, update the <code>build</code> script to include <code>next export</code>:</p>
<pre><code><span class="hljs-string">"build"</span>: <span class="hljs-string">"next build &amp;&amp; next export"</span>,
</code></pre><p>Once updated, run the build command again in the project directory:</p>
<pre><code>npm run build
</code></pre><p>And now we can see that not only did we build the app like we did in our last step, Next.js lets us know that we’re also exporting the app that we built into static files.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/nextjs-exporting-static-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Exporting static Next.js app</em></p>
<p>If we look inside of our project folder, we should now see a new directory called <code>out</code>.</p>
<p>If we look inside of that folder, we can now see our entire app statically compiled including the <code>index.html</code> file as well as all of the CSS and JS needed to use the app!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/nextjs-static-output.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-where-can-we-go-from-here">Where can we go from here?</h2>
<p>We learned that we can use Next.js and the concept of Static Generation to statically compile an app. </p>
<p>Tools like Next.js can do this by compiling our code, similar to what we might see in a browser, so that by the time our app hits the browser, it’s all ready to go.</p>
<p>With a simple command, we can also build and compile our app, as well as export it into static files. We can deploy those static files to any static storage service like Vercel or AWS S3. This provides us with an easy way to craft dynamic web apps that are fast and cheap.</p>
<p>Learn more about how Next.js leverages its different APIs to provide both static and dynamic experiences by <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching">visiting the Next.js docs</a>.</p>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://jamstackhandbook.com/">
      <img src="https://www.freecodecamp.org/news/content/images/size/w1600/2020/11/jamstack-handbook-banner.jpg" alt="Jamstack Handbook" width="600" height="400" loading="lazy">
    </a>
  </p>
</div>

<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">🐦 Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">📺 Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">📫 Sign Up For My Newsletter</a>
    </li>
    <li>
      <a href="https://github.com/sponsors/colbyfayock">💝 Sponsor Me</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Hugo vs Jekyll: an Epic Battle of Static Site Generator Themes ]]>
                </title>
                <description>
                    <![CDATA[ In this article, we'll compare the nuances of creating themes for the top two static site generators. I recently took on the task of creating a documentation site theme for two projects. Both projects needed the same basic features, but one uses Jeky... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/hugo-vs-jekyll-battle-of-static-site-generator-themes/</link>
                <guid isPermaLink="false">66bd8f5f27629f4c5e1893b2</guid>
                
                    <category>
                        <![CDATA[ Hugo ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JAMstack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ jekyll ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                    <category>
                        <![CDATA[ themes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ website development, ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Victoria Drake ]]>
                </dc:creator>
                <pubDate>Mon, 27 Apr 2020 13:42:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/04/cover-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, we'll compare the nuances of creating themes for the top two static site generators.</p>
<p>I recently took on the task of creating a documentation site theme for two projects. Both projects needed the same basic features, but one uses Jekyll while the other uses Hugo.</p>
<p>In typical developer rationality, there was clearly only one option. I decided to create the same theme in both frameworks, and to give you, dear reader, a side-by-side comparison.</p>
<p>This post isn’t a comprehensive theme-building guide, but is rather intended to familiarize you with the process of building a theme in either generator. Here's what we'll cover:</p>
<ul>
<li>How theme files are organized</li>
<li>Where to put content</li>
<li>How templating works</li>
<li>Creating a top-level menu with the <code>pages</code> object</li>
<li>Creating a menu with nested links from a data list</li>
<li>Putting the template together</li>
<li>Creating styles</li>
<li>How to configure and deploy to GitHub Pages</li>
</ul>
<p>Here’s a crappy wireframe of the theme I’m going to create.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/wireframe.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you’re planning to build-along, it may be helpful to serve the theme locally as you build it – and both generators offer this functionality. For Jekyll, run <code>jekyll serve</code>, and for Hugo, <code>hugo serve</code>.</p>
<p>There are two main elements: the main content area, and the all-important sidebar menu. To create them, you’ll need template files that tell the site generator how to generate the HTML page. To organize theme template files in a sensible way, you first need to know what  directory structure the site generator expects.</p>
<h2 id="heading-how-theme-files-are-organized">How theme files are organized</h2>
<p>Jekyll supports gem-based themes, which users can install like any other Ruby gems. This method hides theme files in the gem, so for the purposes of this comparison, we aren’t using gem-based themes.</p>
<p>When you run <code>jekyll new-theme &lt;name&gt;</code>, Jekyll will scaffold a new theme for you. Here’s what those files look like:</p>
<pre><code class="lang-sh">.
├── assets
├── Gemfile
├── _includes
├── _layouts
│   ├── default.html
│   ├── page.html
│   └── post.html
├── LICENSE.txt
├── README.md
├── _sass
└── &lt;name&gt;.gemspec
</code></pre>
<p>The directory names are appropriately descriptive. The <code>_includes</code> directory is for small bits of code that you reuse in different places,  in much the same way you’d put butter on everything. (Just me?) </p>
<p>The <code>_layouts</code> directory contains templates for different types of pages on your site. The <code>_sass</code> folder is for <a target="_blank" href="https://sass-lang.com/documentation/syntax">Sass</a> files used to build your site’s stylesheet.</p>
<p>You can scaffold a new Hugo theme by running <code>hugo new theme &lt;name&gt;</code>. It has these files:</p>
<pre><code class="lang-sh">.
├── archetypes
│   └── default.md
├── layouts
│   ├── 404.html
│   ├── _default
│   │   ├── baseof.html
│   │   ├── list.html
│   │   └── single.html
│   ├── index.html
│   └── partials
│       ├── footer.html
│       ├── header.html
│       └── head.html
├── LICENSE
├── static
│   ├── css
│   └── js
└── theme.toml
</code></pre>
<p>You can see some similarities. Hugo’s page template files are tucked into <code>layouts/</code>. Note that the <code>_default</code> page type has files for a <code>list.html</code> and a <code>single.html</code>. </p>
<p>Unlike Jekyll, Hugo uses these specific file names to distinguish between <a target="_blank" href="https://gohugo.io/templates/lists/">list pages</a> (like a page with links to all your blog posts on it) and <a target="_blank" href="https://gohugo.io/templates/single-page-templates/">single pages</a> (like one of your blog posts). The <code>layouts/partials/</code> directory contains the buttery reusable bits, and stylesheet files have a spot picked out in <code>static/css/</code>.</p>
<p>These directory structures aren’t set in stone, as both site generators allow some measure of customization. For example, Jekyll lets  you define <a target="_blank" href="https://jekyllrb.com/docs/collections/">collections</a>, and Hugo makes use of <a target="_blank" href="https://gohugo.io/content-management/page-bundles/">page bundles</a>. These features let you organize your content multiple ways, but for now, let's look at where to put some simple pages.</p>
<h2 id="heading-where-to-put-content">Where to put content</h2>
<p>To create a site menu that looks like this:</p>
<pre><code class="lang-md">Introduction
<span class="hljs-code">    Getting Started
    Configuration
    Deploying
Advanced Usage
    All Configuration Settings
    Customizing
    Help and Support</span>
</code></pre>
<p>You’ll need two sections (“Introduction” and “Advanced Usage”) containing their respective subsections.</p>
<p>Jekyll isn’t strict with its content location. It expects pages in the root of your site, and will build whatever’s there. Here’s how you might organize these pages in your Jekyll site root:</p>
<pre><code class="lang-sh">.
├── 404.html
├── assets
├── Gemfile
├── _includes
├── index.markdown
├── intro
│   ├── config.md
│   ├── deploy.md
│   ├── index.md
│   └── quickstart.md
├── _layouts
│   ├── default.html
│   ├── page.html
│   └── post.html
├── LICENSE.txt
├── README.md
├── _sass
├── &lt;name&gt;.gemspec
└── usage
    ├── customizing.md
    ├── index.md
    ├── settings.md
    └── support.md
</code></pre>
<p>You can change the location of the site source in your <a target="_blank" href="https://jekyllrb.com/docs/configuration/default/">Jekyll configuration</a>.</p>
<p>In Hugo, all rendered content is expected in the <code>content/</code> folder. This prevents Hugo from trying to render pages you don’t want, such as <code>404.html</code>, as site content. Here’s how you might organize your <code>content/</code> directory in Hugo:</p>
<pre><code class="lang-sh">.
├── _index.md
├── intro
│   ├── config.md
│   ├── deploy.md
│   ├── _index.md
│   └── quickstart.md
└── usage
    ├── customizing.md
    ├── _index.md
    ├── settings.md
    └── support.md
</code></pre>
<p>To Hugo, <code>_index.md</code> and <code>index.md</code> mean different things. It can be helpful to know what kind of <a target="_blank" href="https://gohugo.io/content-management/page-bundles/">Page Bundle</a> you want for each section: Leaf, which has no children, or Branch.</p>
<p>Now that you have some idea of where to put things, let’s look at how to build a page template.</p>
<h2 id="heading-how-templating-works">How templating works</h2>
<p>Jekyll page templates are built with the <a target="_blank" href="https://jekyllrb.com/docs/liquid/">Liquid templating language</a>. It uses braces to output variable content to a page, such as the page’s title: <code>{{ page.title }}</code>.</p>
<p>Hugo’s templates also use braces, but they’re built with <a target="_blank" href="https://gohugo.io/templates/introduction/">Go Templates</a>. The syntax is similar, but different: <code>{{ .Title }}</code>.</p>
<p>Both Liquid and Go Templates can handle logic. Liquid uses <em>tags</em> syntax to denote logic operations:</p>
<pre><code class="lang-liquid">{% if user %}
  Hello {{ user.name }}!
{% endif %}
</code></pre>
<p>And Go Templates places its functions and arguments in its braces syntax:</p>
<pre><code class="lang-go">{{ <span class="hljs-keyword">if</span> .User }}
    Hello {{ .User }}!
{{ end }}
</code></pre>
<p>Templating languages allow you to build one  skeleton HTML page, then tell the site generator to put variable content in areas you define. Let’s compare two possible <code>default</code> page templates for Jekyll and Hugo.</p>
<p>Jekyll’s scaffold <code>default</code> theme is bare, so we’ll look at their starter theme <a target="_blank" href="https://github.com/jekyll/minima">Minima</a>. Here’s <code>_layouts/default.html</code> in Jekyll (Liquid):</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"{{ page.lang | default: site.lang | default: "</span><span class="hljs-attr">en</span>" }}"&gt;</span>

  {%- include head.html -%}

  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

    {%- include header.html -%}

    <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page-content"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Content"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>
        {{ content }}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>

    {%- include footer.html -%}

  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Here’s Hugo’s scaffold theme <code>layouts/_default/baseof.html</code> (Go Templates):</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
    {{- partial "head.html" . -}}
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        {{- partial "header.html" . -}}
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"content"</span>&gt;</span>
        {{- block "main" . }}{{- end }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {{- partial "footer.html" . -}}
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Different syntax, same idea. Both templates pull in reusable bits for <code>head.html</code>, <code>header.html</code>, and <code>footer.html</code>.  These show up on a lot of pages, so it makes sense not to have to  repeat yourself. </p>
<p>Both templates also have a spot for the main content, though the Jekyll template uses a variable (<code>{{ content }}</code>) while Hugo uses a block (<code>{{- block "main" . }}{{- end }}</code>). <a target="_blank" href="https://gohugo.io/templates/base/#readout">Blocks</a> are just another way Hugo lets you define reusable bits.</p>
<p>Now that you know how templating works, you can build the sidebar menu for the theme.</p>
<h2 id="heading-creating-a-top-level-menu-with-the-pages-object">Creating a top-level menu with the <code>pages</code> object</h2>
<p>You can programmatically create a top-level menu from your pages. It will look like this:</p>
<pre><code class="lang-md">Introduction
Advanced Usage
</code></pre>
<p>Let’s start with Jekyll. You can display links to site pages in your Liquid template by iterating through the <code>site.pages</code> object that Jekyll provides and building a list:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    {% for page in site.pages %}
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ page.url | absolute_url }}"</span>&gt;</span>{{ page.title }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    {% endfor %}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<p>This returns all of the site’s pages, including all the ones that you might not want, like <code>404.html</code>. You can filter for the pages you actually want with a couple more tags, such as conditionally including pages if they have a <code>section: true</code> parameter set:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    {% for page in site.pages %}
    {%- if page.section -%}
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ page.url | absolute_url }}"</span>&gt;</span>{{ page.title }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    {%- endif -%}
    {% endfor %}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<p>You can achieve the same effect with slightly less code in Hugo. Loop through Hugo’s <code>.Pages</code> object using Go Template’s <a target="_blank" href="https://golang.org/pkg/text/template/#hdr-Actions"><code>range</code> action</a>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
{{ range .Pages }}
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{.Permalink}}"</span>&gt;</span>{{.Title}}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
{{ end }}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<p>This template uses the <code>.Pages</code> object to return all the top-level pages in <code>content/</code> of your Hugo site. Since Hugo uses a specific folder for the site content you want rendered, there’s no additional filtering necessary to build a simple menu of site pages.</p>
<h2 id="heading-creating-a-menu-with-nested-links-from-a-data-list">Creating a menu with nested links from a data list</h2>
<p>Both site generators can use a separately defined data list of links to render a menu in your template. This is more suitable for creating nested links, like this:</p>
<pre><code class="lang-md">Introduction
<span class="hljs-code">    Getting Started
    Configuration
    Deploying
Advanced Usage
    All Configuration Settings
    Customizing
    Help and Support</span>
</code></pre>
<p>Jekyll supports <a target="_blank" href="https://jekyllrb.com/docs/datafiles/">data files</a> in a few formats, including YAML. Here’s the definition for the menu above in <code>_data/menu.yml</code>:</p>
<pre><code class="lang-yml"><span class="hljs-attr">section:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">Introduction</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">/intro</span>
    <span class="hljs-attr">subsection:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">Getting</span> <span class="hljs-string">Started</span>
        <span class="hljs-attr">url:</span> <span class="hljs-string">/intro/quickstart</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">Configuration</span>
        <span class="hljs-attr">url:</span> <span class="hljs-string">/intro/config</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">Deploying</span>
        <span class="hljs-attr">url:</span> <span class="hljs-string">/intro/deploy</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">Advanced</span> <span class="hljs-string">Usage</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">/usage</span>
    <span class="hljs-attr">subsection:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">Customizing</span>
        <span class="hljs-attr">url:</span> <span class="hljs-string">/usage/customizing</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">All</span> <span class="hljs-string">Configuration</span> <span class="hljs-string">Settings</span>
        <span class="hljs-attr">url:</span> <span class="hljs-string">/usage/settings</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">page:</span> <span class="hljs-string">Help</span> <span class="hljs-string">and</span> <span class="hljs-string">Support</span>
        <span class="hljs-attr">url:</span> <span class="hljs-string">/usage/support</span>
</code></pre>
<p>Here’s how to render the data in the sidebar template:</p>
<pre><code class="lang-html">{% for a in site.data.menu.section %}
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ a.url }}"</span>&gt;</span>{{ a.page }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    {% for b in a.subsection %}
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ b.url }}"</span>&gt;</span>{{ b.page }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    {% endfor %}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
{% endfor %}
</code></pre>
<p>This method allows you to build a custom menu, two nesting levels deep. The nesting levels are limited by the <code>for</code> loops in the template. For a recursive version that handles further levels of nesting, see <a target="_blank" href="https://jekyllrb.com/tutorials/navigation/#scenario-9-nested-tree-navigation-with-recursion">Nested tree navigation with recursion</a>.</p>
<p>Hugo does something similar with its <a target="_blank" href="https://gohugo.io/templates/menu-templates/#section-menu-for-lazy-bloggers">menu templates</a>. You can define menu links in your <a target="_blank" href="https://gohugo.io/getting-started/configuration/">Hugo site config</a>, and even add useful properties that Hugo understands, like weighting. Here’s a definition of the menu above in <code>config.yaml</code>:</p>
<pre><code class="lang-yml"><span class="hljs-attr">sectionPagesMenu:</span> <span class="hljs-string">main</span>

<span class="hljs-attr">menu:</span>  
  <span class="hljs-attr">main:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">identifier:</span> <span class="hljs-string">intro</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">Introduction</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/intro/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">1</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Getting</span> <span class="hljs-string">Started</span>
      <span class="hljs-attr">parent:</span> <span class="hljs-string">intro</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/intro/quickstart/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">1</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Configuration</span>
      <span class="hljs-attr">parent:</span> <span class="hljs-string">intro</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/intro/config/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">2</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploying</span>
      <span class="hljs-attr">parent:</span> <span class="hljs-string">intro</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/intro/deploy/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">3</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">identifier:</span> <span class="hljs-string">usage</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">Advanced</span> <span class="hljs-string">Usage</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/usage/</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Customizing</span>
      <span class="hljs-attr">parent:</span> <span class="hljs-string">usage</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/usage/customizing/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">2</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">All</span> <span class="hljs-string">Configuration</span> <span class="hljs-string">Settings</span>
      <span class="hljs-attr">parent:</span> <span class="hljs-string">usage</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/usage/settings/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">1</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Help</span> <span class="hljs-string">and</span> <span class="hljs-string">Support</span>
      <span class="hljs-attr">parent:</span> <span class="hljs-string">usage</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/usage/support/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">3</span>
</code></pre>
<p>Hugo uses the <code>identifier</code>, which must match the section name, along with the <code>parent</code> variable to handle nesting. Here’s how to render the menu in the sidebar template:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    {{ range .Site.Menus.main }}
    {{ if .HasChildren }}
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ .URL }}"</span>&gt;</span>{{ .Name }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sub-menu"</span>&gt;</span>
        {{ range .Children }}
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ .URL }}"</span>&gt;</span>{{ .Name }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        {{ end }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    {{ else }}
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ .URL }}"</span>&gt;</span>{{ .Name }}<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    {{ end }}
    {{ end }}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</code></pre>
<p>The <code>range</code> function iterates over the menu data, and Hugo’s <code>.Children</code> variable handles nested pages for you.</p>
<h2 id="heading-putting-the-template-together">Putting the template together</h2>
<p>With your menu in your reusable sidebar bit (<code>_includes/sidebar.html</code> for Jekyll and <code>partials/sidebar.html</code> for Hugo), you can add it to the <code>default.html</code> template.</p>
<p>In Jekyll:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"{{ page.lang | default: site.lang | default: "</span><span class="hljs-attr">en</span>" }}"&gt;</span>

{%- include head.html -%}

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    {%- include sidebar.html -%}

    {%- include header.html -%}

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page-content"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Content"</span>&gt;</span>
        {{ content }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    {%- include footer.html -%}

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>In Hugo:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
{{- partial "head.html" . -}}

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    {{- partial "sidebar.html" . -}}

    {{- partial "header.html" . -}}
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page-content"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Content"</span>&gt;</span>
        {{- block "main" . }}{{- end }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    {{- partial "footer.html" . -}}
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>When the site is generated, each page will contain all the code from your <code>sidebar.html</code>.</p>
<h2 id="heading-create-a-stylesheet">Create a stylesheet</h2>
<p>Both site generators accept Sass for creating CSS stylesheets. Jekyll <a target="_blank" href="https://jekyllrb.com/docs/assets/">has Sass processing built in</a>, and Hugo uses <a target="_blank" href="https://gohugo.io/hugo-pipes/scss-sass/">Hugo Pipes</a>. Both options have some quirks.</p>
<h3 id="heading-sass-and-css-in-jekyll">Sass and CSS in Jekyll</h3>
<p>To process a Sass file in Jekyll, create your style definitions in the <code>_sass</code> directory. For example, in a file at <code>_sass/style-definitions.scss</code>:</p>
<pre><code class="lang-scss"><span class="hljs-variable">$background-color</span>: <span class="hljs-number">#eef</span> !default;
<span class="hljs-variable">$text-color</span>: <span class="hljs-number">#111</span> !default;

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$background-color</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-variable">$text-color</span>;
}
</code></pre>
<p>Jekyll won’t generate this file directly, as it only processes files with front matter. To create the end-result  filepath for your site’s stylesheet, use a placeholder with empty front matter where you want the <code>.css</code> file to appear. For example, <code>assets/css/style.scss</code>. In this file, simply import your styles:</p>
<pre><code class="lang-scss">---
---

<span class="hljs-keyword">@import</span> <span class="hljs-string">"style-definitions"</span>;
</code></pre>
<p>This rather hackish configuration has an upside: you can use Liquid template tags and variables in your placeholder file. This is a nice way to allow users to set variables from the site <code>_config.yml</code>, for example.</p>
<p>The resulting CSS stylesheet in your generated site has the path <code>/assets/css/style.css</code>. You can link to it in your site’s <code>head.html</code> using:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ "</span>/<span class="hljs-attr">assets</span>/<span class="hljs-attr">css</span>/<span class="hljs-attr">style.css</span>" | <span class="hljs-attr">relative_url</span> }}" <span class="hljs-attr">media</span>=<span class="hljs-string">"screen"</span>&gt;</span>
</code></pre>
<h3 id="heading-sass-and-hugo-pipes-in-hugo">Sass and Hugo Pipes in Hugo</h3>
<p>Hugo uses <a target="_blank" href="https://gohugo.io/hugo-pipes/scss-sass/">Hugo Pipes</a> to process Sass to CSS. You can achieve this by using Hugo’s asset processing function, <code>resources.ToCSS</code>, which expects a source in the <code>assets/</code> directory. It takes the SCSS file as an argument. </p>
<p>With your style definitions in a Sass file at <code>assets/sass/style.scss</code>, here’s how to get, process, and link your Sass in your theme’s <code>head.html</code>:</p>
<pre><code class="lang-html">{{ $style := resources.Get "/sass/style.scss" | resources.ToCSS }}
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ $style.RelPermalink }}"</span> <span class="hljs-attr">media</span>=<span class="hljs-string">"screen"</span>&gt;</span>
</code></pre>
<p>Hugo asset processing <a target="_blank" href="https://gohugo.io/troubleshooting/faq/#i-get-tocss--this-feature-is-not-available-in-your-current-hugo-version">requires extended Hugo</a>, which you may not have by default. You can get extended Hugo from the <a target="_blank" href="https://github.com/gohugoio/hugo/releases">releases page</a>.</p>
<h2 id="heading-configure-and-deploy-to-github-pages">Configure and deploy to GitHub Pages</h2>
<p>Before your site generator can build your site, it needs a  configuration file to set some necessary parameters. Configuration files  live in the site root directory. Among other settings, you can declare  the name of the theme to use when building the site.</p>
<h3 id="heading-configure-jekyll">Configure Jekyll</h3>
<p>Here’s a minimal <code>_config.yml</code> for Jekyll:</p>
<pre><code class="lang-yml"><span class="hljs-attr">title:</span> <span class="hljs-string">Your</span> <span class="hljs-string">awesome</span> <span class="hljs-string">title</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">&gt;-</span> <span class="hljs-comment"># this means to ignore newlines until "baseurl:"</span>
  <span class="hljs-string">Write</span> <span class="hljs-string">an</span> <span class="hljs-string">awesome</span> <span class="hljs-string">description</span> <span class="hljs-string">for</span> <span class="hljs-string">your</span> <span class="hljs-string">new</span> <span class="hljs-string">site</span> <span class="hljs-string">here.</span> <span class="hljs-string">You</span> <span class="hljs-string">can</span> <span class="hljs-string">edit</span> <span class="hljs-string">this</span>
  <span class="hljs-string">line</span> <span class="hljs-string">in</span> <span class="hljs-string">_config.yml.</span> <span class="hljs-string">It</span> <span class="hljs-string">will</span> <span class="hljs-string">appear</span> <span class="hljs-string">in</span> <span class="hljs-string">your</span> <span class="hljs-string">document</span> <span class="hljs-string">head</span> <span class="hljs-string">meta</span> <span class="hljs-string">(for</span>
  <span class="hljs-string">Google</span> <span class="hljs-string">search</span> <span class="hljs-string">results)</span> <span class="hljs-string">and</span> <span class="hljs-string">in</span> <span class="hljs-string">your</span> <span class="hljs-string">feed.xml</span> <span class="hljs-string">site</span> <span class="hljs-string">description.</span>
<span class="hljs-attr">baseurl:</span> <span class="hljs-string">""</span> <span class="hljs-comment"># the subpath of your site, e.g. /blog</span>
<span class="hljs-attr">url:</span> <span class="hljs-string">""</span> <span class="hljs-comment"># the base hostname &amp; protocol for your site, e.g. http://example.com</span>
<span class="hljs-attr">theme:</span> <span class="hljs-comment"># for gem-based themes</span>
<span class="hljs-attr">remote_theme:</span> <span class="hljs-comment"># for themes hosted on GitHub, when used with GitHub Pages</span>
</code></pre>
<p>With <code>remote_theme</code>, any <a target="_blank" href="https://help.github.com/en/github/working-with-github-pages/adding-a-theme-to-your-github-pages-site-using-jekyll#adding-a-jekyll-theme-in-your-sites-_configyml-file">Jekyll theme hosted on GitHub can be used</a> with sites hosted on GitHub Pages.</p>
<p>Jekyll has a <a target="_blank" href="https://jekyllrb.com/docs/configuration/default/">default configuration</a>, so any parameters added to your configuration file will override the defaults. Here are <a target="_blank" href="https://jekyllrb.com/docs/configuration/options/">additional configuration settings</a>.</p>
<h3 id="heading-configure-hugo">Configure Hugo</h3>
<p>Here’s a minimal example of Hugo’s <code>config.yml</code>:</p>
<pre><code class="lang-yml"><span class="hljs-attr">baseURL:</span> <span class="hljs-string">https://example.com/</span> <span class="hljs-comment"># The full domain your site will live at</span>
<span class="hljs-attr">languageCode:</span> <span class="hljs-string">en-us</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">Hugo</span> <span class="hljs-string">Docs</span> <span class="hljs-string">Site</span>
<span class="hljs-attr">theme:</span> <span class="hljs-comment"># theme name</span>
</code></pre>
<p>Hugo makes no assumptions, so if a necessary parameter is missing, you’ll see a warning when building or serving your site. Here are <a target="_blank" href="https://gohugo.io/getting-started/configuration/#all-configuration-settings">all configuration settings for Hugo</a>.</p>
<h3 id="heading-deploy-to-github-pages">Deploy to GitHub Pages</h3>
<p>Both generators build your site with a command.</p>
<p>For Jekyll, use <code>jekyll build</code>. See <a target="_blank" href="https://jekyllrb.com/docs/configuration/options/#build-command-options">further build options here</a>.</p>
<p>For Hugo, use <code>hugo</code>. You can run <code>hugo help</code> or see <a target="_blank" href="https://gohugo.io/getting-started/usage/#test-installation">further build options here</a>.</p>
<p>You’ll have to choose the source for your GitHub Pages site. Once done, your site will update each time you push a new build. Of course, you can also automate your GitHub Pages build using GitHub Actions. Here’s one for <a target="_blank" href="https://github.com/victoriadrake/hugo-latest-cd">building and deploying with Hugo</a>, and one for <a target="_blank" href="https://github.com/victoriadrake/jekyll-cd">building and deploying Jekyll</a>.</p>
<h2 id="heading-showtime">Showtime!</h2>
<p>All the substantial differences between these two generators are under the hood. All the same, let’s take a look at the finished themes, in two color variations.</p>
<p>Here’s Hugo:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/ogd_hugo.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here's Jekyll:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/04/ogd_jekyll.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-wait-who-won">Wait who won?</h2>
<p>?</p>
<p>Both Hugo and Jekyll have their quirks and conveniences.</p>
<p>From this developer’s perspective, Jekyll is a workable choice for simple sites without complicated organizational needs. If you’re looking to render some one-page posts in an <a target="_blank" href="https://jekyllrb.com/docs/themes/">available theme</a> and host with GitHub Pages, Jekyll will get you up and running fairly quickly.</p>
<p>Personally, I use Hugo. I like the organizational capabilities of its Page Bundles, and it’s backed by a dedicated and conscientious team that really seems to strive to facilitate convenience for their users. This is evident in Hugo’s many functions, and handy tricks like <a target="_blank" href="https://gohugo.io/content-management/image-processing/">Image Processing</a> and <a target="_blank" href="https://gohugo.io/content-management/shortcodes/">Shortcodes</a>. They seem to release new fixes and versions about as often as I make a new cup of coffee - which, depending on your use case, may be fantastic, or annoying.</p>
<p>If you still can’t decide, don’t worry. The <a target="_blank" href="https://github.com/opengitdocs">OpenGitDocs documentation theme</a> I created is available for both Hugo and Jekyll. Start with one, switch later if you want. That’s the benefit of having options.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Publish a no-code website in 10 minutes ]]>
                </title>
                <description>
                    <![CDATA[ In this article, I'll introduce a no-code, no-software and no-cost solution to publishing sophisticated web sites managed by non-technical people. The full codebase is on GitHub here. Sir Issac Newton discovered the law of gravity when practicing “so... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/publish-a-no-code-website-in-10-minutes/</link>
                <guid isPermaLink="false">66d4604ec7632f8bfbf1e459</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub Actions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ github pages ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Hugo ]]>
                    </category>
                
                    <category>
                        <![CDATA[ No Code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 28 Mar 2020 20:59:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/igor-miske-Px3iBXV-4TU-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, I'll introduce a no-code, no-software and no-cost solution to publishing sophisticated web sites managed by non-technical people. The full codebase is <a target="_blank" href="https://github.com/second-state/hugo-website">on GitHub here</a>.</p>
<p>Sir Issac Newton discovered the law of gravity when practicing “social distancing” during the Plague. What will YOU do? One silver lining of quarantine is that all this free time brings out the entrepreneur spirit and creativity in us.</p>
<p>However, especially because of the quarantine, now more than ever, any new idea or project must have a web site. Traditional CMS solutions like Wordpress, Squarespace, or Wix are difficult to use, look dated, are expensive, or all of the above!</p>
<p>We wanted to create a web site that has a sophisticated look and feel, yet is easy to customize. A non-technical person should be able to edit the source files and see the changes appear on the live web site in a few minutes. Ideally, it should also be free (forever free, not just free-for-now), and can scale to handle millions of visitors if it becomes popular.</p>
<p>Is this possible?</p>
<p>In this short article, I will walk you through a solution based on the Hugo framework, GitHub Pages, and GitHub Actions. You can get up and running with your shiny new website by the end of this article.</p>
<blockquote>
<p>It is so easy that my 9-year-old son did it. He now manages a web site for his fictional country called <a target="_blank" href="https://www.arenztopia.com/">Arenztopia</a>. Check out the <a target="_blank" href="https://medium.com/@michaelyuan_88928/welcome-to-arenztopia-95cc85253163">back story</a>.</p>
</blockquote>
<h2 id="heading-tldr">TL;DR</h2>
<p>If you just want to get started with a working web site as soon as possible, first make sure that you have a free GitHub account.</p>
<p>Go to <a target="_blank" href="https://github.com/second-state/hugo-website">this GitHub repository</a>, and click on the “Fork” button on the top right, and <strong>fork</strong> it to your own account.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_edHfJdOu6Ppjiyd-M4tMJQ.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go to your forked repository, and click on the Actions tab. You will see a message like the one in the picture below. <strong>Click on</strong> the “I understand my workflows …” button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_nCrYAyGhCmLeFC2PMbUBEA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go to the Settings tab of your repository, and then scroll down to GitHub Pages. <strong>Re-select</strong> the <code>gh-pages</code> from the dropdown for the web site to build.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_K4qXL2CN2rtMonI4dXRWnA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Go to the Code tab of the repository, open the <code>config.toml</code> file, and edit it. <strong>Change</strong> its <code>title</code> attribute to something else, and click on the “Commit changes” button at the bottom. We need this step to trigger the workflow at the new repository.</p>
<p>Wait a few minutes, go to the “published at” web address at GitHub Pages and you will see the template web site.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_WRQaxyvCqagRMfsN6T1B_A.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, you can customize the site by editing the <code>config.toml</code> file and the files in the <code>content</code> folder. Go to the “Add your own content” section at the end of this article to see how. You can check out <a target="_blank" href="https://github.com/budparr/gohugo-theme-ananke#getting-started">the instructions for the Ananke theme here</a>.</p>
<p>That’s it for the quick start! In the next several sections, I will explain in more detail the concepts and processes.</p>
<h2 id="heading-hugo-basics">Hugo basics</h2>
<p>Older generation CMS solutions like Wordpress are too difficult to adapt to new web site designs, and commercially hosted solutions like SquareSpace are too expensive and not flexible enough. Static web site generators like Hugo offer a good balance among features, flexibility, and ease of authoring.</p>
<ul>
<li><p>Hugo web sites can be customized and modified through configuration files.</p>
</li>
<li><p>New pages and content sections can be written in markdown instead of HTML. That makes content authoring much easier.</p>
</li>
<li><p>There are many modern design themes to choose from.</p>
</li>
<li><p>The output of Hugo is a static HTML web site that can be deployed on any low-cost hosting provider.</p>
</li>
<li><p>Together with static web site hosting services like GitHub Pages and Netlify, they can offer a completely free solution.</p>
</li>
</ul>
<p>The Hugo software distribution is <a target="_blank" href="https://gohugo.io/getting-started/installing/">available</a> on all major operating systems. You can <a target="_blank" href="https://gohugo.io/getting-started/quick-start/">learn about it here</a>. But, since we will use GitHub Actions to automatically build our Hugo web sites, we do not actually need to install Hugo software here.</p>
<p>Here is how to do it.</p>
<h2 id="heading-create-a-template-website">Create a template website</h2>
<p>First, select a Hugo theme. There are <a target="_blank" href="https://themes.gohugo.io/">many</a>. Some are geared toward web sites with one or more content web pages, while others are optimized for blog-like sites with timestamped posts.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_CjWlyVScCKNkxDIxshwYTQ.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Hugo themes</em></p>
<p>Find one you like, download a zip package or clone a GitHub repo, and unpack the theme into a folder. Let’s assume that the theme distribution is unpacked into a folder called <code>my-theme</code>. The following are commands in a Linux terminal. You could use the Terminal app on Mac or PowerShell on Windows.</p>
<p>Next, create your web site project directory on your computer.</p>
<pre><code class="lang-javascript">$ mkdir -r my-site/themes
</code></pre>
<p>Copy the theme folder into your project.</p>
<pre><code class="lang-javascript">$ cp -r my-theme my-site/themes
</code></pre>
<p>Next, copy the theme’s <code>exampleSite</code> to the project’s top-level directory.</p>
<pre><code class="lang-javascript">$ cd my-site
$ cp -r themes/my-theme/exampleSite<span class="hljs-comment">/* ./</span>
</code></pre>
<p>Edit the <code>config.toml</code> in the project root directory <code>my-site/</code> to point to the right theme.</p>
<pre><code class="lang-javascript">baseURL = <span class="hljs-string">"/"</span>themesDir = <span class="hljs-string">"themes"</span>theme = <span class="hljs-string">"my-theme"</span>
</code></pre>
<p>Next, create a GitHub repo called <code>my-site</code>, and push the <code>my-site</code> directory onto its <code>master</code> branch. Here are the steps for <a target="_blank" href="https://help.github.com/en/github/managing-files-in-a-repository/adding-a-file-to-a-repository">uploading files from GitHub’s web UI</a>. Now we are ready to publish the theme example site.</p>
<p>For a Hugo-based system to be useable to a non-developer (or a young developer who has yet to master the command line tools), we must automate the process of building and deploying the static web site.</p>
<h2 id="heading-automate-deployment">Automate deployment</h2>
<p>In the GitHub project, go to Settings and enable GitHub Pages. Select the source to be the <code>gh-pages</code> branch.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_TrbrZti-GFpVBK0HtrezcA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Settings, GitHub Pages</em></p>
<p>Next, we create a GitHub Actions workflow to run the Hugo command on the source files from <code>master</code> branch, and push the generated HTML files to the <code>gh-pages</code> branch for publication. From the project’s Actions tab, click on the “set up a workflow yourself” button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_b5UuewhfFq2Gfa0vwJ8bqw.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Set up a workflow yourself</em></p>
<p>The workflow is stored in the <code>master</code> branch as <code>.github/workflows/main.yml</code> file. The content of the file is as follows.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">github</span> <span class="hljs-string">pages</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-18.04</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v1</span>  <span class="hljs-comment"># v2 does not have submodules option now</span>
        <span class="hljs-comment"># with:</span>
        <span class="hljs-comment">#   submodules: true</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Hugo</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">peaceiris/actions-hugo@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">hugo-version:</span> <span class="hljs-string">'0.62.2'</span>
          <span class="hljs-attr">extended:</span> <span class="hljs-literal">true</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">hugo</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">peaceiris/actions-gh-pages@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">github_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">publish_dir:</span> <span class="hljs-string">./public</span>
</code></pre>
<p>What happens here is that the web site authors and editors will change content and files on the <code>master</code> branch. Whenever new content is pushed to the <code>master</code> branch, the automated GitHub Actions workflow will <a target="_blank" href="https://github.com/peaceiris/actions-hugo/blob/master/README.md">set up the Hugo software</a>, run the <code>hugo</code> command, and turn those files into HTML files for a static web site.</p>
<p>The HTML files are <a target="_blank" href="https://github.com/peaceiris/actions-gh-pages/blob/master/README.md">pushed</a> to the <code>gh-pages</code> branch of the same repository. They will be published on the specified web address by GitHub Pages as configured.</p>
<p>Notice the <code>cname</code> attribute in the last line. That is the <a target="_blank" href="https://help.github.com/en/github/working-with-github-pages/configuring-a-custom-domain-for-your-github-pages-site">custom domain name</a> we set up with GitHub Pages. If you do not have a custom domain name, just remove this line, and you can access your web site at the domain provided by GitHub Pages.</p>
<p>Now go to the web site, and you should see the theme’s default web page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/1_fkJkt7YXYEZX96Qv3v5u8A.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>The HugoSerif template for one of our web sites.</em></p>
<h2 id="heading-add-your-own-content">Add your own content</h2>
<p>To change the default theme web site to your own content, you just need to change the files on the <code>master</code> branch. Please refer to the <a target="_blank" href="https://gohugo.io/content-management/organization/">documentation</a> of your selected theme. In general, Hugo templates work like this:</p>
<ul>
<li><p>The web pages are authored in markdown format and the <code>md</code> files are in the <code>content</code> folder.</p>
</li>
<li><p>Each <code>md</code> file has a header section with properties such as the page’s menu placement, priority, timestamp, excerpt, etc.</p>
</li>
<li><p>The overall configuration, such as the menu items and properties used by multiple pages, are stored in the <code>data</code> folder.</p>
</li>
<li><p>Static content such as raw HTML files, JavaScript files, and image files can be placed in the <code>static</code> folder.</p>
</li>
</ul>
<p>In particular, here is how you customize the Ananke theme that comes with our template:</p>
<ul>
<li><p>The <a target="_blank" href="https://github.com/second-state/hugo-website/blob/master/config.toml">config.toml</a> file allows you to configure the website title, social icons on all pages, and the big featured image on the home page.</p>
</li>
<li><p>All images should be uploaded to the <a target="_blank" href="https://github.com/second-state/hugo-website/tree/master/static/images">static/images</a> folder.</p>
</li>
<li><p>The <a target="_blank" href="https://github.com/second-state/hugo-website/blob/master/content/_index.md">content/_index.md</a> file contains the text for the home page.</p>
</li>
<li><p>To add pages to the site, you can just create <a target="_blank" href="https://guides.github.com/features/mastering-markdown/">markdown</a> files in the <a target="_blank" href="https://github.com/second-state/hugo-website/tree/master/content">content</a> folder. An example is the <a target="_blank" href="https://github.com/second-state/hugo-website/blob/master/content/contact.md">contact.md</a> file. Notice that at the top of the file, there are attributes to control whether this page should be on the website menu.</p>
</li>
<li><p>To add articles to the site, you can create markdown files in the <a target="_blank" href="https://github.com/second-state/hugo-website/tree/master/content/post">content/post</a> folder. Those are blog-like content articles that have dates and titles at the top. The most recent two articles will show up on the home page.</p>
</li>
</ul>
<p>If you are interested in learning more and see how we did it, you can watch our progress at</p>
<ul>
<li><p>The Country of Arenztopia [<a target="_blank" href="https://github.com/juntao/arenztopia">GitHub</a>] [<a target="_blank" href="https://www.arenztopia.com/">Web site</a>]</p>
</li>
<li><p>Second State blog [<a target="_blank" href="https://github.com/second-state/blog">GitHub</a>] [<a target="_blank" href="https://blog.secondstate.io/categories/en/">Web site</a>]</p>
</li>
</ul>
<p>Good luck and stay healthy!</p>
<h2 id="heading-about-the-author">About the author</h2>
<p>Dr. Michael Yuan is the <a target="_blank" href="http://www.michaelyuan.com/">author of 5 books</a> on software engineering. His latest book <a target="_blank" href="https://www.buildingblockchainapps.com/">Building Blockchain Apps</a> was published by Addison-Wesley in Dec 2019. Dr. Yuan is the co-founder of <a target="_blank" href="https://www.secondstate.io/">Second State</a>, a VC-funded startup that brings WebAssembly and Rust technologies to <a target="_blank" href="https://www.secondstate.io/articles/why-webassembly-server/">cloud</a>, <a target="_blank" href="https://docs.secondstate.io/">blockchain</a>, and <a target="_blank" href="https://github.com/second-state/rust-wasm-ai-demo/blob/master/README.md">AI</a> applications. It enables developers to deploy fast, safe, portable, and serverless <a target="_blank" href="https://www.secondstate.io/articles/getting-started-with-rust-function/">Rust functions on Node.js</a>.</p>
<div class="embed-wrapper"><iframe src="https://webassemblytoday.substack.com/embed" width="480" height="320" style="border:1px solid #EEE;background:white" title="Embedded content" loading="lazy"></iframe></div>

<p>Prior to Second State, Dr. Yuan was a long time open source contributor at Red Hat, JBoss, and Mozilla. Outside of software, Dr. Yuan is a Principal Investigator at the National Institutes of Health, with multiple research awards on cancer and public health research. He holds a PhD in astrophysics from the University of Texas at Austin.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to host and deploy a static website or JAMstack app to AWS S3 and CloudFront ]]>
                </title>
                <description>
                    <![CDATA[ S3 and CloudFront are AWS cloud services that make serving static assets powerful and cheap. How can we host a simple static website or JAMstack app on it? A little about AWS What are the benefits of serving from S3 and CloudFront? Before we start, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/</link>
                <guid isPermaLink="false">66bee8fdf53892da32acd273</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ beginners guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Services ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Solutions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cloudfront ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JAMstack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ S3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 11 Mar 2020 13:16:31 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/static-website-in-aws-s3.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>S3 and CloudFront are AWS cloud services that make serving static assets powerful and cheap. How can we host a simple static website or JAMstack app on it?</p>
<ul>
<li><a class="post-section-overview" href="#heading-a-little-about-aws">A little about AWS</a></li>
<li><a class="post-section-overview" href="#heading-what-are-the-benefits-of-serving-from-s3-and-cloudfront">What are the benefits of serving from S3 and CloudFront?</a></li>
<li><a class="post-section-overview" href="#heading-before-we-start-youll-need-an-aws-account">Before we start, you’ll need an AWS account</a></li>
<li><a class="post-section-overview" href="#heading-storing-your-website-on-s3">Storing your website on S3</a></li>
<li><a class="post-section-overview" href="#heading-serving-your-website-on-s3">Serving your website on S3</a></li>
<li><a class="post-section-overview" href="#heading-distributing-your-website-on-cloudfront">Distributing your website on CloudFront</a></li>
<li><a class="post-section-overview" href="#heading-custom-domain-names">Custom domain names</a></li>
<li><a class="post-section-overview" href="#heading-advanced-aws-usage">Advanced AWS Usage</a></li>
<li><a class="post-section-overview" href="#heading-resources">Resources</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/1lDGDzmbQWg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-a-little-about-aws">A little about AWS</h2>
<p>If you’re not familiar, <a target="_blank" href="https://aws.amazon.com/">AWS</a> (Amazon Web Services) is a cloud service provider that gives developers opportunities to build pretty much anything they can imagine in the cloud.</p>
<p>Though their <a target="_blank" href="https://aws.amazon.com/products/">services</a> extend beyond the likes of <a target="_blank" href="https://aws.amazon.com/machine-learning/">machine learning</a> and <a target="_blank" href="https://aws.amazon.com/ai/">artificial intelligence</a>, we’re going to stick with the entry level services for the purpose of this guide that will allow us to easily host an HTML website.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-services-overview.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Types of AWS services available</em></p>
<p>Building a site with S3 and CloudFront is a common recipe that small and high scale companies across the web use, but let’s break down what each service actually does.</p>
<h3 id="heading-object-storage-with-s3">Object storage with S3</h3>
<p><a target="_blank" href="https://aws.amazon.com/s3/">S3</a> (Simple Storage Service) acts as your hosting for your static website. Think of it like a hard drive in the cloud which we’re not able to use it for processing purposes, but rather for simple file storage and access.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-file-list.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>List of files from a static site in an AWS S3 bucket</em></p>
<p>When an app or website is compiled in static form, this is all we need to serve it to the people visiting our site. The HTML is sent in the initial request “as is” (unless there’s processing with your provider) and any additional work occurs after the page loads in the browser usually by JavaScript. This allows us to take this simple (and cheap) approach by serving these files from S3.</p>
<h3 id="heading-content-delivery-network-with-cloudfront">Content Delivery Network with CloudFront</h3>
<p><a target="_blank" href="https://aws.amazon.com/cloudfront/">CloudFront</a> works as a <a target="_blank" href="https://en.wikipedia.org/wiki/Content_delivery_network">CDN</a> (Content Delivery Network) that sits in front of your website, caching the files, and serving them directly to the people visiting your site.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/cdn-distribution-map.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>CDN Diagram</em></p>
<p>Where you host and serve your website from, typically called the origin, is the main source of your files and can serve the website itself. But putting a CDN in front of it provides the people accessing your content a shorter and faster way to make their request.</p>
<h2 id="heading-what-are-the-benefits-of-serving-from-s3-and-cloudfront">What are the benefits of serving from S3 and CloudFront?</h2>
<p>Given the rise in the <a target="_blank" href="https://jamstack.org/">JAMstack</a> era, many services are popping up that provide similar services for static sites that make it really easy to deploy. Some even come with a generous free tier like <a target="_blank" href="https://www.netlify.com/">Netlify</a> and <a target="_blank" href="https://zeit.co/">Zeit</a>!</p>
<p>But sometimes developers need a little bit more control over their services or they need to integrate into a larger cloud pipeline that’s already 99% percent in AWS, which is exactly where S3 shines. Also, chances are, during your first year you might still qualify for AWS’s <a target="_blank" href="https://aws.amazon.com/free/">free tier</a>.</p>
<h3 id="heading-fitting-in-to-the-aws-well-architected-framework">Fitting in to the AWS Well-Architected Framework</h3>
<p>As a lead provider in cloud services, AWS has published many guides to help developers and teams strive for excellence in their solutions in terms of performance, cost, and security.</p>
<p>One particular guideline is their 5 pillars of what they describe as a <a target="_blank" href="https://aws.amazon.com/architecture/well-architected/">“well-architected" infrastructure</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-well-architected-framework.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS Well-Architected Framework</em></p>
<p>By default, we check all of these boxes with our hosting solution by using S3 and CloudFront. Out of the box, the HTML and assets you serve will be fast, cheap, secure, and reliable.</p>
<h3 id="heading-the-beauty-of-static-and-jamstack-sites">The beauty of static and JAMstack sites</h3>
<p>Building on top of the pillars, what you’re actually serving is a static HTML file and group of assets that won’t require any type of rendering resources on the initial request. Before this, a common problem was having to worry about a site crashing due to heavy load. But with S3 and CloudFront, your website is infinitely scalable.</p>
<p>On a similar note, when that server scales up as it's trying to serve millions of hits on your post that went viral, so will your costs. Serving a static site is cheap and can greatly reduce the cost associated with running a web server.</p>
<h2 id="heading-before-we-start-youll-need-an-aws-account">Before we start, you’ll need an AWS account</h2>
<p>To work through this guide, you’ll need an AWS account. Luckily, it's free to create an account – you’ll only pay for the services used.</p>
<p>On top of that, AWS provides a generous free tier for some of its services. Some services provide only 12 months of a free tier (like S3) where others are always eligible for the free tier (like <a target="_blank" href="https://aws.amazon.com/lambda/">Lambda</a>), so make sure to do your homework so you don’t rack up an unexpectedly high bill.</p>
<p>To create your account, head over to the AWS website and then continue on to get started: <a target="_blank" href="https://aws.amazon.com/">https://aws.amazon.com/</a>.</p>
<h2 id="heading-storing-your-website-on-s3">Storing your website on S3</h2>
<p>To get started, we’re going to begin with a simple HTML file that will serve as our website. This will allow us to focus more on the process of hosting rather than the intricacies of the website itself.</p>
<h3 id="heading-creating-our-website-file">Creating our website file</h3>
<p>Begin by creating a new folder called <code>my-static-site</code>. Inside that folder, let's create a new file called <code>index.html</code> and add the following to the file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">“en”</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">“UTF-8”</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“viewport”</span> <span class="hljs-attr">content</span>=<span class="hljs-string">“width</span>=<span class="hljs-string">device-width,</span> <span class="hljs-attr">initial-scale</span>=<span class="hljs-string">1.0”</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Static Website<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello World!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is my static website. ?<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>If you open this file from your computer in your favorite browser, you should now be seeing this.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/hello-world-local-website-file-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Hello World! Opening a local webpage</em></p>
<h3 id="heading-creating-a-new-bucket">Creating a new bucket</h3>
<p>Head on over to your AWS account, log in, and navigate to your <a target="_blank" href="https://s3.console.aws.amazon.com/s3/">S3 console</a>.</p>
<p>Once there, let’s create our bucket by clicking on the blue <strong>Create bucket</strong> button:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-create-bucket.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a bucket in AWS S3</em></p>
<p>The first thing AWS wants us to do is enter a <strong>Bucket name</strong>. The bucket name must be globally unique, meaning, the name you use can be the only one in the world, so let’s try something like <code>[yourname]-static-website</code>, where I’ll use <code>colbyfayock-static-website</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-name.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Naming a bucket in AWS S3</em></p>
<p>Next, let’s set the <a target="_blank" href="https://aws.amazon.com/about-aws/global-infrastructure/regions_az/"><strong>Region</strong></a>. This is the geographic location where AWS will host the bucket and your website. You’re probably fine with the default, but if you’d like, you can select the location closest to you if it’s permitted. Since I’m in Virginia, I’m going to stick with my default of <strong>US East (N. Virginia)</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-region.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting the region of a bucket in AWS S3</em></p>
<p>Finally, hit the <strong>Create</strong> button on the bottom left of the page.</p>
<p><em>Note: even if you use the <code>[yourname]-static-website</code> pattern, there’s a chance the name will be taken. If it’s taken, AWS will show an error stating “Bucket name already exists,” at which point you’ll want to try a new name of your choosing.</em></p>
<p>Alternatively, you can hit <strong>Next</strong> for advanced usage, but for this guide, we’re okay with all of the defaults S3 provides.</p>
<p>If successful, you should now see your bucket in the list on the S3 console dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New bucket in AWS S3</em></p>
<h3 id="heading-uploading-your-website-to-the-bucket">Uploading your website to the bucket</h3>
<p>Let’s navigate to our new bucket by clicking the row of our bucket. You’ll be greeted with a message stating “This bucket is empty. Upload new objects to get started,” so that’s what we’ll do.</p>
<p>Click the <strong>Upload</strong> button to get started.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-upload.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Uploading files to AWS S3</em></p>
<p>You’ll then see a popup that will ask you to upload a file. Click on the <strong>Add files</strong> button and select your <code>index.html</code> file we created earlier.</p>
<p>Once selected, click the <strong>Upload</strong> button on the bottom left.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-upload-files.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Selecting files to upload in AWS S3</em></p>
<p>And now your file is uploaded to S3!</p>
<h2 id="heading-serving-your-website-on-s3">Serving your website on S3</h2>
<p>If you try to navigate to your <code>index.html</code> file and open it, you’ll notice a big ugly "Access Denied" message.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-access-denied.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Access Denied to bucket file</em></p>
<p>This is because your file doesn’t currently have the permissions and settings necessary to serve the file to the public, so let’s fix that.</p>
<h3 id="heading-setting-up-your-bucket-as-a-website">Setting up your bucket as a website</h3>
<p>Navigate to the <strong>Properties</strong> tab inside of your bucket, then click <strong>Static website hosting</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-properties-static-hosting.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up an AWS S3 bucket for statice website hosting</em></p>
<p>Once there, we want to do a few things:</p>
<ul>
<li>Note down the <strong>Endpoint</strong> at the top of the block. We’ll use this to access our site later (you can always find this here again)</li>
<li>Select the “Use this bucket to host a website” option</li>
<li>Enter <code>index.html</code> in the <strong>Index document</strong> field</li>
<li>Finally hit <strong>Save</strong></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-static-website-configuration.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring an AWS S3 bucket for static website hosting</em></p>
<h3 id="heading-setting-up-your-bucket-policy-and-permissions">Setting up your bucket policy and permissions</h3>
<p>Next, navigate to the <strong>Permissions</strong> tab. Here we’ll want to do 2 things: unblock all public access and add a Bucket Policy.</p>
<p>First, on the main page, let’s click <strong>Edit</strong> to unblock all access.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-permissions.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring an AWS S3 bucket permissions</em></p>
<p>Then, uncheck the “Block all public access” checkbox and hit <strong>Save</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-block-access.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Allowing public access to an AWS S3 bucket</em></p>
<p>AWS will ask you to confirm these settings, as this may not always be what you want to do with your bucket. But for the purposes of hosting a website, we want the whole world to see, so type in the word “confirm” and hit the <strong>Confirm</strong> button.</p>
<p>After confirming, click the <strong>Bucket policy</strong> button and you’ll be taken to a text editor.</p>
<p>In this text box, we’ll want to paste the following snippet. Within this snippet, make sure to replace <code>[your-bucket-name]</code> with the name of your bucket, otherwise you will not be able to save this file.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"Version"</span>:<span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-attr">"Statement"</span>:[{
    <span class="hljs-attr">"Sid"</span>:<span class="hljs-string">"PublicReadGetObject"</span>,
        <span class="hljs-attr">"Effect"</span>:<span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Principal"</span>: <span class="hljs-string">"*"</span>,
      <span class="hljs-attr">"Action"</span>:[<span class="hljs-string">"s3:GetObject"</span>],
      <span class="hljs-attr">"Resource"</span>:[<span class="hljs-string">"arn:aws:s3:::[your-bucket-name]/*”
      ]
    }
  ]
}</span>
</code></pre>
<p><a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteAccessPermissionsReqd.html#bucket-policy-static-site">This policy</a> states that it’s allowing the public to perform a GetObject request on the S3 resource, which is your S3 bucket.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-static-website-bucket-policy.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up a public policy for an AWS S3 bucket</em></p>
<p>After you add the policy, click the <strong>Save</strong> button. Your should now see a message stating "This bucket has public access.”</p>
<h3 id="heading-previewing-your-new-bucket-website">Previewing your new bucket website</h3>
<p>If you noted down the Endpoint from your Properties page, you can now visit that address to see your website. The endpoint should look like this:</p>
<pre><code class="lang-plaintext">http://[your-bucket-name].s3-website-[region-id].amazonaws.com
</code></pre>
<p>If you didn’t, jump back up a few steps to remind yourself how to find it or look under the Properties tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-static-website.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Hello World! Opening an AWS S3 website</em></p>
<p>Congrats, you're halfway there! ?</p>
<h2 id="heading-distributing-your-website-on-cloudfront">Distributing your website on CloudFront</h2>
<p>Now that we have our static website being served from a bucket on S3, let’s take it up another level and serve it across the world using CloudFront.</p>
<h3 id="heading-creating-a-cloudfront-distribution">Creating a CloudFront distribution</h3>
<p>Navigate to your <a target="_blank" href="https://console.aws.amazon.com/cloudfront">CloudFront dashboard</a> and click the <strong>Create Distribution</strong> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-create-distribution.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new distribution in AWS CloudFront</em></p>
<p>Next, select <strong>Get Started</strong> under the <strong>Web</strong> delivery method.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-creating-web-distribution.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Getting started with an AWS CloudFront distribution with Web delivery</em></p>
<p>Here, we’ll enter a few custom parameters to get our distribution set up.</p>
<p>Click into the <strong>Origin Domain Name</strong> field. Once selected, a dropdown list should appear where you can select the S3 bucket you just created. Go ahead and select your S3 bucket.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-origin-name-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting the origin domain name in AWS CloudFront to your bucket</em></p>
<p>While you can <a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html">customize most of the settings</a> to your liking, for our purposes, we’re going to leave all as their default values except for one.</p>
<p>Scroll down to the <strong>Default Root Object</strong> field and type <code>index.html</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-default-root-object-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting the Default Root Object for a distribution in AWS CloudFront</em></p>
<p>After, scroll down to the bottom and click <strong>Create Distribution</strong> in the bottom right.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-setup-create-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating an AWS CloudFront distribution</em></p>
<h3 id="heading-previewing-your-new-cloudfront-distribution">Previewing your new CloudFront distribution</h3>
<p>After hitting the <strong>Create</strong> button, it will take some time for your distribution to be created and set up. You’ll notice on the <strong>CloudFront Distributions</strong> list page that the <strong>Status</strong> of your new distribution is <strong>In Progress</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-in-progress-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS CloudFront distribution deployment is In Progress</em></p>
<p>Once this completes, it will say <strong>Deployed</strong>. Then you can find your <strong>Domain Name</strong> in the same row.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-deployed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS CloudFront distribution is Deployed</em></p>
<p>Using the value in the Domain Name column, open your distribution in your browser and success! You now are viewing your S3 bucket through CloudFront’s distribution network.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-static-website-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Hello World! Opening an AWS CloudFront website</em></p>
<h2 id="heading-custom-domain-names">Custom domain names</h2>
<p>While most of us will probably want to use a custom domain name with our website, we’re not going to dive too deep into that this guide, as there are many ways to set that up depending on where you purchase your domain name.</p>
<p>However, here are a few things to consider.</p>
<h3 id="heading-https-ssl-certificate">HTTPS / SSL Certificate</h3>
<p>If you’re creating your CloudFront distribution to use with a custom domain name, you'll most likely want to configure your distribution with an <a target="_blank" href="https://www.cloudflare.com/learning/ssl/what-is-an-ssl-certificate/">SSL certificate</a> using AWS’s <a target="_blank" href="https://aws.amazon.com/certificate-manager/">Certificate Manager</a>. Alternatively you can provide your own certificate with tools like <a target="_blank" href="https://letsencrypt.org/">Let's Encrypt</a>, but by using ACM, AWS makes it easy to pull in the records for use with your distribution.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/fay.io-ssl-certificate.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once in ACM, you’ll want to configure the certificate, map what domains and subdomains should match (typically <code>*.domain.com</code>), and then create your certificate to use with your distribution.</p>
<p>To get started, you can check out the AWS guide for <a target="_blank" href="https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html">requesting a public certificate</a>.</p>
<h3 id="heading-cnames-and-aliases">CNAMEs and Aliases</h3>
<p>A common approach to setting up a custom domain is to use a CNAME. CloudFront makes this pretty painless, as you’ll add it as a configuration option when you’re configuring your distribution.</p>
<p>To get started with setting up a CNAME in CloudFront, <a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html">see the AWS guide</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/fay.io-route53-alias.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you’re using <a target="_blank" href="https://aws.amazon.com/route53/">Route53</a> to manage your <a target="_blank" href="https://www.cloudflare.com/learning/dns/what-is-dns/">DNS</a>, you can then set up an A record (alias) to point to your distribution. You can learn more <a target="_blank" href="https://aws.amazon.com/premiumsupport/knowledge-center/route-53-create-alias-records/">using this guide</a>.</p>
<h2 id="heading-advanced-aws-usage">Advanced AWS Usage</h2>
<p>For this guide, we walked you through setting up a new static website and app using the AWS console. But whether you want to learn more, improve your deploy efficiency, or want to automate this process, you’ll want to take a it a step further with the AWS CLI or CloudFormation.</p>
<p>While we won’t walk you through how to use these tools here, we’ll get you started with a little bit of an idea of what you’re up against.</p>
<h3 id="heading-aws-cli">AWS CLI</h3>
<p>The <a target="_blank" href="https://aws.amazon.com/cli/">AWS CLI</a> allows someone to perform AWS operations from the command line. This can be incredibly powerful when you want to script out your resource creation or if you simply prefer to do all of your work from the terminal.</p>
<p>Once set up locally, you’ll be able to perform actions like creating a bucket using the following command:</p>
<pre><code class="lang-shell">aws s3api create-bucket —-bucket [your-bucket-name] —-region [bucket-region]
</code></pre>
<p>To get started, check out the AWS CLI <a target="_blank" href="https://github.com/aws/aws-cli">Github page</a> or the AWS CLI <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html">User Guide</a>    .</p>
<h3 id="heading-aws-cloudformation">AWS CloudFormation</h3>
<p>AWS preaches “infrastructure as code.” It’s the idea that you can spin up your infrastructure using something that’s written in a file, where in this particular case, it would be a CloudFormation template. This allows you to have a repeatable process that will be the same each time you perform the deploy.</p>
<p><a target="_blank" href="https://aws.amazon.com/cloudformation/">CloudFormation</a> allows you to set up a configuration file that will deploy the services and resources of your choosing by pointing to that file with the CLI or by uploading it in the console.</p>
<p>Here’s an <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/quickref-s3.html#scenario-s3-bucket-website">example from AWS</a> of what that looks like for a static S3 bucket that could serve as a website.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudformation-template-s3.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS CloudFront template example</em></p>
<p>To get started, check out AWS’s CloudFormation <a target="_blank" href="https://aws.amazon.com/cloudformation/resources/templates/">example templates</a> or their <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/GettingStarted.Walkthrough.html">Get Started guide</a>.</p>
<h2 id="heading-resources">Resources</h2>
<p>If you’re interested in getting deeper into the AWS ecosystem, here are a few resources to get started:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/aws-certified-cloud-practitioner-training-2019-free-video-course/">AWS Certified Cloud Practitioner Training 2019 - A Free 4-hour Video Course</a> (freeCodeCamp.org)</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/awscertified-challenge-free-path-aws-cloud-certifications/">Introducing The #AWSCertified Challenge: A Path to Your First AWS Certifications</a> (freeCodeCamp.org)</li>
<li><a target="_blank" href="https://aws.amazon.com/getting-started/tutorials/">10-Minute Tutorials</a> (AWS)</li>
<li><a target="_blank" href="https://acloud.guru/">A Cloud Guru</a> (Paid courses)</li>
<li><a target="_blank" href="https://aws.amazon.com/solutions/case-studies/">AWS Case Studies</a> (AWS)</li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Brilliant Add-on For Static Sites That Will Make You Dance ]]>
                </title>
                <description>
                    <![CDATA[ By Jared Wolff This post is originally from www.jaredwolff.com Privacy. Performance. Brilliant looks. Can you have all three? (Of course!) Having a statically generated blog is great. Many folks use services like Disqus and Google Analytics to make t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-setup-worry-free-blog-comments-in-20-simple-steps/</link>
                <guid isPermaLink="false">66d8505639c4dccc43d4d4a7</guid>
                
                    <category>
                        <![CDATA[ blog ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker compose ]]>
                    </category>
                
                    <category>
                        <![CDATA[ nginx ]]>
                    </category>
                
                    <category>
                        <![CDATA[ oauth ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 08 Jul 2019 12:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/Copy-of-Static-Site-Docker-Recipes-2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jared Wolff</p>
<p><strong>This post is originally from <a target="_blank" href="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/">www.jaredwolff.com</a></strong></p>
<p>Privacy.</p>
<p>Performance.</p>
<p>Brilliant looks.</p>
<p>Can you have all three?</p>
<p>(Of course!)</p>
<p>Having a statically generated blog is great. Many folks use services like Disqus and Google Analytics to make them even better. Not surprising if you were one of them!  Privacy concerns are are the forefront of everyone’s attention. So, rather than keeping the status quo, it’s time to do something about it!</p>
<p><strong>If you've been looking to protect your site visitor's privacy and improve performance this blog post is for you.</strong></p>
<p>In this article we'll be using DigitalOcean’s Docker droplet. It allows you to host several different applications/services on one (virtual) machine. By the end of it you'll know how to run your own comments server using Commento. Plus i’ll share a few tricks i’ve learned along the way to make it much easier for you.</p>
<p>Leeeets go!</p>
<h2 id="heading-reverse-proxy">Reverse Proxy</h2>
<p>One of the most important aspects of this setup is the reverse proxy. A reverse proxy acts like a router. Requests come in for a certain domain.  That request is then routed to the service associated with that domain.</p>
<p>Here’s a diagram from the Nginx Reverse Proxy + Let’s Encrypt Helper documentation. It'll help illustrate the idea.</p>
<p><img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/webproxy-1f1c7540-4b86-4478-bb3e-f05043d671a5.jpg" alt="Nginx Reverse Proxy with Let's Encrypt" width="730" height="334" loading="lazy"></p>
<p>Another benefit is that there’s an extra layer of protection to the outside world. Your websites run in a private network and the only access is through the Nginx reverse proxy. Point your DNS to the server and Nginx handles all the magic.</p>
<p>Here's how to get it setup:</p>
<ol>
<li>Go ahead and set up your Digital Ocean Droplet. <a target="_blank" href="https://marketplace.digitalocean.com/apps/docker">All the info you need is right here</a>. The $5 version is more than sufficient.</li>
<li><p><a target="_blank" href="https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion">Go here to clone the repository.</a> You can also run this in your terminal. Make sure you SSH into your Digital Ocean droplet first!</p>
<p>     git clone git@github.com:evertramos/docker-compose-letsencrypt-nginx-proxy-companion.git</p>
</li>
<li><p>Change directories to the cloned repository.</p>
</li>
<li>Copy <code>.env.sample</code> to <code>.env</code> and update the values inside. I had to change the <code>IP</code> value to the IP of my Digital Ocean Droplet. I left all the other ones alone.</li>
<li>Run <code>docker-compose up -d</code> to start everything. (you can run without the <code>-d</code> option to make sure everything starts ok. Or you can attach the log output using <code>docker container logs -f &lt;container name</code></li>
</ol>
<p>When pointing your sub-domains to this server, make sure you use an A record. Here's an example of mine:</p>
<p><img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-9c0432cd-4d40-4c89-88f3-24037d915eaf.52.32_PM.png" alt="NS1 A Record Configuration" width="730" height="581" loading="lazy"></p>
<p>Depending on your DNS provider, you'll have to figure out how to set an A record. That is beyond the purpose of this article though!</p>
<h2 id="heading-setting-up-commento-with-docker-compose">Setting Up Commento with Docker Compose</h2>
<p><img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Compose-1c868832-6819-43e2-8696-ab698a10dbee.jpg" alt="Commento Logo with Docker Logo" width="730" height="486" loading="lazy"></p>
<p>Here is the current docker compose file i'm using for Commento. It includes a few more environment variables for configuring Github, Gitlab and Google. It also includes the environment variables for setting the SMTP settings. These parameters are important. Otherwise you can't receive password reset or moderation emails!</p>
<p>    version: '3'</p>
<p>    services:
      commento:
        image: registry.gitlab.com/commento/commento
        container_name: commento
        restart: always
        environment:
          COMMENTO_ORIGIN: https://${COMMENTS_URL}
          COMMENTO_PORT: 8080
          COMMENTO_POSTGRES: postgres://postgres:postgres@postgres:5432/commento?sslmode=disable
          COMMENTO_SMTP_HOST: ${SMTP_HOST}
          COMMENTO_SMTP_PORT: ${SMTP_PORT}
          COMMENTO_SMTP_USERNAME: ${SMTP_USERNAME}
          COMMENTO_SMTP_PASSWORD: ${SMTP_PASSWORD}
          COMMENTO_SMTP_FROM_ADDRESS: ${SMTP_FROM_ADDRESS}
          COMMENTO_GITHUB_KEY: ${COMMENTO_GITHUB_KEY}
          COMMENTO_GITHUB_SECRET: ${COMMENTO_GITHUB_SECRET}
          COMMENTO_GITLAB_KEY: ${COMMENTO_GITLAB_KEY}
          COMMENTO_GITLAB_SECRET: ${COMMENTO_GITLAB_SECRET}
          COMMENTO_GOOGLE_KEY: ${COMMENTO_GOOGLE_KEY}
          COMMENTO_GOOGLE_SECRET: ${COMMENTO_GOOGLE_SECRET}
          COMMENTO_TWITTER_KEY: ${COMMENTO_TWITTER_KEY}
          COMMENTO_TWITTER_SECRET: ${COMMENTO_TWITTER_SECRET}
          VIRTUAL_HOST: ${COMMENTS_URL}
          VIRTUAL_PORT: 8080
          LETSENCRYPT_HOST: ${COMMENTS_URL}
          LETSENCRYPT_EMAIL: ${EMAIL}
        depends_on:</p>
<ul>
<li>postgres
networks:</li>
<li>db_network</li>
<li><p>webproxy</p>
<p>postgres:
image: postgres
container_name: postgres
environment:
POSTGRES_DB: commento
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
networks:</p>
</li>
<li>db_network
volumes:</li>
<li><p>postgres_data_volume:/var/lib/postgresql/data</p>
<p>networks:
db_network:
webproxy:
external: true</p>
<p>volumes:
postgres_data_volume:</p>
</li>
</ul>
<p>To set the environment variables, put them inside an <code>.env</code> file. Make sure the <code>.env</code> file is in the same directory as <code>docker-compose.yml</code>. When you run <code>docker-compose up</code> it will apply the variables set in the <code>.env</code> file. Nothing gets set if they're left blank.</p>
<p>Set the required <code>COMMENTS_URL</code> and <code>EMAIL</code> or you may run into problems. The best way to set these is by pacing them in the <code>.env</code> file. Here is an example:</p>
<p>    COMMENTS_URL=comments.your.url
    EMAIL=you@your.url</p>
<h2 id="heading-getting-oauth-key-amp-secret">Getting OAuth Key &amp; Secret</h2>
<p>Commento works with most popular OAuth providers. Thus visitors can leave comments without making an account.</p>
<p>The instructions are similar for each. I've outlined the steps for all of them below.</p>
<h3 id="heading-twitter">Twitter</h3>
<ol>
<li><p>Login to <a target="_blank" href="http://twitter.com">Twitter.com</a> and apply for a developer account: <a target="_blank" href="https://developer.twitter.com/en/application/use-case">https://developer.twitter.com/en/application/use-case</a></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-4171cdf7-6c2b-408b-bb64-57822ede91cb.26.08_PM.png" alt="Twitter API Access" width="730" height="581" loading="lazy"></p>
</li>
<li><p>Describe how you'll use the API. You can use what I wrote.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-4c0aecf2-c020-4005-bd5f-81e3b4ac6b8f.28.43_PM.png" alt="How will you use the API?" width="730" height="581" loading="lazy"></p>
</li>
<li><p>Double check your entry and click <strong>Looks Good!</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-ade63510-86d3-48a4-a121-221f6e14cd96.28.50_PM.png" alt="Is everything correct?" width="730" height="581" loading="lazy"></p>
</li>
<li><p>Agree to the terms of service.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-2e8e3089-bd51-4d27-8573-6987aafc663e.28.59_PM.png" alt="Agree to Developer Agreement" width="730" height="581" loading="lazy"></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-145b1bfd-9fc7-4ea6-ba5f-032e59d7fe8d.41.47_PM.png" alt="You did it!" width="730" height="581" loading="lazy"></p>
</li>
<li><p>They'll tell you to check your email for a confirmation. Confirm your email and you should be able to create your first app!</p>
</li>
<li><p>Once approved to to <strong>Get started</strong> click <strong>Create an app</strong>.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-640686b8-15c6-4af0-b9df-65ce15ae0fe7.29.22_PM.png" alt="Create an app" width="730" height="581" loading="lazy"></p>
</li>
<li><p>Next screen, again click <strong>Create an app</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-de2b85d5-8bb7-428f-bfd1-2a23d0b7d4e0.29.26_PM.png" alt="Create an app" width="730" height="581" loading="lazy"></p>
</li>
<li><p>Enter all the appropriate details. For the callback URL, use <a target="_blank" href="https://comments.jaredwolff.com/api/oauth/google/callback"><code>https://&lt;your URL&gt;/api/oauth/github/callback</code></a> where <a target="_blank" href="https://comments.jaredwolff.com/api/oauth/google/callback"><code>&lt;your URL&gt;</code></a> is your Commento subdomain.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-91acb343-9dee-4917-be77-9704fe439722.32.44_PM.png" alt="App details" width="730" height="581" loading="lazy"></p>
</li>
<li><p>Finally, once you're done filling out the information to go the <strong>Keys and Token</strong>s area. Save both the key and token. Enter them into the <code>.env</code> file. You can use <code>COMMENTO_TWITTER_KEY</code> and <code>COMMENTO_TWITTER_SECRET</code></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_6-b910e9ff-dc34-45e8-94df-affb06702617.33.07_PM.png" alt="Get oauth key and secret" width="730" height="581" loading="lazy"></p>
</li>
</ol>
<h3 id="heading-gitlab">Gitlab</h3>
<ol>
<li>Login to <a target="_blank" href="http://gitlab.com">Gitlab.com</a> and go to to top right and click <strong>Settings</strong></li>
<li><p>Then click on <strong>Applications</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_12-c6da9d02-2052-4fa4-89de-d5212b8f49ca.56.47_PM.png" alt="Gitlab profile" width="730" height="445" loading="lazy"></p>
</li>
<li><p>Enter a name for your app. I put <strong>Commento</strong>.</p>
</li>
<li>Set the Redirect URI to <a target="_blank" href="https://comments.jaredwolff.com/api/oauth/google/callback"><code>https://&lt;your URL&gt;/api/oauth/gitlab/callback</code></a></li>
<li><p>Select the <strong>read_user</strong> scope.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_12-e616c338-6144-4704-93c6-914db6fad5f6.59.15_PM.png" alt="Gitlab add application" width="730" height="500" loading="lazy"></p>
</li>
<li><p>Click the green <strong>Save Application</strong> button</p>
</li>
<li><p>Copy the <strong>Application ID</strong> and <strong>Secret</strong> and place them in your <code>.env</code> file using <code>COMMENTO_GITLAB_KEY</code> and <code>COMMENTO_GITLAB_SECRET</code></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_1-a4f4ab4a-9fd6-423f-821c-6ff2f174e589.04.10_PM.png" alt="Application key and secret" width="730" height="689" loading="lazy"></p>
</li>
</ol>
<h3 id="heading-github">Github</h3>
<ol>
<li>To get your OAuth key and secret, you'll need to go to this URL: <a target="_blank" href="https://github.com/settings/developers">https://github.com/settings/developers</a></li>
<li><p>Once there, click on <strong>New OAuth App</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_9-18bf8f23-916f-476b-8c25-3377de931fe3.15.33_AM.png" alt="Add OAuth application" width="730" height="562" loading="lazy"></p>
</li>
<li><p>Enter your details. For the callback URL, use <a target="_blank" href="https://comments.jaredwolff.com/api/oauth/google/callback"><code>https://&lt;your URL&gt;/api/oauth/github/callback</code></a> where <a target="_blank" href="https://comments.jaredwolff.com/api/oauth/google/callback"><code>&lt;your URL&gt;</code></a> is your Commento subdomain.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_9-6e616334-7123-4de4-a4fd-f2fe319b1971.28.24_AM.png" alt="Register new OAuth application" width="730" height="585" loading="lazy"></p>
<p> <em>Note: Make sure you include <code>https</code> in your URLs.</em></p>
</li>
<li><p>Grab the <strong>Client ID</strong> and <strong>Client secret</strong> and put that into your <code>.env</code> file using <code>COMMENTO_GITHUB_KEY</code> and <code>COMMENTO_GITHUB_SECRET</code></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_9-7505a3ef-386a-4b75-a7dc-1dd3e22d0baf.29.28_AM.png" alt="Application created successfully" width="730" height="585" loading="lazy"></p>
</li>
</ol>
<h3 id="heading-google">Google</h3>
<p>Setting up Google is just about as tedious to set up as Twitter. Despite how scary I just made it out to be, it's completely doable. Here are the steps.</p>
<ol>
<li>Go to this URL: <a target="_blank" href="https://console.developers.google.com/cloud-resource-manager?previousPage=%2Fapi">Google Developer Console</a></li>
<li><p>Create a new project</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_8-f3793926-cc54-4345-b81c-5ec0f4631a35.42.48_AM.png" alt="Create a new project" width="730" height="588" loading="lazy"></p>
</li>
<li><p>Click the <strong>GoogleAPIs logo</strong> in the top left corner to go back once you have a project. (Make sure the dropdown next to the <strong>GoogleAPIs logo</strong> is the same as your new project!)</p>
</li>
<li>Then, click <strong>Credentials</strong> on the left side.</li>
<li><p>Update the <strong>Application Name</strong> and <strong>Authorized Domains</strong> in the <strong>OAuth consent screen</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_8-d839a5c9-3368-4f18-b674-73b6e4e7c17c.47.15_AM.png" alt="Setup application" width="730" height="499" loading="lazy"></p>
</li>
<li><p>Click <strong>Create credentials</strong> then <strong>OAuth client ID</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_8-201545f9-4d47-4e0c-ae9a-b40efdc35a4b.44.36_AM.png" alt="Setup credentials" width="730" height="545" loading="lazy"></p>
</li>
<li><p>On the <strong>Create OAuth client ID</strong> enter the subdomain associated with Commento to <strong>Authorized Javascript origins.</strong> Then, enter the full callback URL. For example <a target="_blank" href="https://comments.jaredwolff.com/api/oauth/google/callback"><code>https://comments.jaredwolff.com/api/oauth/google/callback</code></a>. Make it yours by replacing <code>comments.jaredwolff.com</code> with your URL.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_8-fdba3491-d562-41f3-acff-2857ea816cec.52.15_AM.png" alt="Create OAuth Client ID" width="730" height="706" loading="lazy"></p>
<p> Once entered, click the <strong>create</strong> button.</p>
</li>
<li><p>Grab the <strong>client ID</strong> and <strong>client secret</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-04_at_8-0c3f2895-0cb9-4b3a-a154-a3d80fd9716a.57.40_AM.png" alt="OAuth Credentials" width="730" height="706" loading="lazy"></p>
</li>
<li><p>Update your <code>.env</code> file using <code>COMMENTO_GOOGLE_KEY</code> and <code>COMMENTO_GOOGLE_SECRET</code></p>
</li>
</ol>
<h2 id="heading-install-your-application">Install your application</h2>
<p>You've entered your OAuth Credentials email, domain and SMTP credentials. It's time to wrap this show up!</p>
<ol>
<li>Once you're done editing your <code>.env</code> file. Run <code>docker-compose up</code> (For files not named <code>docker-compose.yml</code>, use the <code>-f</code> flag. Example: <code>docker-compose -f commento.yml up</code></li>
<li>Watch the output for errors. If it looks good you may want to kill it (<strong>CTRL+C</strong>) and run with the <code>-d</code> flag</li>
<li><p>On first start, Commento will prompt you with a login screen.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_12-d5a1ca53-93b3-49c5-a3a7-e8b728259e2d.11.29_PM.png" alt="Commento Login" width="730" height="545" loading="lazy"></p>
</li>
<li><p>Create a new account by clicking <strong>Don't have an account yet? Sign up.</strong></p>
</li>
<li>Enter your information and click <strong>Sign Up</strong></li>
<li><p>Check your email and click the included link:</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_12-e263aa4f-201b-42ac-986c-b28c5f003f38.12.48_PM.png" alt="Validation email with link" width="730" height="733" loading="lazy"></p>
</li>
<li><p>Log in with your freshly made account.</p>
</li>
<li><p>Then, click <strong>Add a New Domain.</strong></p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_12-46acfe9c-f3f4-4d3e-b8fb-97fbff643a86.10.47_PM.png" alt="Add new domain" width="730" height="598" loading="lazy"></p>
</li>
<li><p>Once created go to <strong>Installation Guide.</strong>  Copy the snippet and place it where ever you want your comments to live. In my case, I put the snippet in an area just after my <code>&lt;article&gt;</code> tag.</p>
<p> <img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_12-f78f36c5-f3f7-45ec-971d-9bf0bf7b7d1f.36.35_PM.png" alt="Code snippet" width="730" height="589" loading="lazy"></p>
</li>
<li><p>Re-compile your site and check for success!</p>
<p><img src="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/images/Screen_Shot_2019-07-05_at_12-8f7ffbdc-c49f-49bc-95bb-1f53a926f361.30.27_PM.png" alt="Blog comment section with checkmarks" width="730" height="589" loading="lazy"></p>
<p>Checkmark! Finally, I recommend you try logging in with each individual OAuth configuration. That way you know it working for your website visitors. ?</p>
</li>
</ol>
<h2 id="heading-alternatives">Alternatives</h2>
<p>I spent a good chunk playing around with some of the alternatives. This is by no means a definitive guide on what will work best for your site. Here are some of the top ones as of this writing:</p>
<p><a target="_blank" href="https://utteranc.es/#configuration">https://utteranc.es/#configuration</a></p>
<p><a target="_blank" href="https://github.com/netlify/gotell">https://github.com/netlify/gotell</a></p>
<p><a target="_blank" href="https://github.com/eduardoboucas/staticman">https://github.com/eduardoboucas/staticman</a></p>
<p><a target="_blank" href="https://posativ.org/isso/">https://posativ.org/isso/</a></p>
<p><a target="_blank" href="https://www.remarkbox.com/">https://www.remarkbox.com</a></p>
<p><a target="_blank" href="https://www.vis4.net/blog/2017/10/hello-schnack/">https://www.vis4.net/blog/2017/10/hello-schnack/</a></p>
<p><a target="_blank" href="https://github.com/gka/schnack">https://github.com/gka/schnack</a></p>
<p>There's also a huge thread over at the Hugo blog which has a ton more links and resources as well:</p>
<p><a target="_blank" href="https://discourse.gohugo.io/t/alternative-to-disqus-needed-more-than-ever/5516">https://discourse.gohugo.io/t/alternative-to-disqus-needed-more-than-ever/5516</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congrats! You are now hosting your own comments server! ?</p>
<p>In this article you've learned how to harness the power of Docker and a Nginx Reverse Proxy. As an added bonus, you know how to  set up OAuth credentials! That way future setup will be easy peasy.</p>
<p>By the way, this is only the tip of the iceberg. You can set up the same server for analytics, data collection and more. <a target="_blank" href="https://www.jaredwolff.com/files/host-your-comments/">All the example code including code for other applications can be found here.</a></p>
<p>Finally, if you're looking pay for Commento head to <a target="_blank" href="http://www.commento.io">www.commento.io</a> and sign up for the service. You'll be supporting awesome open source software!</p>
<p>If you have comments and questions let's hear em'. Start the conversation down below. ???</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Gatsby vs Hugo, a detailed comparison ]]>
                </title>
                <description>
                    <![CDATA[ By David In this article, I compare two static site generators, Gatsby and Hugo. I discuss framework familiarity, stability, security, tooling, build speed, performance and the community surrounding both. So let’s get started. About a year ago, I cha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/gatsby-vs-hugo-a-detailed-comparison-e78d94f640fc/</link>
                <guid isPermaLink="false">66d46013d1ffc3d3eb89de1a</guid>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Hugo ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 28 Jul 2018 00:00:00 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*Qrk2M5aQ_J6Gtt-WZBrnGA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By David</p>
<p>In this article, I compare two static site generators, Gatsby and Hugo. I discuss framework familiarity, stability, security, tooling, build speed, performance and the community surrounding both. So let’s get started.</p>
<p>About a year ago, I changed <a target="_blank" href="https://learnitmyway.com/">my website</a> from Wordpress to <a target="_blank" href="http://gohugo.io/">Hugo</a>, which is a static site generator written in Go that uses Go’s template libraries for templating. I have recently done a viability assessment of <a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a>, another static site generator written in React that uses React for templating.</p>
<p>In this article, I compare the differences between Hugo v0.42 and Gatsby v1.93. For the comparison, I used <a target="_blank" href="https://learnitmyway-hugo.netlify.com/">this Hugo site</a> and <a target="_blank" href="http://learnitmyway-gatsby.netlify.com/">this Gatsby site</a>. The code for each can be found on Github for <a target="_blank" href="https://github.com/DeveloperDavo/learnitmyway/tree/gatsby-vs-hugo">the Hugo site</a> and for <a target="_blank" href="https://github.com/DeveloperDavo/learnitmyway-gatsby">the Gatsby site</a>.</p>
<h4 id="heading-framework-familiarity">Framework familiarity</h4>
<p>If you are not familiar with React and you don’t plan on learning React, then you should probably choose Hugo. If you know and like React, should you choose Gatsby? Well, not necessarily.</p>
<p>I would argue that you need a decent understanding of React (see <a target="_blank" href="https://learnitmyway.com/learn-react-with-these-resources/">Learn React with these Resources</a>) if you want to use Gatsby. And in order to understand React, you need a decent understanding of JavaScript (see <a target="_blank" href="https://learnitmyway.com/learn-javascript-with-these-resources/">Learn JavaScript with these resources</a>).</p>
<p>Even though I have been using Hugo for almost a year, it wasn’t necessary for me to understand Go. I also only had to learn a little bit about Go’s template libraries. However, I did find that I had to refer to the documentation more often with Hugo because of my lack of familiarity. Gatsby requires a deeper understanding of React than Hugo expects of Go. Nevertheless, if framework familiarity were the only criteria, I would choose Gatsby because it’s nice not to have to refer to the documentation while adding new features to my website.</p>
<h4 id="heading-stability">Stability</h4>
<p>One way of assessing stability would be to compare <a target="_blank" href="https://github.com/gohugoio/hugo/issues">Hugo’s issues on GitHub</a> with <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues">Gatsby’s issues on GitHub</a>. You will see that Gatsby has more features, (which is exciting) but also has more bugs (which is not so exciting). I initially did not consider stability as a criteria until I found <a target="_blank" href="https://github.com/gatsbyjs/gatsby/issues/6392">this bug</a> and that made me realise the importance of stability in software. I may be taking this one personally because of the time and effort I expended, trying to find that bug, but I still think Hugo is more stable than Gatsby.</p>
<h4 id="heading-security">Security</h4>
<p>Gatsby uses JavaScript, and JavaScript applications are notorious for requiring a lot of Node modules to run. There is even a Node module that <a target="_blank" href="https://medium.com/@jdan/i-peeked-into-my-node-modules-directory-and-you-wont-believe-what-happened-next-b89f63d21558">sends Hot Pocket tweets</a> and another that <a target="_blank" href="https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8cb347c5b5">harvests credit card numbers</a> :D. Static sites tend to be more secure by nature, but I still think it is worth mentioning that more dependencies result in more code that you might not trust.</p>
<h4 id="heading-tooling">Tooling</h4>
<p>Gatsby has all the advantages of the <a target="_blank" href="https://www.npmjs.com/search?q=Gatsby">JavaScript toolchain</a> and all the disadvantages of <a target="_blank" href="https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4">JavaScript fatigue</a>. On top of that, Gatsby has a really nice <a target="_blank" href="https://www.gatsbyjs.org/plugins/">plugin library</a>. In particular, <a target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-offline">gatsby-plugin-offline</a> allowed me to easily add offline capabilities to my website, which I still haven’t figured out with Hugo.</p>
<p>On the other hand, some things that require a plugin with Gatsby just come out of the box with Hugo. For example, <a target="_blank" href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-react-helmet">gatsby-plugin-react-helmet</a> is needed to edit the head tag, whereas this can be done with simple HTML in Hugo. Since I enjoyed using the tooling that came with Gatsby, I give this one to Gatsby.</p>
<h4 id="heading-build-speed">Build speed</h4>
<p>Hugo is able to build my website without any additional tooling in less than 100ms. Gatsby is able to build my website in about 15 seconds, but this does include a lot of additional tooling. Adding <a target="_blank" href="https://github.com/postcss/postcss">PostCSS</a> and <a target="_blank" href="https://github.com/imagemin/imagemin">Imagemin</a> to the Hugo build bumps the build time up to about 5 seconds. Watching for changes during development was also faster using Hugo. I think Hugo is the winner here.</p>
<h4 id="heading-documentation">Documentation</h4>
<p>Both Gatsby and Hugo have really nice documentation. Hugo has a <a target="_blank" href="https://gohugo.io/getting-started/quick-start/">Quick Start</a> and Gatsby has a <a target="_blank" href="https://www.gatsbyjs.org/docs/">Getting Started</a> section. Gatsby also has a really nice <a target="_blank" href="https://www.gatsbyjs.org/tutorial/">tutorial</a>, which evens out the steeper learning curve. Personally, I found it easier to get started with Gatsby, but that is because I already understood React. I think it is fair to say that both Hugo and Gatsby have great documentation.</p>
<h4 id="heading-performance">Performance</h4>
<p>Using <a target="_blank" href="https://developers.google.com/web/tools/lighthouse/">Lighthouse</a>, the performance score was 100 for my site in Hugo and 95 for my site in Gatsby. The First Contentful Paint for a 3G connection was about 1 second for the Hugo site and 1.5 seconds for the Gatsby site. Using <a target="_blank" href="https://www.webpagetest.org/">Web Page Test</a> the load time on a 2G connection was <a target="_blank" href="https://www.webpagetest.org/result/180722_Y6_19710626850f2326f4610b156398dbf0/">8.7 seconds in Hugo</a> and <a target="_blank" href="https://www.webpagetest.org/result/180722_PJ_fa010df1c51f603586ee9c04f2abd558/">11.7 seconds in Gatsby</a>.</p>
<p>However, doing a simple manual test to see which site loads first, Gatsby was noticeably faster, so I don’t really understand what Lighthouse or Web Page Test was measuring. Furthermore, as Gatsby is a Single Page App, navigating within the website does not require a request from the server. Pages are just re-rendered with JavaScript. Anyhow, I can say with certainty that both Hugo and Gatsby are fast. I would be interested to hear your thoughts in the comments below.</p>
<h4 id="heading-community">Community</h4>
<p>Gatsby is gaining popularity quickly, which comes with a thriving community. That is not to say that Hugo’s community is boring. If GitHub stars are anything to go by, Hugo has more than 27 thousand and Gatsby has more than 23 thousand. On Twitter, Gatsby seems to be more active than Hugo.</p>
<h4 id="heading-final-thoughts">Final thoughts</h4>
<p>So which one should you choose? Gatsby uses React, which I am more familiar with, it has better tooling, and it has a thriving community. On the other hand, Hugo is more stable and spends less time building. For larger websites, build speeds become more important and some of you might not care for React at all. In my case, stability was the most important criteria, so I decided to stick with Hugo. I am very excited to see what the future brings in this space.</p>
<hr>
<p><strong>Before you go…</strong> Thanks for reading the article! I write about my professional and educational experiences as a self-taught software developer, so check out <a target="_blank" href="https://www.learnitmyway.com/">my blog</a> or subscribe to <a target="_blank" href="https://learnitmyway.com/newsletter">my newsletter</a> for more content.</p>
<p><strong>You might also like:</strong></p>
<ul>
<li><a target="_blank" href="https://learnitmyway.com/how-i-release-updates-to-my-personal-website/">How I release updates to my personal website</a></li>
<li><a target="_blank" href="https://www.learnitmyway.com/2016/11/11/learning-material-software-development/">Learning material — software development</a> (a list of learning resources, starting with Introduction to Computer Science)</li>
<li><a target="_blank" href="https://learnitmyway.com/opinion-full-stack/">Is full-stack web development worth learning?</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
