<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Estela Franco</title>
    <description>The latest articles on DEV Community by Estela Franco (@guaca).</description>
    <link>https://dev.to/guaca</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F783696%2Fd1a79436-40f2-44cf-ae24-724b54598787.jpg</url>
      <title>DEV Community: Estela Franco</title>
      <link>https://dev.to/guaca</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/guaca"/>
    <language>en</language>
    <item>
      <title>Create a Blog with Eleventy and Storyblok</title>
      <dc:creator>Estela Franco</dc:creator>
      <pubDate>Mon, 13 May 2024 14:01:45 +0000</pubDate>
      <link>https://dev.to/guaca/create-a-blog-with-eleventy-and-storyblok-88k</link>
      <guid>https://dev.to/guaca/create-a-blog-with-eleventy-and-storyblok-88k</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the third post in a series of articles called "Eleventy and Storyblok" to learn how to use this combination to create a web project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Too much text for you? Don't worry! You have all the code described in this tutorial in this template repository: &lt;a href="https://github.com/guaca/11ty-storyblok-template/" rel="noopener"&gt;&lt;/a&gt;&lt;a href="https://github.com/guaca/11ty-storyblok-template/"&gt;https://github.com/guaca/11ty-storyblok-template/&lt;/a&gt;. Keep in mind, however, that you will need to create your &lt;strong&gt;.env&lt;/strong&gt; file with the proper variables described in this article for the template to work.&lt;/p&gt;


&lt;p&gt;Now that you're familiar with why &lt;a href="https://www.11ty.dev/" rel="noopener"&gt;Eleventy&lt;/a&gt; + &lt;a href="https://get.storyblok.com/estela-franco-cuenca" rel="noopener"&gt;Storyblok&lt;/a&gt; is my ideal combination and how to seamlessly query data from Storyblok for use in an Eleventy project, it's time for the next exciting step: let's embark on creating a complete blog site using these two phenomenal technologies!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Blog Architecture in Storyblok
&lt;/h2&gt;

&lt;p&gt;The desired blog content architecture has the following levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── Homepage
├── articles
│   └── article-1
│   └── article-2
│   └── article-n
├── categories
│   └── category-1
│   └── category-2
│   └── category-n
└── authors
    └── author-1
    └── author-2
    └── author-n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So first you need to create a new site in Storyblok (you already learned how to do this in my &lt;a href="/blog/eleventy-storyblok-2/"&gt;previous article&lt;/a&gt;). Got it? Then let's create the data architecture and some initial content in it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing the boilerplate content
&lt;/h3&gt;

&lt;p&gt;Remove the default content that &lt;a href="https://get.storyblok.com/estela-franco-cuenca" rel="noopener"&gt;Storyblok&lt;/a&gt; adds when you create a new site. We will create the blocks and stories we need from scratch. &lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the initial Blocks: Author and Category
&lt;/h3&gt;

&lt;p&gt;Let's start by creating the &lt;code&gt;Author&lt;/code&gt; type. To do this, go to the &lt;code&gt;Block Library&lt;/code&gt; (which should be empty if you have deleted all the default blocks) tab and create a new block named &lt;code&gt;Author&lt;/code&gt;. Select the option &lt;code&gt;Content type block&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pbwO2muC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-block.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pbwO2muC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-block.webp" alt="Create new block form. The technical name field is filled with Author. The content type block option is checked" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's edit it and define the required fields: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name (text)&lt;/li&gt;
&lt;li&gt;Bio (textarea)&lt;/li&gt;
&lt;li&gt;Avatar (asset - images)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to mark them as &lt;code&gt;Required field&lt;/code&gt; if they are mandatory. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4SOp9ySf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-block-fields.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4SOp9ySf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-block-fields.webp" alt="Fields of the Author block: Name, Bio and Avatar" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's repeat the process to create the &lt;code&gt;Category&lt;/code&gt; type. Still in the &lt;code&gt;Block Library&lt;/code&gt;, create a new &lt;code&gt;Content type block&lt;/code&gt; type. In this case, the name is &lt;code&gt;Category&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In this case, and to keep it simple for this tutorial, I have only defined one field called (surprise) &lt;code&gt;Category&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e8hLqsCG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-category-block.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e8hLqsCG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-category-block.webp" alt="The Category blok only has a field in it, and it is called Category" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the first stories: Authors and Categories
&lt;/h2&gt;

&lt;p&gt;Now that we have created two content types, let's create the first stories! To do that, go to the &lt;code&gt;Content&lt;/code&gt; section (which should be empty) and create a new &lt;code&gt;Folder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l1_3vkrz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-content-folder.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l1_3vkrz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-content-folder.webp" alt="There is a menu under the 'Create new' button with two options: Story or Folder" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the &lt;code&gt;Authors&lt;/code&gt;! During the definition of the folder, restrict the content type to &lt;code&gt;Author&lt;/code&gt;. I also recommend you to check the &lt;code&gt;Disable visual editor (Form only)&lt;/code&gt; as we won't use the visual editor in this tutorial.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f9SVaUAJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-authors-folder.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f9SVaUAJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-authors-folder.webp" alt="New Folder form, with the fields Name (Authors), Slug (authors), Parent folder (Root), Content type (Restrict to content types - Author) and Folder content settings (disable visual editor option is checked)" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You're now inside the (empty) Authors folder. Create a new &lt;code&gt;Story&lt;/code&gt;. The pop-up will ask you for some basic data: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name (required). Let's create the &lt;code&gt;Author 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Slug (it's generated based on the name).&lt;/li&gt;
&lt;li&gt;Parent folder. In this case, the Authors folder is the parent folder.
&lt;/li&gt;
&lt;li&gt;Content type (required). Select &lt;code&gt;Author&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---MLfCy7_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-story-basic-info.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---MLfCy7_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-story-basic-info.webp" alt="New Content Story form, with the fields Name (Author 1), Slug (author-1), Parent folder (Authors), and Content type (Author)" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next screen, you will see the form with the fields that we defined when creating the &lt;code&gt;Author&lt;/code&gt; type. Fill them and hit &lt;code&gt;Save&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vUGQg-oc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-story-fields.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vUGQg-oc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-author-story-fields.webp" alt="Author 1 form, with the fields Name, Bio and Avatar" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can repeat the process and create some more authors. In my case, I have created another one, called (surprise) &lt;code&gt;Author 2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UJKbhmL9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/author-stories.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UJKbhmL9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/author-stories.webp" alt="Authors folder has two stories in it: Author 1 and Author 2" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's now move on and create some &lt;code&gt;Categories&lt;/code&gt;. You can repeat the process and create the &lt;code&gt;Categories&lt;/code&gt; folder, which should be restricted to the &lt;code&gt;Category&lt;/code&gt; content type. Remember to check the &lt;code&gt;Disable visual editor (Form only)&lt;/code&gt; checkbox as we're not using this feature in this tutorial. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JDlN50Op--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-categories-folder.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JDlN50Op--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-categories-folder.webp" alt="New Folder form, with the fields Name (Categories), Slug (categories), Parent folder (Root), Content type (Restrict to content types - Category) and Folder content settings (disable visual editor option is checked)" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that you're inside the &lt;code&gt;Categories&lt;/code&gt; folder, create a new story. In this case, it also asks you for some basic information: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name (required). For this example, I'm super creative and I have created the &lt;code&gt;Category One&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Slug (it's generated from the name).&lt;/li&gt;
&lt;li&gt;Parent folder: Categories.&lt;/li&gt;
&lt;li&gt;Content type: Select &lt;code&gt;Category&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the next screen, you will see the category form asking for the &lt;code&gt;Category&lt;/code&gt;. In this basic example, this field is also &lt;code&gt;Category One&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zFJ-bylb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-category-one.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zFJ-bylb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-category-one.webp" alt="New Content Story form, with the fields Name (Category One), Slug (category-one), Parent folder (Categories) and Content type (Category)" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  And what about the Articles?
&lt;/h2&gt;

&lt;p&gt;Yes, I know. This is supposed to be a blog site, but I haven't covered the articles yet, right? That's because we're going step by step. And this is the step to start creating Articles. &lt;/p&gt;

&lt;p&gt;Let's start by creating the &lt;code&gt;Article&lt;/code&gt; content type. Remember how to do this? Correct! Go to the &lt;code&gt;Block Library&lt;/code&gt; and create a new &lt;code&gt;Block&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical name: Article. &lt;/li&gt;
&lt;li&gt;Select block type: Content type block. 
And press &lt;code&gt;Add Block&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bs0TiIAA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-block.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bs0TiIAA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-block.webp" alt="Create new block form. The technical name field is filled with Article. The Content type block option is checked" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now edit it to add the fields. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Title: Text.&lt;/li&gt;
&lt;li&gt;PublicationDate: Date/Time.&lt;/li&gt;
&lt;li&gt;Author. And that's why we created the authors first! Choose the &lt;code&gt;Single-Option&lt;/code&gt; type and choose the following source details: 

&lt;ul&gt;
&lt;li&gt;Source: stories&lt;/li&gt;
&lt;li&gt;Path to the folder of stories: authors/&lt;/li&gt;
&lt;li&gt;Restrict to content type: Author&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Category. Similar to what we have done for &lt;code&gt;Author&lt;/code&gt;. It is also a &lt;code&gt;Single-Option&lt;/code&gt; type and these are the source details: 

&lt;ul&gt;
&lt;li&gt;Source: stories&lt;/li&gt;
&lt;li&gt;Path to the folder of stories: categories/&lt;/li&gt;
&lt;li&gt;Restrict to content type: Category&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Body. Select &lt;code&gt;Markdown&lt;/code&gt;, but check the &lt;code&gt;Rich-text as default&lt;/code&gt; checkbox. This will allow you to have a WYSIWYG interface, but it will be saved (and served) as Markdown. As I mentioned in the first article of this series, Markdown is one of the template languages that you can use in your project. Therefore, choosing &lt;code&gt;Markdown&lt;/code&gt; will be easier to manage than using the &lt;code&gt;Richtext&lt;/code&gt; option.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And you already have the &lt;code&gt;Article&lt;/code&gt; block created!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MRrzu6nk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-block-fields.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MRrzu6nk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-block-fields.webp" alt="Create new block form. The technical name field is filled with Article. The Content type block option is checked" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's now create the first article. Go to the &lt;code&gt;Content&lt;/code&gt; section and create a new &lt;code&gt;Folder&lt;/code&gt; called &lt;code&gt;Articles&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: Articles.&lt;/li&gt;
&lt;li&gt;Slug (it's generated from the Name).&lt;/li&gt;
&lt;li&gt;Parent folder: Root. &lt;/li&gt;
&lt;li&gt;Content type: Restrict content types to Articles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once again, remember to disable the visual editor (Form only). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Px97hE-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-one-basic-info.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Px97hE-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-one-basic-info.webp" alt="Create new content story form with the fields Name (Article 1), Slug (article-1), Parent folder (Articles) and Content type (Article). The Content type block option is checked" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now you will see the form to create your first article. You've seen that I am being very creative with the content I'm using in this new blog site, so you won't be surprised by my first article: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GhEagtdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-one.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GhEagtdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-article-one.webp" alt="Create new block form. The technical name field is filled with Article. The Content type block option is checked" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the Author and Category were selected from the drop-down list. &lt;/p&gt;

&lt;p&gt;You can repeat these steps to generate a few more articles. I have created a total of 3 articles for this tutorial. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KVh3WcPG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/articles-storyblok.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KVh3WcPG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/articles-storyblok.webp" alt="The Articles folder with tree articles in it: Article 1, Article 2 and Article 3" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Homepage
&lt;/h2&gt;

&lt;p&gt;Last but not least, we need a homepage for our brand new blog site. You've seen how creative I've been with the rest of the content. So, the homepage can't be any different!&lt;/p&gt;

&lt;p&gt;Let's create the &lt;code&gt;Block&lt;/code&gt; first. As usual, go to the &lt;code&gt;Block library&lt;/code&gt; and create a new one with the following information: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical name: Page (I used this, but feel free to use whatever works better for you).&lt;/li&gt;
&lt;li&gt;Select block type: Content type block.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qjfCQW2r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-page-block.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qjfCQW2r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-page-block.webp" alt="Create new block form. The technical name is set to Page and 'content type block' is the selected block type" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now, let's define the fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heading: Text.&lt;/li&gt;
&lt;li&gt;Subtitle: Text.&lt;/li&gt;
&lt;li&gt;Highlights: Multi-Options

&lt;ul&gt;
&lt;li&gt;Source: Stories&lt;/li&gt;
&lt;li&gt;Path to folder of stories: articles/&lt;/li&gt;
&lt;li&gt;Restrict to content type: Article&lt;/li&gt;
&lt;li&gt;Minimum: 3&lt;/li&gt;
&lt;li&gt;Maximum: 5&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, our homepage will have a heading, a subtitle and a list of the top articles. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I6tia1CP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-page-block-fields.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I6tia1CP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-page-block-fields.webp" alt="Create new block form. The technical name is set to Page and 'content type block' is the selected block type" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's now create the homepage content. Go to &lt;code&gt;Content&lt;/code&gt;, create a new Story and the pop-up will ask you for some basic information: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name (required)&lt;/li&gt;
&lt;li&gt;Slug (it's generated from the name)&lt;/li&gt;
&lt;li&gt;Parent folder: Root&lt;/li&gt;
&lt;li&gt;Content type: Page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---l0jDd6P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-content-home.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---l0jDd6P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-content-home.webp" alt="New content story form. The name is set to Home, slug is home, parent folder is root and content type is set to page" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, you will see the visual editor enabled. This is happening because Storyblok does offer to disable the visual editor when creating a Folder, but not when creating a Story. Let's disable it as we're not going to use it in this tutorial. &lt;/p&gt;

&lt;p&gt;To do it, access the &lt;code&gt;Entry configuration&lt;/code&gt; from the navbar above the visual editor. This will open a pop-up and you will see the &lt;code&gt;Edit mode&lt;/code&gt; options. Check the &lt;code&gt;Form-only&lt;/code&gt; one. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zvv4u2FF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/disable-visual-editor.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zvv4u2FF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/disable-visual-editor.webp" alt="There is a pop-up named 'Entry configuration'. The Edit mode is set to 'form-only'" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, it looks easier this way, right? Let's fill the fields with my already-famous creative content: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heading: This is the Homepage&lt;/li&gt;
&lt;li&gt;Subtitle: Welcome to the homepage of this amazing web site!&lt;/li&gt;
&lt;li&gt;Highlights: In my example, I created 3 articles and I will select them all. An important detail: it's a reordable drag-and-drop list, so feel free to sort them however you like.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HlEBQoRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-content-home-fields.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HlEBQoRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-content-home-fields.webp" alt="Home page form, with the heading (This is the homepage), subtitle (Welcome to the homepage of this amazing web site!) and highlights (article 3, article 1 and article 2)" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it! You have your headless CMS created and ready to generate new content. &lt;/p&gt;

&lt;h2&gt;
  
  
  Wait. And what about SEO?
&lt;/h2&gt;

&lt;p&gt;If this question has crossed your mind, THANK YOU! Because we need more people who care about making our sites "SEO friendly" since the first stage of the project. &lt;/p&gt;

&lt;p&gt;And if you haven't thought about it, don't worry. I'm here to add this topic just in time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the SEO block
&lt;/h3&gt;

&lt;p&gt;Let's go to the &lt;code&gt;Block Library&lt;/code&gt; and create a new nestable block. Call it &lt;code&gt;SEO&lt;/code&gt;. Define a minimum and maximum of 1 block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--20eNHmg7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-seo-block.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--20eNHmg7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-seo-block.webp" alt="Create new block form. The technical name is set to SEO, and the 'nestable block' is the selected block type." width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the following fields: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;title: text.&lt;/li&gt;
&lt;li&gt;description: text.&lt;/li&gt;
&lt;li&gt;canonical: text.&lt;/li&gt;
&lt;li&gt;noindex: boolean. The default value will be disabled.&lt;/li&gt;
&lt;li&gt;og_title: text.&lt;/li&gt;
&lt;li&gt;og_description: text. &lt;/li&gt;
&lt;li&gt;og_image: asset (image).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1vdZUfxj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-seo-block-fields.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1vdZUfxj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/create-seo-block-fields.webp" alt="The pop-up shows the fields that have been created: title, description, noindex, canonical, og_title, og_description and og_image." width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Add the SEO block to the other blocks
&lt;/h3&gt;

&lt;p&gt;We want to be able to define the proper SEO and Social Media data for all the stories we have already created, so we need to add it to the related content types: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Article&lt;/li&gt;
&lt;li&gt;Author&lt;/li&gt;
&lt;li&gt;Category&lt;/li&gt;
&lt;li&gt;Page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you've added the SEO block to them, you can access the content and add the SEO data: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6gtHV-qH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/add-seo-block-article.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6gtHV-qH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/add-seo-block-article.webp" alt="The pop-up shows the fields that have been created: title, description, noindex, canonical, og_title, og_description and og_image." width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8lKi3xtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/add-seo-block-article-one.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8lKi3xtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/add-seo-block-article-one.webp" alt="The pop-up shows the fields that have been created: title, description, noindex, canonical, og_title, og_description and og_image." width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Eleventy project
&lt;/h2&gt;

&lt;p&gt;Alright, you've got the content, but now it's time to build the site that will put that content to use, right? The following steps will help you achieve the desired project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── src
│   └── _data
│   │    └── articles.js
│   │    └── authors.js
│   │    └── categories.js
│   │    └── home.js
│   │    └── metadata.json
│   └── includes
│   │    └── layouts
│   │    │    └── base.njk
│   │    └── footer.njk
│   │    └── head.njk
│   │    └── header.njk
│   └── public
│   │   └── css
│   │       └── styles.css
│   └── utils
│   │   └── storyblok.js
│   └── article-page.njk
│   └── articles-list.njk
│   └── author-page.njk
│   └── authors-list.njk
│   └── category-page.njk
│   └── categories-list.njk
│   └── index.njk
└── eleventy.js
└── .env
└── .gitignore
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up and configuring the Eleventy project
&lt;/h3&gt;

&lt;p&gt;You already learned how to do this in my &lt;a href="/blog/eleventy-storyblok-2/"&gt;previous article&lt;/a&gt;, so I'll make it faster this time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  1. Create and access your project (and open it using VSCode)
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt; mkdir myproject
 cd myproject
 code .
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  2. Create your &lt;code&gt;package.json&lt;/code&gt; file in the root directory and paste the following lines:
&lt;/h4&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npx @11ty/eleventy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;npx @11ty/eleventy --serve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;devDependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@11ty/eleventy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^2.0.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^16.4.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  3. Install dependencies
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  4. Create the &lt;code&gt;.env&lt;/code&gt; file in the root directory
&lt;/h4&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;STORYBLOK_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[your-storyblok-token-goes-here]"&lt;/span&gt;
&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[your-website-domain-goes-here]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  5. Create the &lt;code&gt;eleventy.js&lt;/code&gt; config file
&lt;/h4&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;// Add the .env domain as GlobalData to use in SEO meta data&lt;/span&gt;
 &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addGlobalData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOMAIN&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="c1"&gt;// Filter articles by Author slug&lt;/span&gt;
 &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;filterArticlesByAuthor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authorFullSlug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredArticles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullSlug&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;authorFullSlug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="c1"&gt;// Sort the filtered articles by PublicationDate in descending order&lt;/span&gt;
   &lt;span class="nx"&gt;filteredArticles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PublicationDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PublicationDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;filteredArticles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="c1"&gt;// Filter articles by Category slug&lt;/span&gt;
 &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;filterArticlesByCategory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;categoryFullSlug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredArticles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullSlug&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;categoryFullSlug&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="c1"&gt;// Sort the filtered articles by PublicationDate in descending order&lt;/span&gt;
   &lt;span class="nx"&gt;filteredArticles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PublicationDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PublicationDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;filteredArticles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="c1"&gt;// Sort the authors  by name: &lt;/span&gt;
 &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sortAuthorsByName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;localeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="c1"&gt;// Pass Trhough Copy&lt;/span&gt;
 &lt;span class="nx"&gt;eleventyConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPassthroughCopy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/public/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;What have we done here? Let's go one by one: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Initializing &lt;code&gt;dotenv&lt;/code&gt;&lt;br&gt;
This initial line populates env variables into process.env and makes it available in the config file. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add Global Data &lt;br&gt;
We're adding the &lt;code&gt;DOMAIN&lt;/code&gt; enviroment variable as Global Data, ensuring it's available throughout the project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filter articles by Author&lt;br&gt;
We've defined a new filter to retrieve articles authored by a specific author. This filter will be applied on the Author's page to list all articles authored by that individual. Additionally, the articles will be sorted by PublicationDate in descending order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filter articles by Category&lt;br&gt;
Similar to the previous filter, here we're filtering articles belonging to a specific Category. This filter will be utilized on the Category's page to list all articles within that particular Category. Once again, the articles are sorted by PublicationDate in descending order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sort authors by name&lt;br&gt;
This filter enables us to arrange the list of authors alphabetically by their names.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  6. Create the &lt;code&gt;src &amp;gt; _data &amp;gt; metadata.json&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My site"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome to my blog site!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[fallback-image-for-social-media-goes-here]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  7. Create the &lt;code&gt;src &amp;gt; _includes &amp;gt; head.njk&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;head&amp;gt;
 &amp;lt;meta charset="UTF-8"&amp;gt;
 &amp;lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&amp;gt;
 &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
 &amp;lt;title&amp;gt;{{ title or metadata.title }}&amp;lt;/title&amp;gt;
 &amp;lt;link rel="stylesheet" href="/css/styles.css"&amp;gt;
 &amp;lt;meta name="description" content="{{ description or metadata.description }}"&amp;gt;
 &amp;lt;link rel="canonical" href="{{ canonical or domain+page.url }}"&amp;gt;
 {% if noindex == "true" %}
   &amp;lt;meta name="robots" content="noindex"&amp;gt;
 {% endif %}
 &amp;lt;meta property="og:title" content="{{ og_title or title or metadata.title }}"&amp;gt;
 &amp;lt;meta property="og:description" content="{{ og_description or description or metadata.description }}"&amp;gt;
 &amp;lt;meta property="og:image" content="{{ og_image or metadata.image }}"&amp;gt;
 &amp;lt;meta name="twitter:card" content="summary_large_image"&amp;gt;
 &amp;lt;meta name="twitter:title" content="{{ og_title or title or metadata.title }}"&amp;gt;
 &amp;lt;meta name="twitter:image" content="{{ og_image or metadata.image }}"&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This might seem a bit overwhelming, right? It's because we're integrating Nunjucks variables and conditionals (using curly braces) with HTML tags. However, it's just as straightforward and incredible as you saw in the previous code.&lt;/p&gt;

&lt;p&gt;For example &lt;code&gt;&amp;lt;link rel="canonical" href="{{ canonical or domain+page.url }}"&amp;gt;&lt;/code&gt; generates a canonical link with the canonical URL sourced from the frontmatter data. If that's missing, it constructs a canonical URL by combining the domain (Global Data) with the URL of the current page. BOOM!&lt;/p&gt;
&lt;h4&gt;
  
  
  8. Create the &lt;code&gt;src &amp;gt; _includes &amp;gt; header.njk&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header-nav"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header-home"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My site&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/articles/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Articles&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/authors/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Authors&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"header-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/categories/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Categories&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  9. Create the &lt;code&gt;src &amp;gt; _includes &amp;gt; footer.njk&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;footer&amp;gt;
   © 2024 - All Rights Reserved
 &amp;lt;/footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  10. Create the &lt;code&gt;src &amp;gt; _includes &amp;gt; layouts &amp;gt; base.njk&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="{{ metadata.language }}"&amp;gt;
{% include "head.njk" %}
&amp;lt;body&amp;gt;
 {% include "header.njk" %}
 &amp;lt;main style="flex-grow:1;"&amp;gt;
   {{ content | safe }}
 &amp;lt;/main&amp;gt;
 {% include "footer.njk" %}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

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


&lt;p&gt;That seemed a bit weird, didn't it? Let's break it down step by step. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;lang&lt;/code&gt; attribute in the HTML comes from the metadata.json file, where we've set the language. &lt;/li&gt;
&lt;li&gt;We utilize &lt;code&gt;include&lt;/code&gt; to pull in the templates we've created (&lt;code&gt;head&lt;/code&gt;, &lt;code&gt;header&lt;/code&gt; and &lt;code&gt;footer&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  11. Create the &lt;code&gt;src &amp;gt; utils &amp;gt; storyblok.js&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;preview&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://gapi.storyblok.com/v1/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STORYBLOK_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}),&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to fetch API&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This code may sound familiar because we've already covered it in the &lt;a href="/blog/eleventy-storyblok-2/"&gt;previous article&lt;/a&gt;. Remember, I'm using the &lt;code&gt;published&lt;/code&gt; version, but you might opt for the &lt;code&gt;draft&lt;/code&gt; version if this template isn't intended for a production site. &lt;/p&gt;
&lt;h4&gt;
  
  
  12. Create your &lt;code&gt;src &amp;gt; _data &amp;gt; articles.js&lt;/code&gt; file.
&lt;/h4&gt;

&lt;p&gt;It will generate the global &lt;code&gt;articles&lt;/code&gt; data file, which you'll utilize to create your article pages. This is the code for this file: &lt;/p&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchAPI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../utils/storyblok.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getArticles&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
 {
   ArticleItems {
     items {
       full_slug
       content {
         Body
         PublicationDate
         Title
         Author {
           name
           fullSlug
         }
         Category {
           name
           fullSlug
         }
         SEO
       }
     }
   }
 }  
 `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;ArticleItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  13. Create the &lt;code&gt;src  &amp;gt; article-pages.njk&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
pagination:
   data: articles
   size: 1
   alias: article
permalink: "{{ article.full_slug }}/"
layout: layouts/base.njk
templateEngineOverride: njk,md
eleventyComputed: 
   title: "{{ article.content.SEO[0].title }}"
   description: "{{ article.content.SEO[0].description }}"
   canonical: "{{ article.content.SEO[0].canonical }}"
   noindex: "{{ article.content.SEO[0].noindex }}"
   og_title: "{{ article.content.SEO[0].og_title }}"
   og_description: "{{ article.content.SEO[0].og_description }}"
   og_image: "{{ article.content.SEO[0].og_image.filename }}"
---
&amp;lt;article&amp;gt;
   &amp;lt;h1&amp;gt;{{ article.content.Title }}&amp;lt;/h1&amp;gt;
   &amp;lt;a href="/{{ article.content.Author.fullSlug }}"&amp;gt;{{ article.content.Author.name }}&amp;lt;/a&amp;gt;
   &amp;lt;span&amp;gt; - &amp;lt;/span&amp;gt;
   &amp;lt;a href="/{{ article.content.Category.fullSlug }}"&amp;gt;{{ article.content.Category.name }}&amp;lt;/a&amp;gt;
   &amp;lt;p&amp;gt;{{ article.content.PublicationDate }}&amp;lt;/p&amp;gt;
   &amp;lt;div&amp;gt;
       {{ article.content.Body | safe }}
   &amp;lt;/div&amp;gt;
&amp;lt;/article&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Yes, that's a lot of SEO and Social Media metadata, but it's necessary! You might be wondering why this data is under &lt;code&gt;eleventyComputed&lt;/code&gt; instead of being set directly, like we did with &lt;code&gt;permalink&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And that's a great question! We can only use variables and shortcodes in &lt;code&gt;permalink&lt;/code&gt; and &lt;code&gt;eleventyComputed&lt;/code&gt;. Therefore, we don't need to set the permalink under eleventyComputed, but the rest of the custom fields must be placed there to ensure they are dynamicly updated."&lt;/p&gt;
&lt;h4&gt;
  
  
  14. Create the &lt;code&gt;src &amp;gt; articles-list.njk&lt;/code&gt; file
&lt;/h4&gt;

&lt;p&gt;This template will list all the published articles. &lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
permalink: "/articles/"
layout: layouts/base.njk
title: "Articles"
description: "This is the list of Articles"
---
&amp;lt;div&amp;gt;
   &amp;lt;h1&amp;gt;Articles&amp;lt;/h1&amp;gt;
   &amp;lt;ul&amp;gt;
       {% for article in articles %}
       &amp;lt;li&amp;gt;&amp;lt;a href="/{{ article.full_slug }}"&amp;gt;{{ article.content.Title }}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
       {% endfor %}
   &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  15. Repeat steps 12 through 14, but this time creating the files related to &lt;code&gt;Authors&lt;/code&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Filename: src &amp;gt; _data &amp;gt; authors.js&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchAPI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../utils/storyblok.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAuthors&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
 {
   AuthorItems {
     items {
       full_slug
       content {
         Name
         Bio
         Avatar {
           filename
         }
         SEO
       }
     }
   }
 }
 `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;AuthorItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;Filename: src &amp;gt; author-page.njk&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
pagination:
   data: authors
   size: 1
   alias: author
permalink: "{{ author.full_slug }}/"
layout: layouts/base.njk
eleventyComputed: 
   title: "{{ author.content.SEO[0].title }}"
   description: "{{ author.content.SEO[0].description }}"
   canonical: "{{ author.content.SEO[0].canonical }}"
   noindex: "{{ author.content.SEO[0].noindex }}"
   og_title: "{{ author.content.SEO[0].og_title }}"
   og_description: "{{ author.content.SEO[0].og_description }}"
   og_image: "{{ author.content.SEO[0].og_image.filename }}"
---

&amp;lt;div&amp;gt;
   &amp;lt;div class="author-info"&amp;gt;
       &amp;lt;img class="author-avatar" src="{{ author.content.Avatar.filename }}"&amp;gt;
       &amp;lt;h1 class="author-name"&amp;gt;{{ author.content.Name }}&amp;lt;/h1&amp;gt;
   &amp;lt;/div&amp;gt;
       &amp;lt;p&amp;gt;{{ author.content.Bio }}&amp;lt;/p&amp;gt;
   &amp;lt;h2&amp;gt;Articles by {{ author.content.Name }}&amp;lt;/h2&amp;gt;

   {% set targetAuthorFullSlug = author.full_slug %}
   {% set filteredArticles = articles | filterArticlesByAuthor(targetAuthorFullSlug) %}
   &amp;lt;ul&amp;gt;
       {% for article in filteredArticles %}
           &amp;lt;li&amp;gt;
               &amp;lt;article&amp;gt;
                   &amp;lt;h3&amp;gt;&amp;lt;a href="/{{ article.full_slug }}"&amp;gt;{{ article.content.Title }}&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
                   &amp;lt;span&amp;gt;Publication Date: {{ article.content.PublicationDate }}&amp;lt;/span&amp;gt;
               &amp;lt;/article&amp;gt;
           &amp;lt;/li&amp;gt;
       {% else %}
           &amp;lt;p&amp;gt;No articles found for this author.&amp;lt;/p&amp;gt;
       {% endfor %}
   &amp;lt;/ul&amp;gt;

&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;Filename: src &amp;gt; authors-list.njk&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
permalink: "/authors/"
layout: layouts/base.njk
title: "Authors"
description: "This is the list of Authors"
---
&amp;lt;div&amp;gt;
   &amp;lt;h1&amp;gt;Authors&amp;lt;/h1&amp;gt;
   &amp;lt;ul&amp;gt;
       {% for author in authors | sortAuthorsByName %}
       &amp;lt;li&amp;gt;&amp;lt;a href="/{{ author.full_slug }}"&amp;gt;{{ author.content.Name }}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
       {% endfor %}
   &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  16. Repeat steps 12 through 14, but this time creating files related to &lt;code&gt;Categories&lt;/code&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Filename: src &amp;gt; _data &amp;gt; categories.js&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchAPI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../utils/storyblok.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCategories&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
 {
   CategoryItems {
     items {
       full_slug
       content {
         Category
         SEO
       }
     }
   }
 }
 `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;CategoryItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;Filename: src &amp;gt; category-page.njk&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
pagination:
   data: categories
   size: 1
   alias: category
permalink: "{{ category.full_slug }}/"
layout: layouts/base.njk
eleventyComputed: 
   title: "{{ category.content.SEO[0].title }}"
   description: "{{ category.content.SEO[0].description }}"
   canonical: "{{ category.content.SEO[0].canonical }}"
   noindex: "{{ category.content.SEO[0].noindex }}"
   og_title: "{{ category.content.SEO[0].og_title }}"
   og_description: "{{ category.content.SEO[0].og_description }}"
   og_image: "{{ category.content.SEO[0].og_image.filename }}"
---
&amp;lt;div&amp;gt;
   &amp;lt;h1&amp;gt;{{ category.content.Category }}&amp;lt;/h1&amp;gt;

   {% set targetCategoryFullSlug = category.full_slug %}
   {% set filteredArticles = articles | filterArticlesByCategory(targetCategoryFullSlug) %}
   &amp;lt;ul&amp;gt;
       {% for article in filteredArticles %}
           &amp;lt;li&amp;gt;
               &amp;lt;article&amp;gt;
                   &amp;lt;h3&amp;gt;&amp;lt;a href="/{{ article.full_slug }}"&amp;gt;{{ article.content.Title }}&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
                   &amp;lt;span&amp;gt;Publication Date: {{ article.content.PublicationDate }}&amp;lt;/span&amp;gt;
               &amp;lt;/article&amp;gt;
           &amp;lt;/li&amp;gt;
       {% else %}
           &amp;lt;p&amp;gt;No articles found for the author with Full Slug "{{ targetAuthorFullSlug }}".&amp;lt;/p&amp;gt;
       {% endfor %}
   &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;Filename: src &amp;gt; categories-list.njk&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
permalink: "/categories/"
layout: layouts/base.njk
title: "Categories"
description: "This is the list of Categories"
---
&amp;lt;div&amp;gt;
   &amp;lt;h1&amp;gt;Categories&amp;lt;/h1&amp;gt;
   &amp;lt;ul&amp;gt;
       {% for category in categories %}
       &amp;lt;li&amp;gt;&amp;lt;a href="/{{ category.full_slug }}"&amp;gt;{{ category.content.Category }}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
       {% endfor %}
   &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  17. Create the &lt;code&gt;Home&lt;/code&gt; related files
&lt;/h4&gt;

&lt;p&gt;In this case, we need the &lt;code&gt;Home id&lt;/code&gt;. You can find it using the GAPI browser, &lt;a href="/blog/eleventy-storyblok-2/#create-the-graphql-query"&gt;as I explained in my previous article&lt;/a&gt;.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once you have the home id, add the variable in the &lt;code&gt;.env&lt;/code&gt; file:&lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  STORYBLOK_API_KEY="[your-storyblok-token-goes-here]"
  DOMAIN="[your-website-domain-goes-here]"
  HOME_ID="[your-home-id]"
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;Create the &lt;code&gt;src &amp;gt; _data &amp;gt; home.js&lt;/code&gt; file and add the following code in it: &lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchAPI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../utils/storyblok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getHome&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
 {
   PageItem(id: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HOME_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;") {
     content {
       Heading
       Subtitle
       Highlights {
         fullSlug
         name
         content
       }
       SEO
     }
   }
 }  
 `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;PageItem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ul&gt;
&lt;li&gt;Create the &lt;code&gt;src &amp;gt; index.njk&lt;/code&gt; file: &lt;/li&gt;
&lt;/ul&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
pagination:
   data: home
   size: 1
permalink: "/"
layout: layouts/base.njk
eleventyComputed: 
   title: "{{ home.content.SEO[0].title }}"
   description: "{{ home.content.SEO[0].description }}"
   canonical: "{{ home.content.SEO[0].canonical }}"
   noindex: "{{ home.content.SEO[0].noindex }}"
   og_title: "{{ home.content.SEO[0].og_title }}"
   og_description: "{{ home.content.SEO[0].og_description }}"
   og_image: "{{ home.content.SEO[0].og_image.filename }}"
---
&amp;lt;h1&amp;gt;{{ home.content.Heading }}&amp;lt;/h1&amp;gt;
&amp;lt;p class="home-subtitle"&amp;gt;{{ home.content.Subtitle }}&amp;lt;/p&amp;gt;

&amp;lt;h2&amp;gt;Highlighted Articles&amp;lt;/h2&amp;gt;
&amp;lt;ul&amp;gt;
   {% for article in home.content.Highlights %}
   &amp;lt;li&amp;gt;&amp;lt;a href={{ article.fullSlug }}&amp;gt;{{ article.content.Title }}&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
   {% endfor %}
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;
  
  
  18. Create the &lt;code&gt;src &amp;gt; public &amp;gt; css &amp;gt; styles.css&lt;/code&gt; file
&lt;/h4&gt;


&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Colors */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="py"&gt;--darkgray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#202124&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="py"&gt;--purple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#713973&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="py"&gt;--white&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#FFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--white&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--darkgray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
 &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;-apple-system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;system-ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Roboto-Light&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.1em&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.25vw&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;0.8em&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;1.25em&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--darkgray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--darkgray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;flex-grow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4em&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60em&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;90%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.header-home&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--darkgray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
     &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--purple&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.header-nav&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;60em&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;90%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.header-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex-end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;flex-grow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.header-item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
     &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--darkgray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
         &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--purple&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.author-avatar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;75px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.author-name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.author-info&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="nd"&gt;:empty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Run it locally
&lt;/h2&gt;

&lt;p&gt;You're nearly finished! With your content in Storyblok and your code stored in a local repository, it's time to run it locally and ensure everything appears as intended.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Executing this command will run Eleventy locally, making your brand new (local) site accessible at port 8080. If port 8080 is already in use, the correct port will be displayed in the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo and Template Repository
&lt;/h2&gt;

&lt;p&gt;You can see this blog site in action here: &lt;a href="https://11ty-storyblok.netlify.app/" rel="noopener"&gt;&lt;/a&gt;&lt;a href="https://11ty-storyblok.netlify.app"&gt;https://11ty-storyblok.netlify.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H2z9Svil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/home.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H2z9Svil--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/home.webp" alt="Blog site homepage." width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5cSsOTEr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/articles-list.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5cSsOTEr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/articles-list.webp" alt="Blog site articles list." width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--28iIhnCX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/article-page.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--28iIhnCX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/article-page.webp" alt="Blog site article page." width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vSNEyPVw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/author-page.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vSNEyPVw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://estelafranco.com/img/11ty-storyblok-blog/author-page.webp" alt="Blog site author page." width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, you have all the code described in this tutorial in this template repository: &lt;a href="https://github.com/guaca/11ty-storyblok-template/" rel="noopener"&gt;&lt;/a&gt;&lt;a href="https://github.com/guaca/11ty-storyblok-template/"&gt;https://github.com/guaca/11ty-storyblok-template/&lt;/a&gt;. So, you can create your own blog site by clicking the "Use this template" button. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;This tutorial has guided you through configuring and building a blog site, utilizing Storyblok for data storage and Eleventy for page generation. Once completed, you can deploy your Eleventy project to your preferred Jamstack hosting service. By setting up appropriate workflows and hooks, you can ensure that your site updates automatically whenever you publish a new story in Storyblok.&lt;/p&gt;

&lt;p&gt;You have all these steps defined in this &lt;a href="https://estelafranco.com/blog/storyblok-astro/#4.-deploy-the-project-on-netlify"&gt;article&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Can I use the Storyblok Visual Editor with Eleventy?
&lt;/h2&gt;

&lt;p&gt;Long answer short? Yes but it's not straightforward. &lt;/p&gt;

&lt;p&gt;Long answer: Yes, you can leverage it by configuring an edge function to refresh the API/GAPI data from Storyblok upon updates. Eleventy generates data during build time. Consequently, even after saving new data, the data won't be updated in the Visual Editor preview unless the edge function rebuilds it. &lt;/p&gt;

&lt;p&gt;Storyblok's visual editor seamlessly integrates with other frameworks like Next or Nuxt, allowing you to observe content updates instantly, even without saving changes. However, this functionality is not supported by static site generators.&lt;/p&gt;

&lt;p&gt;In the next article in this series, I will use Netlify Edge functions to leverage the Storyblok Visual Editor to make working with the editor easier and more intuitive. Stay tuned!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>11ty</category>
      <category>storyblok</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to connect Eleventy and Storyblok</title>
      <dc:creator>Estela Franco</dc:creator>
      <pubDate>Sat, 10 Feb 2024 21:49:09 +0000</pubDate>
      <link>https://dev.to/guaca/how-to-connect-eleventy-and-storyblok-49fj</link>
      <guid>https://dev.to/guaca/how-to-connect-eleventy-and-storyblok-49fj</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the second post in a series of articles called “Eleventy and Storyblok” to learn how to use this combination to create a web project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before getting into the matter, I really recommend you to read my first article of this series if you haven’t yet. Take a moment to check it out and discover why Evleventy and Storyblok is my perfect combination and why you should start using it to take advantage of these two technologies. You can read i &lt;a href="https://estelafranco.com/blog/eleventy-storyblok/"&gt; here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, in this second article, I explain to you how to connect Eleventy and Storyblok to start consuming content generated in this amazing Headless CMS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical requirements
&lt;/h2&gt;

&lt;p&gt;This article will show you step by step how to query data from Storyblok to use it in an Eleventy project. You'll need a computer with the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js. Eleventy requires Node.js 14. I've used v20.10.0 in the examples provided. 

&lt;ul&gt;
&lt;li&gt;You can check if you have Node installed by running &lt;code&gt;node --version&lt;/code&gt; in a terminal window.&lt;/li&gt;
&lt;li&gt;If the command is not found or returns a number less than 14, you will need to download and install &lt;a href="https://nodejs.org/en/download/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt; before proceeding to the next step.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A code editor. I use &lt;a href="https://code.visualstudio.com/download" rel="nofollow noopener noreferrer"&gt;VSCode&lt;/a&gt;, but feel free to use your favorite.&lt;/li&gt;
&lt;li&gt;A Storyblok account. You can create yours &lt;a href="https://get.storyblok.com/estela-franco-cuenca" rel="nofollow noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of HTML, CSS, JavaScript, Markdown, and the command line is helpful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have all the technical requirements in place, it's time to create the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a project in Storyblok
&lt;/h2&gt;

&lt;p&gt;First of all, let's create a basic Storyblok space for this project!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to Storyblok and create a new space by clicking &lt;strong&gt;Add Space&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;New Space&lt;/strong&gt; option, enter a name for the space, and select the server location (EU by default):&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;You can review the structure and sample content details. By default, you'll find the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Home page in the &lt;code&gt;Content&lt;/code&gt; section:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;When you access it, you will see the "Welcome to the Visual Editor" page by default, and the components of the page in the right sidebar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdtxw8dtr9csmuxn58ez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdtxw8dtr9csmuxn58ez.png" alt="Welcome to the Visual Editor" width="600" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the Home page has a &lt;code&gt;body&lt;/code&gt; component with two block elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teaser: this is a text component. The current value of this field is "Hello world!".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6szcvpgfvg29dqc2zkq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg6szcvpgfvg29dqc2zkq.png" alt="Hello World!" width="600" height="652"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grid: this is a block component, with 3 text elements in it (with the text "Feature 1", "Feature 2", and "Feature 3" text on them).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;We'll cover this later, but understanding the architecture of data is key to creating the right elements in your front-end development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a project in Eleventy
&lt;/h2&gt;

&lt;p&gt;Eleventy does not require you to use any JavaScript framework or library. And you can have a basic project up and running in literally less than 1 minute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open a terminal&lt;/li&gt;
&lt;li&gt;Create a folder for your new project
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir myproject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Access the project
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd myproject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create an &lt;code&gt;index.md&lt;/code&gt; file. The &lt;code&gt;.md&lt;/code&gt; extension is for markdown.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo '# Page header' &amp;gt; index.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run Eleventy. This command compiles the index.md file (but &lt;code&gt;.md&lt;/code&gt; is only one of many valid file extensions) into the output folder(&lt;code&gt;_site&lt;/code&gt; by default), ready to be upload to your favorite hosting service.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @11ty/eleventy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You should get something like this:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[11ty] Writing _site/index.html from ./index.md (liquid)
[11ty] Wrote 1 file in 0.03 seconds (v2.0.1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;However, you may want to run Eleventy from a local development server while working on it. Run Eleventy locally with this code:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @11ty/eleventy --serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You will get something like this:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[11ty] Writing _site/index.html from ./index.md (liquid)
[11ty] Wrote 1 file in 0.04 seconds (v2.0.1)
[11ty] Watching…
[11ty] Server at http://localhost:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;http://localhost:8080&lt;/code&gt; in your browser and you will see your website.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up and configuring the Eleventy project
&lt;/h2&gt;

&lt;p&gt;So far, so simple, right? Your project should have the following architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── _site
│   └── index.html
└── index.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But let's add a few more things to prepare our project for our Storyblok integration.&lt;/p&gt;

&lt;p&gt;We've created our first page, index.html, using one of the valid file extensions (.md). But this is just one of many options you can use for your templates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML: *.html &lt;/li&gt;
&lt;li&gt;Markdown: *.md &lt;/li&gt;
&lt;li&gt;WebC: *.webc &lt;/li&gt;
&lt;li&gt;JavaScript: *.11ty.js &lt;/li&gt;
&lt;li&gt;Liquid: *.liquid &lt;/li&gt;
&lt;li&gt;Nunjucks: *.njk &lt;/li&gt;
&lt;li&gt;Handlebars: *.hbs &lt;/li&gt;
&lt;li&gt;Mustache: *.mustache &lt;/li&gt;
&lt;li&gt;EJS: *.ejs &lt;/li&gt;
&lt;li&gt;Haml: *.haml &lt;/li&gt;
&lt;li&gt;Pug: *.pug &lt;/li&gt;
&lt;li&gt;Custom: *.*&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will use &lt;code&gt;.md&lt;/code&gt; and &lt;code&gt;.njk&lt;/code&gt; in this article, but feel free to use the template languages you feel more comfortable with.&lt;/p&gt;

&lt;p&gt;There's plenty of options to use, combine and override the template language. You can find all the details &lt;a href="https://www.11ty.dev/docs/languages/" rel="nofollow noopener noreferrer"&gt;in the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to pages, you can use layouts to wrap other content. So let's open the project in VSCode and let's create a base layout to define the common HTML elements for all pages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;{{ "{{ title }}" }}&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    {{ "{{ content | safe }}"}}
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this file as &lt;code&gt;base.njk&lt;/code&gt; in a new &lt;code&gt;_includes/layouts&lt;/code&gt; folder. Your project now has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── _includes
│   └── layouts
│       └── base.njk
├── _site
│   └── index.html
└── index.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can add the &lt;code&gt;base.njk&lt;/code&gt; layout to your &lt;code&gt;index.md&lt;/code&gt; page. To do this, add the following information to your existing file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ ---
+ layout: layouts/base.njk
+ title: "This is my homepage"
+ ---
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;# Page header
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What have been done here? The content between the two &lt;code&gt;---&lt;/code&gt; marks is the &lt;strong&gt;front matter&lt;/strong&gt;. This is a YAML block at the top of the file and is a standard convention used by many Static Site Generators (SSG). So you're probably familiar with it if you've used other SSGs in the past. The content in the front matter is key-value pairs that provide information on how to generate data for this page.&lt;/p&gt;

&lt;p&gt;In this case, two key-value pairs have been added: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;layout: This is the route of the desired layout to apply to the page.&lt;/li&gt;
&lt;li&gt;title: This is the title of the page. As mentioned earlier in the &lt;code&gt;base.njk&lt;/code&gt; file, this information will be used in the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Get the API token from Storyblok
&lt;/h3&gt;

&lt;p&gt;An API token is necessary to follow the next steps. By default, you can see your preview token on the "Welcome to the Visual Editor" page. But you can find it (or create a new one) in &lt;code&gt;Settings &amp;gt; Access Tokens&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetjmro45c22jjdhqkxmy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetjmro45c22jjdhqkxmy.png" alt="Access Token on the Welcome to the visual editor page" width="700" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8tnipw5jz8202cd0xha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8tnipw5jz8202cd0xha.png" alt="Access token in Settings - Access Token section" width="700" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to query the Storyblok data from Eleventy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the GraphQL query
&lt;/h3&gt;

&lt;p&gt;In this article, I will be using the &lt;strong&gt;GraphQL Content Delivery API&lt;/strong&gt;. This is is a read-only endpoint and is optimized for fast content delivery.&lt;/p&gt;

&lt;p&gt;Use the following endpoint to send GraphQL requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gapi.storyblok.com/v1/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you need to append the region code if you are using different regions. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gapi-us.storyblok.com/v1/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you haven't used GraphQL schemas before, I have good news for you! Storyblok has a playground where you can explore the functionalities and create the right query. You can access it using this URL with your Access Token in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gapi-browser.storyblok.com/?token=insert-here-your-access-token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we want to get the Home page data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Teaser (Hello world!)&lt;/li&gt;
&lt;li&gt;Grid

&lt;ul&gt;
&lt;li&gt;Feature 1&lt;/li&gt;
&lt;li&gt;Feature 2&lt;/li&gt;
&lt;li&gt;Feature 3&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, this will be our query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  PageItem(id: "insert-your-home-id") {
    name
    content {
      body
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find your Home id running the following query in the playground:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  PageItems {
    items {
      id
      name
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it will return the id and the name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "data": {
    "PageItems": {
      "items": [
        {
          "id": xxxxxxxxxx,
          "name": "Home"
        }
      ]
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, now we know what data we want to get from Storyblok. But how to do it?&lt;/p&gt;

&lt;h3&gt;
  
  
  Data in Eleventy
&lt;/h3&gt;

&lt;p&gt;Eleventy can use data from several different sources. However, when merging data, there is a priority order for data sources. From highest to lowest priority: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Computed data&lt;/li&gt;
&lt;li&gt;Front matter data in a template&lt;/li&gt;
&lt;li&gt;Template data files&lt;/li&gt;
&lt;li&gt;Directory data files&lt;/li&gt;
&lt;li&gt;Front matter data in layouts&lt;/li&gt;
&lt;li&gt;Configuration API global data&lt;/li&gt;
&lt;li&gt;Global data files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two ways that work best for applying a headless CMS are &lt;strong&gt;Global Data&lt;/strong&gt; files and &lt;strong&gt;Configuration API global data&lt;/strong&gt;. In this article, I'm going to use the first one.&lt;/p&gt;

&lt;p&gt;As you can imagine, this means some code is necessary to execute the API call. Let's create a folder called &lt;code&gt;utils&lt;/code&gt;. Inside it, create a &lt;code&gt;storyblok.js&lt;/code&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;preview&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://gapi.storyblok.com/v1/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xxxxxxxxxxxxxxxxxxxxxxxx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;preview&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;draft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to fetch API&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hardcoding your API token into your code is not a good practice. So you can store it as an &lt;code&gt;.env&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env file&lt;/span&gt;
STORYBLOK_TOKEN: xxxxxxxxxxxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, you will need to install the &lt;code&gt;dotenv&lt;/code&gt; package to access it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;dotenv &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see this step will generate some more files and folders. Don't worry, we'll manage this later. Now you can modify your &lt;code&gt;storyblok.js&lt;/code&gt; file to use the &lt;code&gt;.env&lt;/code&gt; variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ require('dotenv').config()
&lt;/span&gt;&lt;span class="p"&gt;module.exports = async function fetchAPI(query, { variables, preview } = {}) {
&lt;/span&gt;    const res = await fetch("https://gapi.storyblok.com/v1/api", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
&lt;span class="gd"&gt;-        Token: "xxxxxxxxxxxxxxxxxxxxxxxx",
&lt;/span&gt;&lt;span class="gi"&gt;+        Token: process.env.STORYBLOK_TOKEN,
&lt;/span&gt;        Version: preview ? "draft" : "published",
      },
      body: JSON.stringify({
        query,
        variables,
      }),
    });
&lt;span class="err"&gt;
&lt;/span&gt;    const json = await res.json();
    if (json.errors) {
      console.error(json.errors);
      throw new Error("Failed to fetch API");
    }
    console.log(json.data)
    return json.data;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, let's create a &lt;code&gt;_data&lt;/code&gt; folder, and a &lt;code&gt;homepage.js&lt;/code&gt; file in it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchAPI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../utils/storyblok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getHomepage&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  {
    PageItem(id: "insert-your-home-id") {
      name
      content {
        body
      }
    }
  }
  `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;PageItem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The previous code will generate the &lt;code&gt;homepage&lt;/code&gt; data from Storyblok's API, and it will be accessible from other pages and layouts. &lt;/p&gt;

&lt;p&gt;So let's modify our &lt;code&gt;index.md&lt;/code&gt; file to use it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;---
layout: layouts/base.njk
title: This is my homepage
---
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- # Page header
&lt;/span&gt;&lt;span class="gi"&gt;+ {{ "{{ homepage.content.body[0].headline }}" }}
+ {{ "{{ homepage.content.body[1].columns[0].name }}" }}
+ {{ "{{ homepage.content.body[1].columns[1].name }}" }}
+ {{ "{{ homepage.content.body[1].columns[2].name }}" }}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After following all these steps, you should see something similar to this on your local server:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctzpovi3ye2v1d636lsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fctzpovi3ye2v1d636lsp.png" alt="Hello World. Feature 1. Feature 2. Feature 3." width="600" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Visual Editor from Storyblok
&lt;/h2&gt;

&lt;p&gt;As seen earlier, the Visual Editor uses a "Welcome to the Visual Editor" by default page. To see the page we've created using Storyblok's data we must set up the preview URL. Eleventy runs locally on &lt;code&gt;http://localhost:8080&lt;/code&gt;. However, Storyblok requires an https connection to use the visual editor. There are several frameworks offering straightforward methods to use SSL in local development. Unfortunately, it's not that easy on Eleventy. &lt;/p&gt;

&lt;p&gt;However, we can fis this issue with &lt;code&gt;mkcert&lt;/code&gt; and &lt;code&gt;local-ssl-proxy&lt;/code&gt;. Before following this process, ensure your local server is stopped. If it's still running, you can stop it by clicking &lt;code&gt;Control + C&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install mkcert (globally)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; mkcert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add mkcert to your local root CAs
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Generate a certificate for your localhost on your project, signed by mkcert. This will generate two &lt;code&gt;.pem&lt;/code&gt; files.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mkcert localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install local-ssl-proxy (globally)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; local-ssl-proxy

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run Eleventy locally (and keep this terminal running it!). By default, it will run over the port 8080:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @11ty/eleventy &lt;span class="nt"&gt;--serve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Open a new terminal and run the proxy SSL server through the port 3000 (and keep it running in this new terminal):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;local-ssl-proxy &lt;span class="nt"&gt;--source&lt;/span&gt; 3000 &lt;span class="nt"&gt;--target&lt;/span&gt; 8080 &lt;span class="nt"&gt;--cert&lt;/span&gt; localhost.pem &lt;span class="nt"&gt;--key&lt;/span&gt; localhost-key.pem  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After these steps, open &lt;code&gt;https://localhost:3000&lt;/code&gt; on your browser and you should be able to see the same content as on &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With these two local servers running, you can go to Storyblok and change the preview URL. You can set it up directly from the default "Welcome to the Visual Editor" page, in the &lt;strong&gt;"Set up preview URL"&lt;/strong&gt; field. Or you can change the preview URL from &lt;code&gt;Settings &amp;gt; Configuration &amp;gt; Visual Editor&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jus01xr960l7lssqsuh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jus01xr960l7lssqsuh.png" alt="Set up preview URL" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But you will probably be a bit disappointed after doing this because you'll get a 404 error after saving it. That's because Storyblok adds the content page path to the URL. So, by default, the visual editor will try to load &lt;code&gt;https://localhost:3000/home&lt;/code&gt;. To change this, go to the page configuration and set &lt;code&gt;/&lt;/code&gt; as the Real path:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrx2wnhcximgc94ib1nz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrx2wnhcximgc94ib1nz.png" alt="Set / as the real path" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now you have it! You can see the content using the Visual Editor. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58ppdpeiglm6923lu64i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58ppdpeiglm6923lu64i.png" alt="Home page inside the visual editor" width="700" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;After following all these steps, you now know how to create content on Storyblok and consume it from Eleventy using the GraphQL API. However, you will notice that content management is not dynamic. If you change something in Storyblok, it will not be updated on the local server or in the visual editor.&lt;/p&gt;

&lt;p&gt;That's the expected behavior because Eleventy executes the GraphQL query at build time. If we serve the page locally, this means that the query won't be updated unless we change a file in our project. Therefore, any changes we make won't be visible on Storyblok until we rebuild the project on our local server.&lt;/p&gt;

&lt;p&gt;That's a bit annoying, isn't it? But don't worry. I will show you how to overcome all these inconveniences in my next article (part 3) of this series of &lt;strong&gt;Eleventy and Storyblok&lt;/strong&gt; articles.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>storyblok</category>
      <category>eleventy</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Eleventy and Storyblok — My perfect combination</title>
      <dc:creator>Estela Franco</dc:creator>
      <pubDate>Mon, 09 Oct 2023 10:59:37 +0000</pubDate>
      <link>https://dev.to/guaca/eleventy-and-storyblok-my-perfect-combination-42oe</link>
      <guid>https://dev.to/guaca/eleventy-and-storyblok-my-perfect-combination-42oe</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the first post in a series of articles called "Eleventy and Storyblok" to learn how to use this combination to create a web project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you know me, you already know that I'm a big &lt;a href="https://www.11ty.dev" rel="noopener noreferrer"&gt;Eleventy&lt;/a&gt; fan. So far, it's my favorite choice for creating a static project. But you can do &lt;a href="https://www.11ty.dev/docs/" rel="noopener noreferrer"&gt;so much more&lt;/a&gt; with it. You can even &lt;a href="https://www.11ty.dev/docs/single-page-applications/" rel="noopener noreferrer"&gt;build an SPA with 11ty&lt;/a&gt;! The only downside to the escalation of my personal projects is the lack of a CMS to generate new content. &lt;/p&gt;

&lt;p&gt;Don't get me wrong. I feel super comfortable writing in Markdown, but sometimes I miss the power of a CMS to easily check grammar mistakes or even preview the content instead of running my project on localhost. &lt;/p&gt;

&lt;p&gt;That's why I started researching how to integrate a headless CMS into an Eleventy project. After investigating a few options, I fell in love with Storyblok.&lt;/p&gt;

&lt;p&gt;In this article, I'll explain why I chose these options in the hope that you'll fall in love with them, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eleventy, a simpler static site generator
&lt;/h2&gt;

&lt;p&gt;11ty in a nutshell: Fast builds and even faster websites. That's what I'm looking for when starting a new project, so Eleventy seems to be made for my needs! But let the data speak for itself.&lt;/p&gt;

&lt;p&gt;Eleventy ran a benchmark of x4000 Markdown files (you have all the information in this &lt;a href="https://github.com/zachleat/bench-framework-markdown" rel="noopener noreferrer"&gt;open source repository&lt;/a&gt; and the results in this &lt;a href="https://www.zachleat.com/web/build-benchmark/#benchmark-results" rel="noopener noreferrer"&gt;article&lt;/a&gt;) and Eleventy performance is impressive:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;Name&lt;/th&gt;
            &lt;th colspan="2"&gt;Building ×4000 MD Files&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Eleventy&lt;/td&gt;
            &lt;td&gt;1.93s
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Astro&lt;/td&gt;
            &lt;td&gt;22.90s
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Gatsby&lt;/td&gt;
            &lt;td&gt;29.05s
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Next.js&lt;/td&gt;
            &lt;td&gt;70.65s
&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Performance is important, but so is the developer experience. And I love the versatility of 11ty: you can create a project and choose between many template languages like HTML, Markdown, Nunjucks, Liquid, WebC, and others. You can even override the template language if you need to!&lt;/p&gt;

&lt;p&gt;In addition, there are many official plugins that you can integrate into your project to take it to the next level easily. Or you can integrate plugins contributed by the community!&lt;/p&gt;

&lt;p&gt;And that's important to me: Eleventy has an active community that helps make this SSG a great option.&lt;/p&gt;

&lt;p&gt;For example, you can easily configure your templates using data keys like permalinks, layouts, pagination, tags, etc. And integrate plugins like Image (to resize and generate images), RSS, i18n (for internationalization), Navigation (also for breadcrumbs!), Shopify (import your Shopify products, pages and collections into Eleventy as global data), and more.&lt;/p&gt;

&lt;p&gt;But don't take my word for it and see who trusts Eleventy to build their projects: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IYG0S8ZY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/403pvv6utx4v4m5opq2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IYG0S8ZY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/403pvv6utx4v4m5opq2f.png" alt="NASA, Cern, TC39, W3C, Google, Netlify, web.dev, MIT, Stanford, V8, Chrome Developers, Foursquare, CSS Tricks, WebPageTest, A11Y Project, NOAA, United Kingdom (gov), France (gov), Stack Overflow, Lit, Glitch, Stackblitz, Open Web Docs, jamstack.org, Greenpeace, CSIS, ESLint, Mocha, reveal.js, Panic, Filament Group, Set Studio, Ookla, Khan Academy, Basecamp, Snowpack, ffconf, UX London, Nordhealth, Orange, Univ. of Wisconsin-Madison, California (gov), Red Hat, Just Eat Takeaway and more…" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Storyblok, the way to modern content
&lt;/h2&gt;

&lt;p&gt;As you can see, Eleventy is a super powerful SSG that provides a great DX. However, if your project has content editors creating articles and pages, they won't be as excited as developers. Creating content directly on an SSG project can be difficult until they know how to write using Markdown or other project options. &lt;/p&gt;

&lt;p&gt;So implementing a headless CMS is a great solution to solve this problem. As I told you before, after investigating several options, I fell in love with &lt;a href="https://www.storyblok.com/" rel="noopener noreferrer"&gt;Storyblok&lt;/a&gt;, and you will agree with me after reading this article.&lt;/p&gt;

&lt;p&gt;But let's take a step back. &lt;/p&gt;

&lt;h3&gt;
  
  
  What's a headless CMS?
&lt;/h3&gt;

&lt;p&gt;A headless CMS is a pure backend web content management system that acts as a content repository. The content is then exposed through an API for display on any device, without a built-in front-end. This separates your data (body) from its presentation (head).&lt;/p&gt;

&lt;p&gt;Separating the two gives you remarkable freedom and allows you to integrate Storyblok into any project such as websites, ecommerces, or apps. Moreover, developers can use whatever technology they want without worrying about how it affects the front end, and content editors can reuse their content on any number of devices of any type because it's not tied to a specific channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oCklXL3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4as3pcq6px27yi34w24s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oCklXL3x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4as3pcq6px27yi34w24s.png" alt="Headless CMS shema" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Storyblok?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.storyblok.com/" rel="noopener noreferrer"&gt;Storyblok&lt;/a&gt; is a headless CMS designed to empower all content stakeholders while providing a cloud-native, headless architecture that allows developers to effortlessly work with and adapt to any technology. &lt;/p&gt;

&lt;p&gt;Here are some of its most notable features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Headless: You can integrate Storyblok into any front-end project because it's a headless solution. Moreover, you can change your front-end and easily migrate your content from one technology to another.&lt;/li&gt;
&lt;li&gt;Cloud-native: You don't have to host it. Storyblok does that for you.&lt;/li&gt;
&lt;li&gt;Internationalization: Excellent multilingual management. You can manage your translations at three different levels: field level, folder level, or space level. This allows you to choose the approach that best suits your project.&lt;/li&gt;
&lt;li&gt;Visual Editor: You don't need to know Markdown or any other markup languages to create and manage your content. Storyblok has a visual editor where you can visually manage, organize and preview your content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And trust me, the Visual Editor looks amazing and your content editors will love it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HOq8ehno--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2tpb8cj8eywkee1n2i0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HOq8ehno--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2tpb8cj8eywkee1n2i0.png" alt="Storyblok's visual editor" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, Storyblok has a preview feature where you can see how the content will fit on the page on the fly! &lt;/p&gt;

&lt;p&gt;If you want to learn more about Storyblok's features, check out this video (highly recommended!):&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/902M1Pt6yW8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How to integrate Storyblok into Eleventy
&lt;/h2&gt;

&lt;p&gt;As a headless CMS, you can integrate Storyblok into any front-end project. However, it is easier depending on the technology you use.&lt;/p&gt;

&lt;p&gt;Storyblok works with many modern frameworks and maintains SDKs to install in Vue, Nuxt, React, Next, Astro, SvelteKit, Remix or Gatsby projects. However, there is no official Eleventy SDK (yet).&lt;/p&gt;

&lt;p&gt;Therefore, using the &lt;a href="https://github.com/storyblok/storyblok-js" rel="noopener noreferrer"&gt;JavaScript SDK&lt;/a&gt; is necessary to integrate Storyblok into an Eleventy project. &lt;/p&gt;

&lt;p&gt;Want to see it in action? Stay tuned! In a future article, I'll explain step-by-step how to integrate Storyblok into an Eleventy project using the JavaScript SDK.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>storyblok</category>
      <category>eleventy</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
