<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ graphcms - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ graphcms - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 15:48:59 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/graphcms/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Build your Developer Portfolio and Blog from Scratch with Svelte and GraphCMS – A Complete Guide ]]>
                </title>
                <description>
                    <![CDATA[ By Scott Spence A developer portfolio is a great way to showcase your current skill level to potential employers.  This guide will go from hello world to a fully-featured portfolio project to show your current projects with images and links to source... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-your-developer-portfolio-from-scratch-with-sveltekit-and-graphcms/</link>
                <guid isPermaLink="false">66d8521be0db794d56c01c09</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ graphcms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Svelte ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 06 Jan 2022 16:12:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/simon-abrams-k_T9Zj3SE8k-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Scott Spence</p>
<p>A developer portfolio is a great way to showcase your current skill level to potential employers. </p>
<p>This guide will go from hello world to a fully-featured portfolio project to show your current projects with images and links to source code. You'll also build an accompanying blog where you can detail what you have learned along the way.</p>
<p>Hi 👋, my name is <a target="_blank" href="https://scottspence.com">Scott</a>, and I have been blogging about my journey through web development since July 2016. </p>
<p>I'm a freeCodeCamp alumnus – I started my freeCodeCamp journey back in 2016 – and have been a professional developer since March, 2018. </p>
<p>I have written about <a target="_blank" href="https://www.freecodecamp.org/news/build-a-developer-blog-from-scratch-with-gatsby-and-mdx/">how to build a Gatsby blog from scratch in the past</a> and want to take you through doing the same again, this time with Svelte!</p>
<p>This quite an extensive guide (33 sections!) so I've added a table of content's to help you navigate around the post:</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-were-going-to-build">What we're going to build</a></li>
<li><a class="post-section-overview" href="#heading-whos-this-guide-for">Who's this guide for?</a></li>
<li><a class="post-section-overview" href="#heading-the-stack-what-technology-well-be-using">The stack (what technology we'll be using)</a></li>
<li><a class="post-section-overview" href="#heading-what-is-svelte">What is Svelte?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-sveltekit">What is SvelteKit?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-vite">What is Vite?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-graphql">What is GraphQL?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-graphcms">What is GraphCMS?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-graphcms">How to Set Up GraphCMS</a></li>
<li><a class="post-section-overview" href="#heading-how-to-query-content">How to Query Content</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-your-svelte-project">How to Create Your Svelte Project</a></li>
<li><a class="post-section-overview" href="#heading-how-to-show-graphql-data-on-the-index-page">How to Show GraphQL Data on the Index Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-markup-for-the-index-page">How to Add Markup for the Index Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-first-svelte-component">How to Build the First Svelte Component</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-in-svelte">How to Style in Svelte</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-with-tailwind-and-daisyui">How to Style with Tailwind and DaisyUI</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-projects-component">How to Style the Projects Component</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-sveltekit-layout-file">How to Use the SvelteKit <code>__layout</code> File</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-landing-page-with-projects-listed">How to Build the Landing Page with Projects Listed</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-sveltekit-routing">How to Use SvelteKit Routing</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-blog">How to Build the Blog</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-navbar-and-footer-components">How to Build the Navbar and Footer Components</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-a-theme-switch">How to Add a Theme Switch</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-the-about-page">How to Add the About Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-a-sitemap">How to Make a Sitemap</a></li>
<li><a class="post-section-overview" href="#heading-robotstxt">Robots.txt</a></li>
<li><a class="post-section-overview" href="#heading-rss-feed-generation">RSS Feed Generation</a></li>
<li><a class="post-section-overview" href="#heading-email-signup-with-revue">Email Signup with Revue</a></li>
<li><a class="post-section-overview" href="#heading-continuous-deployment-with-vercel">Continuous Deployment with Vercel</a></li>
<li><a class="post-section-overview" href="#heading-google-search-console">Google Search Console</a></li>
<li><a class="post-section-overview" href="#heading-resources">Resources</a></li>
<li><a class="post-section-overview" href="#heading-what-we-have-accomplished">What we have accomplished</a></li>
</ul>
<h2 id="heading-what-were-going-to-build"><strong>What we're going to build</strong></h2>
<p>We will build a fully-featured portfolio and blog with the framework that took the top spot for most loved framework on the Stack Overflow developer survey in 2021 – Svelte.</p>
<p>Using Svelte with GraphCMS means you can control the adding and removing of content on your site without the need to push any changes to Git.</p>
<p>Features:</p>
<ul>
<li>Landing page with projects listed</li>
<li>Blog</li>
<li>Theme switch</li>
<li>Sitemap</li>
<li>RSS feed generation</li>
<li>Robots.txt</li>
<li>Continuous Deployment with Vercel</li>
<li>Build integrations to publish and build the site on content changes</li>
</ul>
<p>There's also an optional email signup section with resources mentioned, but it's not essential to this project we're about to start. You can find resources for that toward the end.</p>
<p>One thing that's typically not covered with guides like this is actual deployment and getting your site on search engines like Google. But here, I'll be going through the whole process so you can have something you will be proud of at the end.</p>
<p>If you want to take it a step further with analytics, then check out my guide on configuring a Svelte project with Fathom Analytics, the privacy-first analytics provider. But I didn't include it here, as it's a paid feature and out of the scope of free.</p>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>This guide comes with a few presumptions for the reader:</p>
<ul>
<li>An understanding of HTML, CSS, and JavaScript (the holy trinity of web development)</li>
<li>A GitHub account or similar (GitLab or Bitbucket). Not essential but some hosting services require you to connect a Git repository.</li>
<li>A development environment, Node.js installed on your machine version 14+, a terminal, and a text editor like VS Code.</li>
<li>There are browser options like <a target="_blank" href="https://github.com/features/codespaces">GitHub codespaces</a> or <a target="_blank" href="https://www.gitpod.io/">Gitpod</a> if you don't have these configured.</li>
</ul>
<p>If you don't have a development environment set up there's no need to worry – you can use Gitpod to spin up an environment with this link: <a target="_blank" href="http://gitpod.io/#https://github.com/spences10/sveltekit-skeleton">http://gitpod.io/#https://github.com/spences10/sveltekit-skeleton</a></p>
<p>This will get you started with the SvelteKit skeleton which is created when you use the CLI to create a new SvelteKit project.</p>
<p>I'll be using Microsoft's <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> (VS Code) along with the VS Code integrated Git client.</p>
<p>There'll be a Git commit at the end of each section. This is optional but helps get you into the habit of committing regularly. It's also useful for when you want to deploy the project at the end. </p>
<h2 id="heading-whos-this-guide-for"><strong>Who's this guide for?</strong></h2>
<p>If you are well on your way through the freeCodeCamp curriculum and want to have something to show where your current skill level is, this guide will be a great accompaniment.</p>
<p>This guide will give you all you need to get started with Svelte and give you the confidence to start making your own projects with it.</p>
<h2 id="heading-the-stack-what-technology-well-be-using">The stack (what technology we'll be using)</h2>
<p>Although I have mentioned a lot of the technology we'll be using, I'll take this opportunity to list out what we'll be using whilst we go through this guide.</p>
<ul>
<li>SvelteKit – the framework we'll use to create the pages and components</li>
<li>Tailwind + daisyUI – how we'll style the project</li>
<li>Tailwind CSS Typography to take care of styling the text content</li>
<li>Marked to convert the Markdown content to HTML </li>
<li>GraphCMS – where we'll store the content for the project details and blog posts</li>
<li>graphql-request – used to query data from the GraphCMS API</li>
</ul>
<h2 id="heading-what-is-svelte"><strong>What is Svelte?</strong></h2>
<p>Svelte is a component framework that allows you to write pages and components in what you're used to – HTML, CSS and JavaScript. It is an open-source front end compiler created by <a target="_blank" href="https://twitter.com/Rich_Harris">Rich Harris</a> and maintained by the Svelte core team members. </p>
<p>Note that it is a compiler. This means that all the HTML, CSS, and JavaScript are built up-front into standalone JavaScript modules that reduces the load on the client (the browser).</p>
<p>It's compiled, rather than shipping a JavaScript run-time to the browser like React or Vue. This produces a much leaner project being shipped to the browser.</p>
<h2 id="heading-what-is-sveltekit">What is SvelteKit?</h2>
<p>SvelteKit is a framework that has the Svelte language at it's core with some added features. These include file-based routing, endpoints, and layouts, to name a few.</p>
<p>Endpoints in SvelteKit are modules that you can write in JavaScript to create HTTP methods (get, post, delete), which can be accessed in SvelteKit via the SvelteKit fetch API. More on this later.</p>
<h2 id="heading-what-is-vite">What is Vite?</h2>
<p>Vite is the build tool you use to compile SvelteKit projects. Vite was created by Evan You, the creator of Vue. Vite is framework-agnostic and a great addition to the SvelteKit toolset.</p>
<h2 id="heading-what-is-graphql"><strong>What is GraphQL?</strong></h2>
<p>GraphQL is a query language for APIs, giving users and clients the flexibility to ask for the data they need when they need it. </p>
<p>A GraphQL query looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-1.png" alt="A GraphQL query displaying the query on the left and the results on the right" width="600" height="400" loading="lazy">
<em>A GraphQL query displaying the query on the left and the results on the right</em></p>
<p>On the left is the <code>query</code> which is for the name field in the project model with the <code>"data"</code> being returned in the resulting query on the right.</p>
<p>The query returned in JavaScript Object Notation (JSON) is what can be consumed by the client (a browser, a mobile app, an in store display or a fridge).</p>
<h2 id="heading-what-is-graphcms"><strong>What is GraphCMS?</strong></h2>
<p>GraphCMS is a headless GraphQL-based Content Management System (CMS) that will let you spin up a back end for your content delivery. </p>
<p>You can do this in minutes with the click of a button from one of the provided templates or you can build your own schema with the simple user interface (UI).</p>
<h2 id="heading-how-to-set-up-graphcms"><strong>How to Set Up GraphCMS</strong></h2>
<p>The team over at GraphCMS created a template for this, so setting up the backend for this is a one click deploy.</p>
<p>You'll need to log into <a target="_blank" href="https://auth.graphcms.com/">GraphCMS</a> first. You can log in with your GitHub account or authenticate via other means.</p>
<p>Once you are logged in you will be presented with your GraphCMS dashboard. If this is your first time using GraphCMS you can scroll down the page to "Developer Portfolio &amp; Blog" in the "Create a new project" section. Select "Developer Portfolio &amp; Blog" and click "+ Create project".</p>
<p>We're then prompted to give our project a name. I'm going to call it "Portfolio and Blog", and the description can be left blank for now. You can pick the data centre closest to you for where your project will be hosted. I'm in the UK so I'll pick the UK data centre.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Pick your data centre</em></p>
<p>Note, if you are adding your own content then toggle "Include template content?" on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-6.png" alt="Image" width="600" height="400" loading="lazy">
<em>Leave this toggled if you are going to add your content at a later date.</em></p>
<p>As a side note, all content for GraphCMS is served from a globally distributed CDN so there's no need to worry about latency for users not near your specified data centre.</p>
<p>Click the "Create project" button at the bottom of the page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-21.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once the project has finished being provisioned you are presented with what plan you want to use. Pick the community "Free forever" plan.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-22.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>There will be another prompt to ask you if you want to invite teammates. Just select "Invite later".</p>
<p>The GraphCMS dashboard will look like this. All of the project sections are on the panel on the left. In the next section we'll be taking a look at those.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-24.png" alt="Image" width="600" height="400" loading="lazy">
<em>The GraphCMS dashboard with arrows pointing to the Schema, Content, Assets and API Playground sections.</em></p>
<h2 id="heading-how-to-query-content">How to Query Content</h2>
<p>Let's make our first GraphQL query. This is going to be a list of all the projects added to the CMS that are in the project model.</p>
<p>Go to the API playground and enter the following GraphQL query into the "New Query" tab in the GraphQL playground.</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> GetProjects {
  projects {
    name
    slug
    description
    demo
    sourceCode
    image {
      url
    }
  }
}
</code></pre>
<p>This query is selecting the <code>projects</code> model then each field that is contained in that model. </p>
<h2 id="heading-how-to-create-your-svelte-project"><strong>How to Create Your Svelte Project</strong></h2>
<p>If you're using Gitpod you can skip to creating a <code>.env</code> file. If you're setting up locally then let's get started. From the terminal we can create our project with the following <code>npm</code> command:</p>
<pre><code class="lang-bash">npm init svelte@next my-developer-portfolio
</code></pre>
<p>From the CLI I'll pick the following options:</p>
<pre><code class="lang-bash">? Which Svelte app template? › - Use arrow-keys. Return to submit.
    SvelteKit demo app
❯   Skeleton project
? Use TypeScript? › No
? Add ESLint <span class="hljs-keyword">for</span> code linting? › No
? Add Prettier <span class="hljs-keyword">for</span> code formatting? › Yes
</code></pre>
<p>I'll follow the rest of the instructions from the CLI. If you take a look at the output from the CLI you'll also notice a couple of other features we'll be taking advantage of soon. Here's what my output looks like:</p>
<pre><code class="lang-bash">Your project is ready!
✔ Prettier
  https://prettier.io/docs/en/options.html
  https://github.com/sveltejs/prettier-plugin-svelte<span class="hljs-comment">#options</span>

Install community-maintained integrations:
  https://github.com/svelte-add/svelte-adders

Next steps:
  1: <span class="hljs-built_in">cd</span> my-developer-portfolio
  2: npm install (or pnpm install, etc)
  3: git init &amp;&amp; git add -A &amp;&amp; git commit -m <span class="hljs-string">"Initial commit"</span> (optional)
  4: npm run dev -- --open

To close the dev server, hit Ctrl-C

Stuck? Visit us at https://svelte.dev/chat
</code></pre>
<p>Take note of the "Install community-maintained integrations" section with Svelte Adders – we'll be using one of those later for adding Tailwind.</p>
<p>Now to change directory (CD) into the project folder, initialise a Git repository, and install the dependencies:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># cd into project directory</span>
<span class="hljs-built_in">cd</span> my-developer-portfolio
<span class="hljs-comment"># initialise a new git repo and make first commit</span>
git init &amp;&amp; git add -A &amp;&amp; git commit -m <span class="hljs-string">"Initial commit"</span>
<span class="hljs-comment"># install dependencies</span>
npm install <span class="hljs-comment"># or 'npm i' for shorthand</span>
</code></pre>
<p>I'll open my text editor and check out the project. I have VS Code installed so using the <code>code</code> command will open it and the <code>.</code> specified the current directory:</p>
<pre><code class="lang-bash">code .
</code></pre>
<p>Time to check that everything is up and running as expected, so let's spin up the dev server:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># start the dev server</span>
npm run dev
</code></pre>
<p>Now that we've validated everything is working as expected, it's time to make a <code>.env</code> file. This is where the GraphQL API URL is going to live. You can create the file with your text editor user interface (UI) if you choose. I'll be using the following command from the root of my project to create the file:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Ctrl-c to stop the dev server</span>
touch .env
<span class="hljs-built_in">echo</span> VITE_GRAPHQL_API= &gt;&gt; .env
</code></pre>
<p>That command from the terminal is creating a <code>.env</code> file then adding <code>VITE_GRAPHQL_API=</code> into that file.</p>
<p>In the <code>.env</code> file, add the "Content API" URL from the GraphCMS project. </p>
<p>The settings panel can be accessed form the sidebar:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-7.png" alt="Image" width="600" height="400" loading="lazy">
<em>GraphCMS project settings</em></p>
<p>Then "API access": </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-8.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then click on the "Content API" URL. This will copy it to the clipboard for you:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-9.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select the content API URL</em></p>
<p>Now add that to the <code>.env</code> file. It should now look something like this:</p>
<pre><code class="lang-env">VITE_GRAPHQL_API=https://api-region.graphcms.com/v2/projectid/master
</code></pre>
<h2 id="heading-how-to-show-graphql-data-on-the-index-page">How to Show GraphQL Data on the Index Page</h2>
<p>Let's make our first request to our GraphQL API!</p>
<p>First up, to get some data on the page we're going to make the request to the GraphQL API from the index page.</p>
<p>To do that we'll need to install a couple of dependencies, <code>graphql-request</code> and <code>graphql</code>. <code>graphql-request</code> is what we'll be using to send our GraphQL queries to the GraphQL API. <code>graphql</code> is the JavaScript implementation of the GraphQL language.</p>
<pre><code class="lang-bash">npm i -D graphql-request graphql
</code></pre>
<p>Note the <code>-D</code> in the install command there. That's because Svelte doesn't need any run-time dependencies as it compiles the code upfront before sending it to the browser.</p>
<p>Let's start by adding a script block with the context of module <code>&lt;script context="module"&gt;</code> and import the <code>gql</code> tag and the <code>GraphQLClient</code> from <code>graphql-request</code>. </p>
<p>We'll also define a SvelteKit load function. This is so we can grab the data from the API before the page mounts (loads). </p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { gql, GraphQLClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {

  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Inside the SvelteKit <code>load</code> function we can then define a new GraphQL client. The client accepts a URL (the GraphCMS API URL) and an options object. </p>
<p>We're going to put in the <code>VITE_GRAPHQL_API</code> we created earlier. Note that the variable starts with <code>VITE_</code> which means that Vite can use this variable. We'll need to import it with <code>import.meta.env</code>, and it should look a lot like this:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { gql, GraphQLClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> GraphQLClient(
      <span class="hljs-keyword">import</span>.meta.env.VITE_GRAPHQL_API
    )
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Now that the client is defined we can use it to pass a query to the GraphCMS GraphQL API.</p>
<p>Taking the query we made earlier to query for all projects, we can add that to a <code>query</code> variable to use with the GraphQL client we defined.</p>
<p>The query uses the GraphQL <code>gql</code> language tag inside backticks <code>gql```. Then we can destructure the projects from the</code>await`'ed response we get from the GraphQL client:  </p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { gql, GraphQLClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> GraphQLClient(
      <span class="hljs-keyword">import</span>.meta.env.VITE_GRAPHQL_API
    )

    <span class="hljs-keyword">const</span> query = gql<span class="hljs-string">`
      query GetProjects {
        projects {
          name
          slug
          description
          demo
          sourceCode
          image {
            url
          }
        }
      }
    `</span>

    <span class="hljs-keyword">const</span> { projects } = <span class="hljs-keyword">await</span> client.request(query)
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Now that the client has the query, we can return the data from the response of the client <code>projects</code> and return them as props for the page to use.</p>
<p>The data from the GraphQL API can now be passed to the page as <code>props</code> in the return of the <code>load</code> function: </p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { gql, GraphQLClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> GraphQLClient(
      <span class="hljs-keyword">import</span>.meta.env.VITE_GRAPHQL_API
    )

    <span class="hljs-keyword">const</span> query = gql<span class="hljs-string">`
      query GetProjects {
        projects {
          name
          slug
          description
          demo
          sourceCode
          image {
            url
          }
        }
      }
    `</span>

    <span class="hljs-keyword">const</span> { projects } = <span class="hljs-keyword">await</span> client.request(query)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        projects,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>Now that the data is being returned we need to bring that into the page.</p>
<p>We can do that in the <code>&lt;script&gt;</code> tags on the page. So yes there two sets of script tags – the first <code>&lt;script context="module"&gt;</code> to run the SvelteKit <code>load</code> function before the page loads (or mounts), then the regular <code>&lt;script&gt;</code> tags to define any JavaScript needed on the <code>index.svelte</code> file and to also to accept the <code>props</code> which is <code>projects</code>.</p>
<p>In the last section here we're accepting the <code>projects</code> returned from the <code>load</code> function with <code>export let projects</code> in the <code>&lt;script&gt;</code> tags. Now that variable can be used in the page.</p>
<p>For illustration purposes I'm adding the <code>projects</code> variable into a <code>&lt;pre&gt;</code> tag and stringifying the results with <code>{JSON.stringify(projects, null, 2)}</code>. This is temporary so that we can validate and visualise the data coming into the page. </p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { gql, GraphQLClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> GraphQLClient(
      <span class="hljs-keyword">import</span>.meta.env.VITE_GRAPHQL_API
    )

    <span class="hljs-keyword">const</span> query = gql<span class="hljs-string">`
      query GetProjects {
        projects {
          name
          slug
          description
          demo
          sourceCode
          image {
            url
          }
        }
      }
    `</span>

    <span class="hljs-keyword">const</span> { projects } = <span class="hljs-keyword">await</span> client.request(query)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        projects,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> projects
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">pre</span>&gt;</span></span><span class="javascript">{<span class="hljs-built_in">JSON</span>.stringify(projects, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">pre</span>&gt;</span></span>
</code></pre>
<p>Time to start up the dev server and see how things look now:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>Here's the output which looks really similar to the Projects GraphQL output in the GraphQL playground we made earlier:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-48.png" alt="Image" width="600" height="400" loading="lazy">
<em>localhost output after running <code>npm run dev</code></em></p>
<p>I know I really walked you through every step of that one. That's to highlight the different sections of what we're doing.</p>
<p>This will be a similar pattern for the rest of of the project.  </p>
<p>The following steps will look like this: </p>
<ol>
<li>Make a GraphQL query to define the data needed.</li>
<li>Give that query to the GraphQL client.</li>
<li>Work with the returned data from the client in the page.</li>
</ol>
<h3 id="heading-refactor-the-graphql-client">Refactor the GraphQL client</h3>
<p>As we'll be using the GraphQL client in more than one page, it's time to move that out into its own file so that it can be reused throughout the project.</p>
<p>Svelte has a <code>lib</code> folder for files that are reused throughout the project but there's not a folder (or directory if you prefer that term) for that yet – so it's time to create one. We can create a <code>graphql-client.js</code> file for the GraphQL client to go in:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># make the folder</span>
mkdir src/lib
<span class="hljs-comment"># create the file</span>
touch src/lib/graphql-client.js
</code></pre>
<p>Now to move out the client from the index page to the newly created <code>src/lib/graphql-client.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { GraphQLClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>
<span class="hljs-keyword">const</span> GRAPHQL_ENDPOINT = <span class="hljs-keyword">import</span>.meta.env.VITE_GRAPHQL_API

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> GraphQLClient(GRAPHQL_ENDPOINT)
</code></pre>
<p>In the <code>src/routes/index.svelte</code> I can remove the initialisation of the client and import the client from its newly created file in the lib folder. </p>
<p>Here's the difference. If you're not familiar with a Git diff, then the <code>+</code> and <code>-</code> next to the lines means that those lines are added (<code>+</code> ) or removed (<code>-</code>):</p>
<pre><code class="lang-git">&lt;script context="module"&gt;
+  import { client } from '$lib/graphql-client'
-  import { gql, GraphQLClient } from 'graphql-request'
+  import { gql } from 'graphql-request'

  export const load = async () =&gt; {
-   const client = new GraphQLClient(import.meta.env.VITE_GRAPHQL_API)

    const query = gql`
      query GetProjects {
        projects {
          name
          slug
          description
          demo
          sourceCode
          image {
            url
          }
        }
      }
    `

    const { projects } = await client.request(query)

    return {
      props: {
        projects,
      },
    }
  }
&lt;/script&gt;

&lt;script&gt;
  export let projects
&lt;/script&gt;

&lt;pre&gt;{JSON.stringify(projects, null, 2)}&lt;/pre&gt;
</code></pre>
<p>With that done we can start using the refactored client in our index page.</p>
<p>Let's commit our changes to Git before moving onto the next section:</p>
<pre><code class="lang-git">git add .
git commit -m "Show GraphQL data on index page"
</code></pre>
<h2 id="heading-how-to-add-markup-for-the-index-page">How to Add M<strong>arkup</strong> for the Index Page</h2>
<p>Up until now we've really only displayed the data from the API endpoint in a pre tag. Time to change that by breaking up the data returned from the GraphQL API into sections on the index page.</p>
<p>So let's start by removing the <code>&lt;pre&gt;</code> tag, adding in a <code>&lt;h1&gt;</code> for the page title, then in a <code>&lt;div&gt;</code> we can use one of the Svelte expressions to loop through the data with Svelte <code>{#each}</code>.</p>
<p>The each expression takes in the <code>projects</code> object. Then you can work with a variable for that, let's say <code>project</code>, and you can reference the various properties on that variable.</p>
<p>Here's an example of how that could look:</p>
<pre><code class="lang-svelte"><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> projects <span class="hljs-keyword">as</span> project}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{project.image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{project.name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{<span class="hljs-string">`/projects/<span class="hljs-subst">${project.slug}</span>`</span>}</span><span class="xml"><span class="hljs-tag">&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span></span><span class="javascript">{project.name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          </span><span class="javascript">{project.description.slice(<span class="hljs-number">0</span>, <span class="hljs-number">80</span>)}</span><span class="xml">...
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span>
</code></pre>
<p>To take it a step further we can de-structure the properties from that part of the loop so there's no need to reference the specific properties from <code>project</code>.</p>
<p>Note that the <code>image.url</code> is also being de-structured here as well.</p>
<p>So instead of <code>{#each projects as project}</code> we can do this <code>{#each projects as { name, slug, description, image }}</code>.</p>
<p>Here's how the <code>src/routes/index.svelte</code> file should look now:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> query = gql<span class="hljs-string">`
      query GetProjects {
        projects {
          name
          slug
          description
          tags
          demo
          sourceCode
          image {
            url
          }
        }
      }
    `</span>
    <span class="hljs-keyword">const</span> { projects } = <span class="hljs-keyword">await</span> client.request(query)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        projects,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> projects
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Recent Projects by Me<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> projects <span class="hljs-keyword">as</span> { name, slug, description, image }}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{<span class="hljs-string">`/projects/<span class="hljs-subst">${slug}</span>`</span>}</span><span class="xml"><span class="hljs-tag">&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
            </span><span class="javascript">{description.slice(<span class="hljs-number">0</span>, <span class="hljs-number">80</span>)}</span><span class="xml">...
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<h2 id="heading-how-to-build-the-first-svelte-component">How to Build the First Svelte Component</h2>
<p>What we're going to do now is make our first Svelte component. This will be for the project card we made in the last code block.</p>
<p>This is so we can reuse that code in other parts of the project. So, it's going to be everything inside the <code>{#each}</code> loop we did to display each project on the index page, this section here:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{<span class="hljs-string">`/projects/<span class="hljs-subst">${slug}</span>`</span>}</span><span class="xml"><span class="hljs-tag">&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        </span><span class="javascript">{description.slice(<span class="hljs-number">0</span>, <span class="hljs-number">80</span>)}</span><span class="xml">...
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Let's create a <code>lib</code> folder and a <code>project-card.svelte</code> component to go in that folder:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># make components folder</span>
mkdir src/lib/components
<span class="hljs-comment"># create the component file</span>
touch src/lib/components/project-card.svelte
</code></pre>
<p>In that file we can now add in the markup for the project card:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{<span class="hljs-string">`/projects/<span class="hljs-subst">${slug}</span>`</span>}</span><span class="xml"><span class="hljs-tag">&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        </span><span class="javascript">{description.slice(<span class="hljs-number">0</span>, <span class="hljs-number">80</span>)}</span><span class="xml">...
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>The markup at the moment has the variables for the image URL, the project name, and description. Currently this won't work because those variables are not referenced anywhere.</p>
<p>Inside some <code>&lt;script&gt;</code> tags we can define the variables that are expected by the component.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> url = <span class="hljs-string">''</span>
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> name = <span class="hljs-string">''</span>
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> slug = <span class="hljs-string">''</span>
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> description = <span class="hljs-string">''</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{<span class="hljs-string">`/projects/<span class="hljs-subst">${slug}</span>`</span>}</span><span class="xml"><span class="hljs-tag">&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        </span><span class="javascript">{description.slice(<span class="hljs-number">0</span>, <span class="hljs-number">80</span>)}</span><span class="xml">...
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>With the component now ready to accept the variables for the project, we can pass them into the component on the index page.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> ProjectCard <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/project-card.svelte'</span>
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> query = gql<span class="hljs-string">`
      query GetProjects {
        projects {
          name
          slug
          description
          tags
          demo
          sourceCode
          image {
            url
          }
        }
      }
    `</span>
    <span class="hljs-keyword">const</span> { projects } = <span class="hljs-keyword">await</span> client.request(query)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        projects,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> projects
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Recent Projects by Me<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> projects <span class="hljs-keyword">as</span> { name, slug, description, image }}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">ProjectCard</span> </span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{description}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">url</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{slug}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>The component is being imported between the <code>&lt;script&gt;</code> tags, and then the individual variables from the loop are being passed into it.</p>
<p>Let's take a quick look at the variables being passed. They could be defined like this:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ProjectCard</span>
  <span class="hljs-attr">name</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">
  <span class="hljs-attr">description</span>=</span></span><span class="javascript">{description}</span><span class="xml"><span class="hljs-tag">
  <span class="hljs-attr">url</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag">
  <span class="hljs-attr">slug</span>=</span></span><span class="javascript">{slug}</span><span class="xml"><span class="hljs-tag">
/&gt;</span></span>
</code></pre>
<p>Because the expected props on the component are the same as what's being passed, then there is no need to label the props. So this is what we can use:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ProjectCard</span> </span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{description}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">url</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{slug}</span><span class="xml"><span class="hljs-tag"> /&gt;</span></span>
</code></pre>
<p>Note that the <code>image</code> property is an array (as the project can take multiple images) so we're referencing the first index of that array.</p>
<p>Let's commit this before moving onto the next section:</p>
<pre><code class="lang-git">git add .
git commit -m "Add first component"
</code></pre>
<h2 id="heading-how-to-style-in-svelte">How to Style in Svelte</h2>
<p>With Svelte being a superset of HTML, this means that you can style your <code>.svelte</code> files the same way you would in HTML files.</p>
<p>Adding some <code>&lt;style&gt;</code> tags at the bottom of the file means you can style the elements on the page:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Hello Svelte<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span></span><span class="css">
  <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">color</span>: red;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre>
<p>This will style all the <code>&lt;p&gt;</code> elements in that file with red font and a font size of 2rem.</p>
<p>You get a lot of control this way, allowing you to specify stiles to that file alone.</p>
<p>This is just an example, and it's not how I will be doing the styling for this project. I'm instead opting for Tailwind CSS.</p>
<h2 id="heading-how-to-style-with-tailwind-and-daisyui">How to Style with Tailwind and DaisyUI</h2>
<p>Styling is a very opinionated and personal subject, so what I'll be doing may not fit with what you have in mind.</p>
<p>For that reason I'll keep styling to a minimum and try not to focus too much on it.</p>
<p>I'll be using <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a> and <a target="_blank" href="https://daisyui.com/">daisyUI</a> for the speed at which I can create components and styles. If this isn't for you, you can continue styling as suggested in the last section.</p>
<p>I am going to be using <code>svelte-add</code> to configure the project to use TailwindCSS. The Svelte Adders project I mentioned earlier does all the configuration for you with an <code>npm</code> command:</p>
<pre><code class="lang-bash">npx svelte-add@latest tailwindcss
<span class="hljs-comment"># install configured dependencies</span>
npm i
</code></pre>
<p>The <code>svelte-add</code> command configured the project for use with Tailwind. It also added a file in <code>src/routes</code> called <code>__layout.svelte</code> – we'll come to this shortly. For now know that it's there, and we will be using it in an upcoming section.</p>
<p>I'm also going to be using a couple of TailwindCSS plugins – these are daisyUI and the TailwindCSS Typography plugin.</p>
<p>daisyUI is a great resource for pre-made components, and you can pick out a number of them from the site. Which is what I'll be doing for the header and footer components.</p>
<p>Tailwind CSS Typography is really useful for styling the content we're getting back from the API. It's a great set of defaults from the Tailwind Labs team.</p>
<p>I'll install them via the terminal:</p>
<pre><code class="lang-bash">npm i -D @tailwindcss/typography daisyui
</code></pre>
<p>Then I can configure them in the <code>tailwind.config.cjs</code> file:</p>
<pre><code class="lang-js">plugins: [
  <span class="hljs-built_in">require</span>(<span class="hljs-string">'@tailwindcss/typography'</span>),
  <span class="hljs-built_in">require</span>(<span class="hljs-string">'daisyui'</span>),
],
</code></pre>
<p>There's some additional configuration needed for the TailwindCSS Typography plugin to remove the max width. Here's what the full <code>tailwind.config.cjs</code> looks like:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> config = {
  <span class="hljs-attr">content</span>: [<span class="hljs-string">'./src/**/*.{html,js,svelte,ts}'</span>],

  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {
      <span class="hljs-attr">typography</span>: {
        <span class="hljs-attr">DEFAULT</span>: {
          <span class="hljs-attr">css</span>: {
            <span class="hljs-attr">maxWidth</span>: <span class="hljs-literal">null</span>,
          },
        },
      },
    },
  },

  <span class="hljs-attr">plugins</span>: [<span class="hljs-built_in">require</span>(<span class="hljs-string">'@tailwindcss/typography'</span>), <span class="hljs-built_in">require</span>(<span class="hljs-string">'daisyui'</span>)],
}

<span class="hljs-built_in">module</span>.exports = config
</code></pre>
<p>Let's spin up the dev server and validate the install. The project font will be different now.</p>
<p>Commit the changes and we'll move onto the next section:</p>
<pre><code class="lang-git">git add .
git commit -m "Add Tailwind CSS and daisyUI"
</code></pre>
<h2 id="heading-how-to-style-the-projects-component">How to Style the Projects Component</h2>
<p>Ok, now I can add in some styles for the <code>src/components/project-card.svelte</code> file.</p>
<p>This uses several Tailwind classes, and will probably be as much as we deviate from the pre-packaged classes we get from daisyUI:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> url = <span class="hljs-string">''</span>
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> name = <span class="hljs-string">''</span>
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> slug = <span class="hljs-string">''</span>
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> description = <span class="hljs-string">''</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative group card shadow-2xl col-span-2"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">class</span>=<span class="hljs-string">"object-cover h-full"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{<span class="hljs-string">`/projects/<span class="hljs-subst">${slug}</span>`</span>}</span><span class="xml"><span class="hljs-tag">&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute bottom-0 left-0 right-0 lg:opacity-0 group-hover:opacity-100 bg-primary p-4 duration-300 text-primary-content"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-bold lg:text-xl"</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm lg:text-xl"</span>&gt;</span>
        </span><span class="javascript">{description.slice(<span class="hljs-number">0</span>, <span class="hljs-number">80</span>)}</span><span class="xml">...
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>On the containing div we're adding a <code>relative</code> position then using the Tailwind <code>group</code> class to apply the <code>group-hover</code> on the div containing the description content.</p>
<p>Because the containing div has a <code>relative</code> position on it we can then <code>absolute</code>ly position the description div at the bottom of the containing div with <code>bottom-0</code>, <code>left-0</code> and <code>right-0</code> so it spans the bottom of the containing div.</p>
<p>The <code>lg:</code> class is so that when the user is on a smaller screen, the div shows regardless of mouse hover.  </p>
<p>Let's commit that to Git an move onto the next section:</p>
<pre><code class="lang-git">git add .
git commit -m "Style Projects component"
</code></pre>
<h2 id="heading-how-to-use-the-sveltekit-layout-file">How to Use the SvelteKit <code>__layout</code> File</h2>
<p>For global styles we can use the special SvelteKit <code>__layout.svelte</code> file. We can use it to control the global styles and also to get external information you want to pass down to any pages or components used in the project. </p>
<p>For now let's add some container classes for responsive screen sizes:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> <span class="hljs-string">'../app.css'</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container max-w-3xl mx-auto px-4 mb-20"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
</code></pre>
<p>Commit that to Git, then onto the next section:</p>
<pre><code class="lang-git">git add .
git commit -m "Add layout container CSS classes"
</code></pre>
<h2 id="heading-how-to-build-the-landing-page-with-projects-listed"><strong>How to Build the Landing Page with Projects Listed</strong></h2>
<p>Let's get started with the landing page. On the landing page we're going to want to display some information about the Author and the Projects.</p>
<p>We already have the projects query defined and in use on the <code>src/routes/index.svelte</code> page. We're also going to want to get data from the <code>author</code> model for use in the index page.</p>
<p>What we're going to need to do is create another GraphQL query for the author in the load function of the <code>src/routes/index.svelte</code> page. Let's hop on over to the GraphCMS GraphQL playground and define that now:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> GetAuthors {
  authors {
    name
    intro
    bio
    slug
    picture {
      url
    }
  }
}
</code></pre>
<p>Ok, so, we have a projects query and an authors query. Onto getting data with these two queries now!</p>
<p>To achieve this we're going to use the JavaScript <code>[Promise.all](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)</code> method to get the data from both endpoints and return them for use in the project.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> ProjectCard <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/project-card.svelte'</span>
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> authorsQuery = gql<span class="hljs-string">`
      query GetAuthors {
        authors {
          name
          intro
          bio
          slug
          picture {
            url
          }
        }
      }
    `</span>
    <span class="hljs-keyword">const</span> projectsQuery = gql<span class="hljs-string">`
      query GetProjects {
        projects {
          name
          slug
          description
          tags
          demo
          sourceCode
          image {
            url
          }
        }
      }
    `</span>
    <span class="hljs-keyword">const</span> [authorReq, projectsReq] = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all([
      client.request(authorsQuery),
      client.request(projectsQuery),
    ])
    <span class="hljs-keyword">const</span> { authors } = authorReq
    <span class="hljs-keyword">const</span> { projects } = projectsReq

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        projects,
        authors,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> projects
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> authors
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-bold text-center mb-20 text-5xl"</span>&gt;</span>
  Welcome to my Portfolio
<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

</span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> authors <span class="hljs-keyword">as</span> { name, intro, <span class="hljs-attr">picture</span>: { url } }</span><span class="xml">}
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex mb-40 items-end"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mr-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl mb-4 font-bold tracking-wider"</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xl mb-4"</span>&gt;</span></span><span class="javascript">{intro}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mask mask-squircle h-48"</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"grid gap-10 md:grid-cols-4 md:px-10 lg:grid-cols-6 lg:-mx-52"</span>
&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> projects <span class="hljs-keyword">as</span> { name, slug, description, image }}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">ProjectCard</span> </span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{description}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">url</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{slug}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Wow! There's a lot in here now.</p>
<p>Those two GraphQL queries are really taking up a lot of space in that load function. Let's take a minute to refactor them out of here so they can be used elsewhere. It'll also help clean up this page as it's getting a bit busy now with GraphQL queries taking up most of the file.</p>
<h3 id="heading-how-to-refactor-the-graphql-queries">How to Refactor the GraphQL Queries</h3>
<p>Let's grab those two queries at the top of the file there, these two:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> authorsQuery = gql<span class="hljs-string">`
  query GetAuthors {
    authors {
      name
      intro
      bio
      slug
      picture {
        url
      }
    }
  }
`</span>
<span class="hljs-keyword">const</span> projectsQuery = gql<span class="hljs-string">`
  query GetProjects {
    projects {
      name
      slug
      description
      tags
      demo
      sourceCode
      image {
        url
      }
    }
  }
`</span>
</code></pre>
<p>And add them to their own JavaScript file. Let's create that now:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># create the graphql-queries.js file</span>
touch src/lib/graphql-queries.js
</code></pre>
<p>Then we can take the queries from the <code>src/routes/index.svelte</code> file and add them in there:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authorsQuery = gql<span class="hljs-string">`
  query GetAuthors {
    authors {
      name
      intro
      bio
      slug
      picture {
        url
      }
    }
  }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> projectsQuery = gql<span class="hljs-string">`
  query GetProjects {
    projects {
      name
      slug
      description
      tags
      demo
      sourceCode
      image {
        url
      }
    }
  }
`</span>
</code></pre>
<p>Note that they now have <code>export</code> in front of the <code>const</code>. This is so they can be exported from this file for use in the <code>src/routes.index.svelte</code> file.</p>
<p>In the <code>src/routes.index.svelte</code> I can now import those queries, cleaning up the file a little by removing all the noise of the queries in the load function. Here's what it should look like now:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> ProjectCard <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/project-card.svelte'</span>
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { authorsQuery, projectsQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-queries'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> [authorReq, projectsReq] = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all([
      client.request(authorsQuery),
      client.request(projectsQuery),
    ])
    <span class="hljs-keyword">const</span> { authors } = authorReq
    <span class="hljs-keyword">const</span> { projects } = projectsReq

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        projects,
        authors,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> projects
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> authors
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">svelte:head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Portfolio project<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svelte:head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-bold text-center mb-20 text-5xl"</span>&gt;</span>
  Welcome to my Portfolio
<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

</span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> authors <span class="hljs-keyword">as</span> { name, intro, <span class="hljs-attr">picture</span>: { url } }</span><span class="xml">}
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex mb-40 items-end"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mr-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl mb-4 font-bold tracking-wider"</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xl mb-4"</span>&gt;</span></span><span class="javascript">{intro}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mask mask-squircle h-48"</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"grid gap-10 md:grid-cols-4 md:px-10 lg:grid-cols-6 lg:-mx-52"</span>
&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> projects <span class="hljs-keyword">as</span> { name, slug, description, image }}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">ProjectCard</span> </span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{description}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">url</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag"> </span></span><span class="javascript">{slug}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Whoa! What's this <code>&lt;svelte:head&gt;</code> doing here? </p>
<p>The <a target="_blank" href="https://svelte.dev/docs#template-syntax-svelte-head">Svelte Head API</a> allows us to add HTML meta data to the project – so, tags like the page title like in the above example but also meta tags for Google, Facebook, and Twitter. Also <a target="_blank" href="https://webmonetization.org/">monetization</a>.</p>
<p>This implementation will give the browser tab a title of "My Portfolio project".</p>
<p>Apart from the head component being added in here, we're also using the data from the <code>authors</code> query to display the data from the authors model on GraphCMS. </p>
<p>Commit the changes to Git:</p>
<pre><code class="lang-git">git add .
git commit -m "Landing page with projects listed"
</code></pre>
<p>Ok, nice – we've got our landing page sorted.</p>
<h2 id="heading-how-to-use-sveltekit-routing">How to Use SvelteKit Routing</h2>
<p>Now we have a nice landing page with links to projects. But clicking on a link will take us to a 404 page. That's because the route for that page doesn't exist yet.</p>
<p>Let's create that now. We'll be using SvelteKit <a target="_blank" href="https://kit.svelte.dev/docs#routing-pages">file-based routing</a> to do this.</p>
<p>We'll need to create a file which will take the <code>slug</code> from the projects card and use that for the path for the project. We can make the file first:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># make the projects folder</span>
mkdir src/routes/projects
<span class="hljs-comment"># create the [slug].svelte file</span>
touch src/routes/projects/<span class="hljs-string">'[slug]'</span>.svelte
</code></pre>
<p>In the <code>src/routes/projects/[slug].svelte</code> we can define a SvelteKit load function which receives a context variable. Let's first take a look at what we get in the context variable:</p>
<pre><code class="lang-js">&lt;script context=<span class="hljs-string">"module"</span>&gt;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> context =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'====================='</span>)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'context'</span>, context)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'====================='</span>)
    <span class="hljs-keyword">return</span> {}
  }
&lt;/script&gt;
</code></pre>
<p>Refreshing the route for <a target="_blank" href="http://localhost:3000/projects/survey-form"><code>localhost:3000/projects/survey-form</code></a> will give output in the terminal like this:</p>
<pre><code class="lang-text">=====================
context {
  url: URL {
    href: 'http://localhost:3000/projects/survey-form',
    origin: 'http://localhost:3000',
    protocol: 'http:',
    username: '',
    password: '',
    host: 'localhost:3000',
    hostname: 'localhost',
    port: '3000',
    pathname: '/projects/survey-form',
    search: '',
    searchParams: URLSearchParams {},
    hash: ''
  },
  params: { slug: 'survey-form' },
  props: {},
  session: [Getter],
  fetch: [AsyncFunction: fetch],
  stuff: {}
}
=====================
</code></pre>
<p>What we're interested in here is the <code>params.slug</code> property which we can use to make a query to the GraphQL API.</p>
<p>Let's hop on over to the GraphQL playground in our GraphCMS project. There, we'll make a query to filter on a project where the <code>slug</code> matches what's being returned from the SvelteKit load function here:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-42.png" alt="GraphQL query to query for project where slug matches &quot;survey-form&quot;" width="600" height="400" loading="lazy">
<em>GraphQL query to query for project where slug matches "survey-form"</em></p>
<p>In the image, here I've defined a query to filter on the <code>slug</code> field where <code>"survey-form"</code> is being passed to the query.</p>
<p>That's great for that one query, but we want a way to pass variables to the query for each individual project slug we have. Let's take a look at using variables in GraphQL now.</p>
<p>I'll add some parenthesis to the end of the query name, and in those parenthesis I'll define a variable <code>query GetProject($slug: String!) {</code>. The <code>$</code> denotes it's a variable while the <code>: String!</code> denotes the data type of the variable.</p>
<p>Because GraphQL is strongly typed, this needs to be defined so that GraphQL knows how it can use the variable. The exclamation point <code>!</code> at the end indicates that the variable is required for the query to work.</p>
<p>Now I can use the variable in place of the hardcoded <code>"survey-form"</code> I used previously:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> GetProject(<span class="hljs-variable">$slug</span>: String!) {
  project(<span class="hljs-symbol">where:</span> {<span class="hljs-symbol">slug:</span> <span class="hljs-variable">$slug</span>}) {
    name
    description
    tags
    demo
    sourceCode
    image {
      url
    }
  }
}
</code></pre>
<p>If I try run that query now I get the following error:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"errors"</span>: [
    {
      <span class="hljs-attr">"message"</span>: <span class="hljs-string">"variable 'slug' must be defined"</span>
    }
  ],
  <span class="hljs-attr">"data"</span>: <span class="hljs-literal">null</span>,
}
</code></pre>
<p>So to get this running in the GraphQL playground here, I can use the "QUERY VARIABLES" panel you may have noticed in the last image. Clicking on that will open the panel and I can add in the variable value there:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-43.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now with the slug variable defined in the query panel I'm able to run the query.</p>
<p>Ok that's great! How do I use that in the project?</p>
<p>Great question! I want a way to pass that query variable to the GraphQL client with the query. </p>
<p>We can do this much the same way we did it for the index page. This is the same repeating pattern now – and this time we're going to accept the <code>slug</code> variable to use in the query I've defined.</p>
<p>Before we get to that, let's add that project query to the <code>src/lib/graphql-queries.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authorsQuery = gql<span class="hljs-string">`
  query GetAuthors {
    authors {
      name
      intro
      bio
      slug
      picture {
        url
      }
    }
  }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> projectsQuery = gql<span class="hljs-string">`
  query GetProjects {
    projects {
      name
      slug
      description
      tags
      demo
      sourceCode
      image {
        url
      }
    }
  }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> projectQuery = gql<span class="hljs-string">`
  query GetProject($slug: String!) {
    project(where: { slug: $slug }) {
      name
      slug
      description
      tags
      demo
      sourceCode
      image {
        url
      }
    }
  }
`</span>
</code></pre>
<p> So, there's a bit of repetition going on in this file, now, with the <code>name</code>, <code>slug</code>, <code>description</code>, <code>tags</code>, <code>demo</code>, <code>sourceCode</code> and <code>image.url</code> being repeated in both the <code>Projects</code> and <code>Project</code> queries.</p>
<p>We can use a <a target="_blank" href="https://graphql.org/learn/queries/#fragments">GraphQL fragment</a> here to re-use the fields on the model. Here's what it looks like:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> PROJECT_FRAGMENT = gql<span class="hljs-string">`
  fragment ProjectDetails on Project {
    name
    slug
    description
    tags
    demo
    sourceCode
    image {
      url
    }
  }
`</span>
</code></pre>
<p>All the fields are in one query now, the fragment is named <code>ProjectDetails</code> and that is <code>on</code> the <code>Project</code> model. Now that can be used in the <code>Projects</code> and <code>Project</code> queries by spreading (<code>...</code>) the <code>ProjectDetails</code> into the queries:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authorsQuery = gql<span class="hljs-string">`
  query GetAuthors {
    authors {
      name
      intro
      bio
      slug
      picture {
        url
      }
    }
  }
`</span>

<span class="hljs-keyword">const</span> PROJECT_FRAGMENT = gql<span class="hljs-string">`
  fragment ProjectDetails on Project {
    name
    slug
    description
    tags
    demo
    sourceCode
    image {
      url
    }
  }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> projectsQuery = gql<span class="hljs-string">`
  <span class="hljs-subst">${PROJECT_FRAGMENT}</span>
  query GetProjects {
    projects {
      ...ProjectDetails
    }
  }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> projectQuery = gql<span class="hljs-string">`
  <span class="hljs-subst">${PROJECT_FRAGMENT}</span>
  query GetProject($slug: String!) {
    project(where: { slug: $slug }) {
      ...ProjectDetails
    }
  }
`</span>
</code></pre>
<p>One thing I'm going to need to do now, before we go any further, is use a dependency for the Markdown content of the project description.</p>
<p>This is to take the Markdown content for the project description and turn it into HTML so that it can be presented on the page. I'm going to use <code>marked</code> here:</p>
<pre><code class="lang-bash">npm i -D marked
</code></pre>
<p>Now that the query is defined we can use it in the <code>src/routes/projects/[slug].svelte</code> file:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { projectQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-queries'</span>
  <span class="hljs-keyword">import</span> { marked } <span class="hljs-keyword">from</span> <span class="hljs-string">'marked'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> ({ params }) =&gt; {
    <span class="hljs-keyword">const</span> { slug } = params
    <span class="hljs-keyword">const</span> variables = { slug }
    <span class="hljs-keyword">const</span> { project } = <span class="hljs-keyword">await</span> client.request(projectQuery, variables)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        project,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> project
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">svelte:head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Portfolio | </span><span class="javascript">{project.name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svelte:head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sm:-mx-5 md:-mx-10 lg:-mx-20 xl:-mx-38 mb-5"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"rounded-lg"</span>
    <span class="hljs-attr">src</span>=</span></span><span class="javascript">{project.image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag">
    <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{project.title}</span><span class="xml"><span class="hljs-tag">
  /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-4xl font-semibold mb-5"</span>&gt;</span></span><span class="javascript">{project.name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-5 flex justify-between"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    </span><span class="javascript">{</span><span class="hljs-keyword">#if</span><span class="javascript"> project.tags}</span><span class="xml">
      </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> project.tags <span class="hljs-keyword">as</span> tag}</span><span class="xml">
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"badge badge-primary mr-2 hover:bg-primary-focus cursor-pointer"</span>
          &gt;</span></span><span class="javascript">{tag}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>
        &gt;</span>
      </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
    </span><span class="javascript">{</span><span class="hljs-keyword">/if</span><span class="javascript">}</span><span class="xml">
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-5 prose flex prose-a:text-primary hover:prose-a:text-primary-focus"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mr-5"</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{project.demo}</span><span class="xml"><span class="hljs-tag">&gt;</span>Demo<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{project.sourceCode}</span><span class="xml"><span class="hljs-tag">&gt;</span>Source Code<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prose prose-xl"</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">@html</span><span class="javascript"> marked(project.description)}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span></span>
</code></pre>
<p>In the <code>src/routes/projects/[slug].svelte</code> file we're doing pretty much the same as we did with the <code>src/routes/index.svelte</code> file except we're using <code>params: { slug }</code> to pass the slug value to the GraphQL client to get the data relating to that slug.</p>
<p><code>{@html}</code> is used to display the contents as HTML. Use this with caution if you do not trust the source of the HTML – but in our case we know we can trust the HTML because we put it there! 😊 </p>
<p>Let's get that committed to Git before moving on:</p>
<pre><code class="lang-git">git add .
git commit -m "Add project page using SvelteKit routing"
</code></pre>
<h3 id="heading-how-to-build-the-projects-index-page">How to Build the Projects Index Page</h3>
<p>Now to create an index for the projects. It's much like the landing page, but this time it's only to list out the projects.</p>
<p>I'll create an index for the projects route:</p>
<pre><code class="lang-bash">touch src/routes/projects/index.svelte
</code></pre>
<p>Now navigating to <code>localhost:3000/projects</code> will show that file.</p>
<p>Time to repeat the pattern used to get the projects list on the index page but without the author information:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> ProjectCard <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/project-card.svelte'</span>
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { projectsQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-queries'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> { projects } = <span class="hljs-keyword">await</span> client.request(projectsQuery)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        projects,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> projects
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">svelte:head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Portfolio projects<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svelte:head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-bold mb-20 text-center text-5xl"</span>&gt;</span>
  Recent Projects by Me
<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"grid gap-10 md:grid-cols-4 md:px-10 lg:grid-cols-6 lg:-mx-52"</span>
&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> projects <span class="hljs-keyword">as</span> { name, slug, description, image }, index}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">ProjectCard</span>
      </span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">
      </span></span><span class="javascript">{description}</span><span class="xml"><span class="hljs-tag">
      <span class="hljs-attr">url</span>=</span></span><span class="javascript">{image[<span class="hljs-number">0</span>].url}</span><span class="xml"><span class="hljs-tag">
      </span></span><span class="javascript">{index}</span><span class="xml"><span class="hljs-tag">
      </span></span><span class="javascript">{slug}</span><span class="xml"><span class="hljs-tag">
    /&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Nice! Now navigating to <code>localhost:3000/projects</code> gives us a dedicated projects page.</p>
<p>Let's move on to repeating these patterns we've learned for the blog index page and the individual blog posts.</p>
<p>Commit to Git the current changes before moving on:</p>
<pre><code class="lang-git">git add .
git commit -m "Add projects index page"
</code></pre>
<h2 id="heading-how-to-build-the-blog"><strong>How to Build the Blog</strong></h2>
<p>Time for the blog now. This is pretty much the same approach as with the projects, but let's go through the process again.</p>
<ol>
<li>Make a GraphQL query to define the data needed.</li>
<li>Give that query to the GraphQL client.</li>
<li>Work with the returned data from the client in the page.</li>
</ol>
<p>Make a GraphQL query for the posts. As we'll be following the same pattern as with the projects (query for all projects and filter for a specific project) we can make a GraphQL fragment for the data we want to get, both on all posts and a single post.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> POST_FRAGMENT = gql<span class="hljs-string">`
  fragment PostDetails on Post {
    title
    slug
    date
    content
    tags
    coverImage {
      url
    }
    authors {
      name
    }
  }
`</span>
</code></pre>
<p>We can then use the same pattern as before where we use the fragment in both a Posts and Post query:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> postsQuery = gql<span class="hljs-string">`
  <span class="hljs-subst">${POST_FRAGMENT}</span>
  query GetPosts {
    posts {
      ...PostDetails
    }
  }
`</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> postQuery = gql<span class="hljs-string">`
  <span class="hljs-subst">${POST_FRAGMENT}</span>
  query GetPost($slug: String!) {
    post(where: { slug: $slug }) {
      ...PostDetails
    }
  }
`</span>
</code></pre>
<p>With the <code>POST_FRAGMENT</code> and <code>postsQuery</code> and <code>postQuery</code> added to the <code>src/lib/graphql-queries.js</code> file we can make a posts route then add in a <code>[slug].svelte</code> file and a <code>index.svelte</code> file.</p>
<pre><code class="lang-bash">mkdir src/routes/posts
touch src/routes/posts/{<span class="hljs-string">'[slug]'</span>.svelte,index.svelte}
</code></pre>
<p>Let's tackle the posts index page first then we can move onto individual posts with the slug file.</p>
<p>The first section we have done a few times now, defining a SvelteKit load function then using the GraphQL client to query for the posts:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { postsQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-queries'</span>
  <span class="hljs-keyword">import</span> { marked } <span class="hljs-keyword">from</span> <span class="hljs-string">'marked'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> { posts } = <span class="hljs-keyword">await</span> client.request(postsQuery)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        posts,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> posts
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">svelte:head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Portfolio | Blog<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svelte:head</span>&gt;</span></span>
</code></pre>
<p>Now we need to add the markup for the page. Using the daisyUI card classes, we can define a pretty decent looking card, then loop through the posts tags and finally link out to the post page.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-4xl mb-10 font-extrabold"</span>&gt;</span>Blog posts<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

</span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> posts <span class="hljs-keyword">as</span> { title, slug, content, coverImage, tags }}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card text-center shadow-2xl mb-20"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">figure</span> <span class="hljs-attr">class</span>=<span class="hljs-string">""</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">""</span>
        <span class="hljs-attr">src</span>=</span></span><span class="javascript">{coverImage.url}</span><span class="xml"><span class="hljs-tag">
        <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{<span class="hljs-string">`Cover image for <span class="hljs-subst">${title}</span>`</span>}</span><span class="xml"><span class="hljs-tag">
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-body prose"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span></span><span class="javascript">{title}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      </span><span class="javascript">{</span><span class="hljs-keyword">@html</span><span class="javascript"> marked(content).slice(<span class="hljs-number">0</span>, <span class="hljs-number">150</span>)}</span><span class="xml">
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex justify-center mt-5 space-x-2"</span>&gt;</span>
        </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> tags <span class="hljs-keyword">as</span> tag}</span><span class="xml">
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"badge badge-primary"</span>&gt;</span></span><span class="javascript">{tag}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"justify-center card-actions"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=</span></span><span class="javascript">{<span class="hljs-string">`/posts/<span class="hljs-subst">${slug}</span>`</span>}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-outline btn-primary"</span>
          &gt;</span>Read <span class="hljs-symbol">&amp;rArr;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
        &gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span>
</code></pre>
<p>Time to repeat that pattern again!</p>
<p>SvelteKit load function using the GraphQL client passing in the post query and variable coming from the page params: </p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { postQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-queries'</span>
  <span class="hljs-keyword">import</span> { marked } <span class="hljs-keyword">from</span> <span class="hljs-string">'marked'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> ({ params }) =&gt; {
    <span class="hljs-keyword">const</span> { slug } = params
    <span class="hljs-keyword">const</span> variables = { slug }
    <span class="hljs-keyword">const</span> { post } = <span class="hljs-keyword">await</span> client.request(postQuery, variables)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        post,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> post

  <span class="hljs-keyword">const</span> { title, date, tags, content, coverImage } = post
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">svelte:head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Blog | </span><span class="javascript">{title}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svelte:head</span>&gt;</span></span>
</code></pre>
<p>Then for the markup on the page, utilising the Tailwind CSS Typography classes here for beautiful markup:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sm:-mx-5 md:-mx-10 lg:-mx-20 xl:-mx-38 mb-5"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"rounded-xl"</span>
    <span class="hljs-attr">src</span>=</span></span><span class="javascript">{coverImage.url}</span><span class="xml"><span class="hljs-tag">
    <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{<span class="hljs-string">`Cover image for <span class="hljs-subst">${title}</span>`</span>}</span><span class="xml"><span class="hljs-tag">
  /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prose prose-xl"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{title}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-secondary text-xs tracking-widest font-semibold"</span>&gt;</span>
  </span><span class="javascript">{<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(date).toDateString()}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-5 flex justify-between"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    </span><span class="javascript">{</span><span class="hljs-keyword">#if</span><span class="javascript"> tags}</span><span class="xml">
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-5 space-x-2"</span>&gt;</span>
        </span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> tags <span class="hljs-keyword">as</span> tag}</span><span class="xml">
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"badge badge-primary"</span>&gt;</span></span><span class="javascript">{tag}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        </span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    </span><span class="javascript">{</span><span class="hljs-keyword">/if</span><span class="javascript">}</span><span class="xml">
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prose prose-lg"</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">@html</span><span class="javascript"> marked(content)}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span></span>
</code></pre>
<p>Commit our changes now:</p>
<pre><code class="lang-git">git add .
git commit -m "Add posts index page and slug page"
</code></pre>
<p>Ok, now we have a lot of pages on the site to look at, but no way to navigate around them yet.</p>
<h2 id="heading-how-to-build-the-navbar-and-footer-components">How to Build the Navbar and Footer Components</h2>
<p>I'm going to grab some pre-made components now from daisyUI for the <a target="_blank" href="https://daisyui.com/components/footer">footer</a> and <a target="_blank" href="https://daisyui.com/components/navbar">navbar</a>. Let's create the files first before hopping over to the daisyUI site to grab them:</p>
<pre><code class="lang-bash">touch src/lib/components/{footer.svelte,navbar.svelte}
</code></pre>
<p> Those curly braces in that command create both the files for us.</p>
<h3 id="heading-how-to-make-the-footer-component">How to Make the Footer Component</h3>
<p>First up, we can do the footer component. I'll be using the second of the <code>footer footer-center</code> components on the daisyUI components footer section. This is what it looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-49.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And here's the markup for that component:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-10 footer bg-base-200 text-base-content footer-center"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-flow-col gap-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span>&gt;</span>About us<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> 
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> 
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span>&gt;</span>Jobs<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> 
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span>&gt;</span>Press kit<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span> 
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-flow-col gap-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fill-current"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> 
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fill-current"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> 
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fill-current"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span> 
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Copyright © 2021 - All right reserved by ACME Industries Ltd<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span></span>
</code></pre>
<p>One thing to note here: if you don't like the SVGs directly in the HTML here, they can be abstracted out into their own components and imported into the footer file. Because Svelte is a superset of HTML, this makes breaking up large files into manageable components possible.</p>
<p>Let's do that now to reduce the file size and make it easier to parse. So, first I'll create the icon files:</p>
<pre><code class="lang-bash">touch src/lib/components/{twitter-icon.svelte,you-tube-icon.svelte,facebook-icon.svelte}
</code></pre>
<p>Now I can remove the <code>&lt;svg&gt;</code> tags from the footer component, and add them to their respective files.</p>
<p>Here's what the Twitter one looks like. You can repeat this for the remaining components:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">svg</span>
  <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span>
  <span class="hljs-attr">width</span>=<span class="hljs-string">"24"</span>
  <span class="hljs-attr">height</span>=<span class="hljs-string">"24"</span>
  <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"fill-current"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
    <span class="hljs-attr">d</span>=<span class="hljs-string">"M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"</span>
  /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span></span>
</code></pre>
<p>There's a few bit's with this we want to change before we use it in our project here.</p>
<p>In the footer element, change the background from <code>bg-base-200</code> to <code>bg-primary</code> and change <code>text-base-content</code> to <code>text-primary-content</code>.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"p-10 footer bg-primary text-primary-content footer-center"</span>
&gt;</span></span>
</code></pre>
<p>Then there's the links to be added in the next section:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-flow-col gap-4"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>&gt;</span>Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>You can add in the hard-links to the social providers for now. Although they are available in the Social model.</p>
<p>For the copyright section at the end of the file, I'll add in some JavaScript to get the current year so there's no need to worry about updating this again.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
  Copyright <span class="hljs-symbol">&amp;copy;</span> </span><span class="javascript">{<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear()}</span>`</span>}</span><span class="xml"> - All right reserved
  by ME
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<p>Here's the adjusted file now:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> FacebookIcon <span class="hljs-keyword">from</span> <span class="hljs-string">'./facebook-icon.svelte'</span>
  <span class="hljs-keyword">import</span> TwitterIcon <span class="hljs-keyword">from</span> <span class="hljs-string">'./twitter-icon.svelte'</span>
  <span class="hljs-keyword">import</span> YouTubeIcon <span class="hljs-keyword">from</span> <span class="hljs-string">'./you-tube-icon.svelte'</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">footer</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"p-10 footer bg-primary text-primary-content footer-center"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-flow-col gap-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>&gt;</span>Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"link link-hover"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-flow-col gap-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://twitter.com"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">TwitterIcon</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://youtube.com"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">YouTubeIcon</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://facebook.com"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">FacebookIcon</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
      Copyright <span class="hljs-symbol">&amp;copy;</span> </span><span class="javascript">{<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear()}</span>`</span>}</span><span class="xml"> - All right reserved
      by ME
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span></span>
</code></pre>
<p>With the imported SVGs there's a lot of noise removed from the file and it's a lot nicer to read.</p>
<p>Now that we have our footer component we're going to want to have it persisted across route (page) changes. The <code>__layout.svelte</code> file is the perfect place for this so let's go and add it in there:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/footer.svelte'</span>
  <span class="hljs-keyword">import</span> <span class="hljs-string">'../app.css'</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container max-w-3xl mx-auto px-4 mb-20"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span></span>
</code></pre>
<p>Let's commit our footer component to Git then move onto the next section:</p>
<pre><code class="lang-git">git add .
git commit -m "Add footer component"
</code></pre>
<h3 id="heading-how-to-make-the-navbar-component">How to Make the Navbar Component</h3>
<p>Now for the navbar component, I'll be using the second to last of the <code>navbar</code> components on the daisyUI components navbar section. This is what it looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-50.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>There's a lot of SVGs in this example that I'm not going to be using, so I'll remove them. Keep them if you prefer, but for the sake of readability I'll be removing them. There only really needs to be links in there for the Portfolio page, Blog page, and the About page.</p>
<p>Here's what the markup looks like for it with the SVGs removed:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar mb-2 shadow-lg bg-neutral text-neutral-content rounded-box"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-1 px-2 mx-2"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold"</span>&gt;</span>Portfolio and Blog<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-none hidden px-2 mx-2 lg:flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-stretch"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>
        Portfolio
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>
        &gt;</span>Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
      &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>
        &gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
      &gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Note that I have added <code>href</code> tags here to point to the various pages in the project.</p>
<p>We should add that in the same place as the footer in the <code>__layout.svelte</code> file so we can see the changes as we go through building this component:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/footer.svelte'</span>
  <span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/navbar.svelte'</span>
  <span class="hljs-keyword">import</span> <span class="hljs-string">'../app.css'</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container max-w-3xl mx-auto px-4 mb-20"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span></span>
</code></pre>
<p>Some more changes to add to this now: I'll increase <code>mb-2</code> up to <code>mb-16</code>, and also I'll remove <code>rounded-box</code> and replace it with a sticky class so the navbar persists on scrolling through long pages <code>sticky top-0 z-10</code>. </p>
<p>One last thing to do is replace the <code>&lt;span&gt;</code> tag with "Portfolio and Blog" in it to an <code>a</code> tag so that we can navigate back to the home page by clicking there:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>Portfolio and Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
</code></pre>
<p>Here's what the file looks like now:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar mb-16 shadow-lg bg-neutral text-neutral-content sticky top-0 z-10"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-1 px-2 mx-2"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>Portfolio and Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-none hidden px-2 mx-2 lg:flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-stretch"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>
        Portfolio
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>
        &gt;</span>Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
      &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>
        &gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
      &gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Very nice! But wait – what about smaller screen sizes? You may have noticed that if you're on a smaller screen, the links for Portfolio, Blog, and About are missing.</p>
<p>In the class for the containing div on the links <code>flex-none hidden px-2 mx-2 lg:flex</code>, this is going to hide the elements until the screen size gets to the large breakpoint (<code>lg:</code>), then the display will be set to <code>flex</code>.</p>
<p>Let's use some additional daisyUI classes from the dropdown section to show when the screen size is below <code>lg:</code> </p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown dropdown-left lg:hidden"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">tabindex</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m-1 btn"</span>&gt;</span>Links<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>
    <span class="hljs-attr">tabindex</span>=<span class="hljs-string">"0"</span>
    <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-neutral rounded-box shadow text-neutral-content p-2 w-52 menu dropdown-content "</span>
  &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>
      Portfolio
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>&gt;</span>
      Blog
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>
      About
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>So when the screen size is below the <code>lg:</code> Tailwind breakpoint the above <code>dropdown</code>, classes will be shown.</p>
<p>Here's what the full file looks like:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar mb-16 shadow-lg bg-neutral text-neutral-content sticky top-0 z-10"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-1 px-2 mx-2"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>Portfolio and Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown dropdown-left lg:hidden"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">tabindex</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m-1 btn"</span>&gt;</span>Links<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>
      <span class="hljs-attr">tabindex</span>=<span class="hljs-string">"0"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-neutral rounded-box shadow text-neutral-content p-2 w-52 menu dropdown-content "</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>
        Portfolio
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>&gt;</span>
        Blog
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>
        About
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-none hidden px-2 mx-2 lg:flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-stretch"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>
        Portfolio
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>
        &gt;</span>Blog<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
      &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>
        &gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
      &gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Sweet! We've now got a nice responsive nav menu for mobile users.</p>
<p>Time to commit the changes we've made to Git:</p>
<pre><code class="lang-git">git add .
git commit -m "Add navbar component"
</code></pre>
<p>Footer and navbar sorted, now let's move onto the theme switch.</p>
<h2 id="heading-how-to-add-a-theme-switch"><strong>How to Add a Theme Switch</strong></h2>
<p>All modern sites have a theme switch, so let's take a look at implementing that on our site. Saadeghi (the creator of daisyUI) has made the really nice package to take care of this for us called <a target="_blank" href="https://github.com/saadeghi/theme-change"><code>theme-change</code></a> so we should install that now:</p>
<pre><code class="lang-bash">npm i -D theme-change
</code></pre>
<p>Now we can use that in the <code>__layout.svelte</code> file like this:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/footer.svelte'</span>
  <span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/components/navbar.svelte'</span>
  <span class="hljs-keyword">import</span> { onMount } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte'</span>
  <span class="hljs-keyword">import</span> { themeChange } <span class="hljs-keyword">from</span> <span class="hljs-string">'theme-change'</span>
  <span class="hljs-keyword">import</span> <span class="hljs-string">'../app.css'</span>

  onMount(<span class="hljs-keyword">async</span> () =&gt; {
    themeChange(<span class="hljs-literal">false</span>)
  })
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container max-w-3xl mx-auto px-4 mb-20"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span></span>
</code></pre>
<p>So, let's break this down and see what's happening here. The <code>onMount</code> is code run once the page is visible in the browser (once it's loaded/mounted). Once the page has loaded then we're initialising <code>themeChange</code>. This will change the <code>data-act-class</code> for the desired theme.</p>
<p>Currently there's no way to set it, so let's change that now on the <code>src/app.html</code> file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span> <span class="hljs-attr">data-theme</span>=<span class="hljs-string">"dracula"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">""</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.png"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span>
      <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span>
    /&gt;</span>
    %svelte.head%
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"svelte"</span>&gt;</span>%svelte.body%<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Here we're adding the default theme of Dracula on the containing <code>html</code> for the whole project with <code>data-theme="dracula"</code>. You can play about with this with all the provided themes from daisyUI – try changing <code>dracula</code> to <code>corporate</code> and see it change!</p>
<p>Ok, that's nice, but how do I change it? Right – let's do that now. Rather than fill up the post with more code I'm going to link to a GitHub repository that has it already packaged up for us in <a target="_blank" href="https://github.com/spences10/sveltekit-theme-switch/blob/main/src/lib/theme-select.svelte">SvelteKit theme switch</a>. That component is an HTML select element that has all the daisyUI themes listed in it.</p>
<p>Copy the contents of that file and add it to a <code>theme-select.svelte</code> component, which doesn't exist yet – so, let's create that now:</p>
<pre><code class="lang-bash">touch src/lib/components/theme-select.svelte
</code></pre>
<p>Remove the <code>class="mb-8"</code> from the containing div and add in some additional styles to the select element. Here's the diff:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/image-55.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now that we have a theme select component, we should add it somewhere accessible throughout the project. Where do you think that should go? You guessed it – the <code>navbar.svelte</code> file:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> ThemeSelect <span class="hljs-keyword">from</span> <span class="hljs-string">'./theme-select.svelte'</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar mb-16 shadow-lg bg-neutral text-neutral-content sticky top-0 z-10"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-1 px-2 mx-2"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-lg font-bold"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span> Portfolio and Blog <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"dropdown dropdown-left lg:hidden"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">tabindex</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m-1 btn"</span>&gt;</span>Links<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>
      <span class="hljs-attr">tabindex</span>=<span class="hljs-string">"0"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-neutral rounded-box shadow text-neutral-content p-2 w-52 menu dropdown-content "</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>
        Portfolio
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>&gt;</span>
        Blog
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>
        About
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex-none hidden px-2 mx-2 lg:flex"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex items-stretch"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/projects"</span>&gt;</span>
        Portfolio
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/posts"</span>&gt;</span>
        Blog
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-ghost btn-sm rounded-btn"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>
        About
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"px-4"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ThemeSelect</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>So here, we're importing the <code>ThemeSelect</code> component and adding in a containing div for the theme select at the end of our pages list.</p>
<p>You can also add it so it is available on mobile view if you so choose.</p>
<p>Commit the changes to Git:</p>
<pre><code class="lang-git">git add .
git commit -m "Add theme select to navbar"
</code></pre>
<h2 id="heading-how-to-add-the-about-page">How to Add the About Page</h2>
<p>Let's add in that about page we're linking to in the navbar.</p>
<pre><code class="lang-bash">touch src/routes/about.svelte
</code></pre>
<p>In this page we can use the <code>authorsQuery</code> we created for the home page to display the author information. Here's the full file:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">context</span>=<span class="hljs-string">"module"</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
  <span class="hljs-keyword">import</span> { authorsQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-queries'</span>
  <span class="hljs-keyword">import</span> { marked } <span class="hljs-keyword">from</span> <span class="hljs-string">'marked'</span>

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> { authors } = <span class="hljs-keyword">await</span> client.request(authorsQuery)

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">props</span>: {
        authors,
      },
    }
  }
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> authors
  <span class="hljs-keyword">const</span> {
    name,
    intro,
    bio,
    <span class="hljs-attr">picture</span>: { url },
  } = authors[<span class="hljs-number">0</span>]
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">svelte:head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Portfolio project | About </span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">svelte:head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-bold text-center mb-20 text-5xl"</span>&gt;</span>About Me<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex mb-40 items-end"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mr-6"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl mb-4 font-bold tracking-wider"</span>&gt;</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xl mb-4"</span>&gt;</span></span><span class="javascript">{intro}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mask mask-squircle h-48"</span> <span class="hljs-attr">src</span>=</span></span><span class="javascript">{url}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">alt</span>=</span></span><span class="javascript">{name}</span><span class="xml"><span class="hljs-tag"> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">article</span> <span class="hljs-attr">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"prose prose-lg"</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">@html</span><span class="javascript"> marked(bio)}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span></span>
</code></pre>
<p>Now to commit those changes to Git:</p>
<pre><code class="lang-git">git add .
git commit -m "Add about page"
</code></pre>
<h2 id="heading-how-to-make-a-sitemap">How to Make a Sitemap</h2>
<p><strong>OPTIONAL</strong>: Let search engines know what's on your site. A sitemap will help web crawlers know the contents of your site.</p>
<p>I have made an <a target="_blank" href="https://scottspence.com/posts/make-a-sitemap-with-sveltekit">extensive post</a> on how to create a sitemap with SvelteKit if you want to take a look at that for more detail.</p>
<p>This is a SvelteKit endpoint that will return an XML file detailing the contents of the site.</p>
<p>If you want to create one, then make a file for it:</p>
<pre><code class="lang-bash">touch src/routes/sitemap.xml.js
</code></pre>
<p>Here's the full file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
<span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

<span class="hljs-keyword">const</span> website = <span class="hljs-string">'https://www.myporfolioproject.com'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> get = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> query = gql<span class="hljs-string">`
    query Posts {
      posts {
        title
        slug
      }
    }
  `</span>
  <span class="hljs-keyword">const</span> { posts } = <span class="hljs-keyword">await</span> client.request(query)
  <span class="hljs-keyword">const</span> pages = [<span class="hljs-string">`about`</span>]
  <span class="hljs-keyword">const</span> body = sitemap(posts, pages)

  <span class="hljs-keyword">const</span> headers = {
    <span class="hljs-string">'Cache-Control'</span>: <span class="hljs-string">'max-age=0, s-maxage=3600'</span>,
    <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/xml'</span>,
  }
  <span class="hljs-keyword">return</span> {
    headers,
    body,
  }
}

<span class="hljs-keyword">const</span> sitemap = <span class="hljs-function">(<span class="hljs-params">
  posts,
  pages
</span>) =&gt;</span> <span class="hljs-string">`&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;urlset
  xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
  xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
  xmlns:xhtml="https://www.w3.org/1999/xhtml"
  xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
  xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
  xmlns:video="https://www.google.com/schemas/sitemap-video/1.1"
&gt;
  &lt;url&gt;
    &lt;loc&gt;<span class="hljs-subst">${website}</span>&lt;/loc&gt;
    &lt;changefreq&gt;daily&lt;/changefreq&gt;
    &lt;priority&gt;0.7&lt;/priority&gt;
  &lt;/url&gt;
  <span class="hljs-subst">${pages
    .map(
      page =&gt; <span class="hljs-string">`
  &lt;url&gt;
    &lt;loc&gt;<span class="hljs-subst">${website}</span>/<span class="hljs-subst">${page}</span>&lt;/loc&gt;
    &lt;changefreq&gt;daily&lt;/changefreq&gt;
    &lt;priority&gt;0.7&lt;/priority&gt;
  &lt;/url&gt;
  `</span>
    )
    .join(<span class="hljs-string">''</span>)}</span>
  <span class="hljs-subst">${posts
    .map(
      post =&gt; <span class="hljs-string">`
  &lt;url&gt;
    &lt;loc&gt;<span class="hljs-subst">${website}</span>/posts/<span class="hljs-subst">${post.slug}</span>&lt;/loc&gt;
    &lt;changefreq&gt;daily&lt;/changefreq&gt;
    &lt;priority&gt;0.7&lt;/priority&gt;
  &lt;/url&gt;
  `</span>
    )
    .join(<span class="hljs-string">''</span>)}</span>
&lt;/urlset&gt;`</span>
</code></pre>
<p>This is a simplified version to generate a sitemap. If you want the full version then you can check out the <a target="_blank" href="https://github.com/GraphCMS/graphcms-sveltekit-portfolio-and-blog-starter">source code for the project</a>.</p>
<p>A little more detail on this file now. Similar to how we can have pages and components in SvelteKit, we can also have endpoints. Endpoints in SvelteKit can handle HTTP methods like get, post, and delete.</p>
<p>A quick note on the file notation here: the <code>.xml.js</code> may look a bit odd. This is so that SvelteKit can understand the return type of the endpoint. In this case we want to return XML, but there are other types you can use, like JSON.</p>
<p>In that function we're defining a <code>get</code> function, adding a GraphQL query for the posts, then returning the posts from the query for use in the XML. </p>
<h3 id="heading-how-to-use-a-sveltekit-endpoint">How to Use a SvelteKit Endpoint</h3>
<p>Now that we've defined our endpoint in <code>src/routes/sitemap.xml.js</code> we can access the data right away. By going to that route in the browser we can see the data returned from that endpoint.</p>
<p>From the browser, go to <code>localhost:3000/sitemap.xml</code> – this will give us the data back from the GraphQL API on our GraphCMS project.</p>
<h2 id="heading-robotstxt"><strong>Robots.txt</strong></h2>
<p><strong>OPTIONAL</strong>: Let search engine robots know what to index. This tells web crawlers like the Googlebot what to and what not to index on your site.</p>
<p>Pages you might not want to index could be things like an admin panel or settings page.</p>
<p>The robots.txt can go in the static folder. Let's create the file now:</p>
<pre><code class="lang-bash">touch static/robots.txt
</code></pre>
<p>In the case of this project, it's ok for the Googlebot to crawl it all. So our <code>robots.txt</code> file can look like this:</p>
<pre><code class="lang-txt"># https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
</code></pre>
<p>This is saying to the web crawler to index everything on the site.</p>
<h2 id="heading-rss-feed-generation"><strong>RSS Feed Generation</strong></h2>
<p><strong>OPTIONAL</strong>: Let users have changes made to your site show up in their RSS apps. Again I'll leave this up to you to implement. Much in the same way as the sitemap was created, you can implement a SvelteKit endpoint to generate the XML needed for an RSS feed.</p>
<pre><code class="lang-bash">touch src/routes/rss.xml.js
</code></pre>
<p>Here's an example file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { client } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/graphql-client'</span>
<span class="hljs-keyword">import</span> { gql } <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-request'</span>

<span class="hljs-keyword">const</span> name = <span class="hljs-string">'My Portfolio'</span>
<span class="hljs-keyword">const</span> website = <span class="hljs-string">'https://myportfolio.com'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> get = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> query = gql<span class="hljs-string">`
    query Posts {
      posts {
        title
        slug
      }
    }
  `</span>
  <span class="hljs-keyword">const</span> { posts } = <span class="hljs-keyword">await</span> client.request(query)
  <span class="hljs-keyword">const</span> body = xml(posts)

  <span class="hljs-keyword">const</span> headers = {
    <span class="hljs-string">'Cache-Control'</span>: <span class="hljs-string">'max-age=0, s-maxage=3600'</span>,
    <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/xml'</span>,
  }
  <span class="hljs-keyword">return</span> {
    headers,
    body,
  }
}

<span class="hljs-keyword">const</span> xml =
  <span class="hljs-function"><span class="hljs-params">posts</span> =&gt;</span> <span class="hljs-string">`&lt;rss xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:atom="https://www.w3.org/2005/Atom" version="2.0"&gt;
  &lt;channel&gt;
    &lt;title&gt;<span class="hljs-subst">${name}</span>&lt;/title&gt;
    &lt;link&gt;<span class="hljs-subst">${website}</span>&lt;/link&gt;
    &lt;description&gt;This is my portfolio!&lt;/description&gt;
    <span class="hljs-subst">${posts
      .map(
        post =&gt;
          <span class="hljs-string">`
        &lt;item&gt;
          &lt;title&gt;<span class="hljs-subst">${post.title}</span>&lt;/title&gt;
          &lt;description&gt;This is my portfolio!&lt;/description&gt;
          &lt;link&gt;<span class="hljs-subst">${website}</span>/posts/<span class="hljs-subst">${post.slug}</span>/&lt;/link&gt;
          &lt;pubDate&gt;<span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(post.date)}</span>&lt;/pubDate&gt;
          &lt;content:encoded&gt;<span class="hljs-subst">${post.previewHtml}</span> 
            &lt;div style="margin-top: 50px; font-style: italic;"&gt;
              &lt;strong&gt;
                &lt;a href="<span class="hljs-subst">${website}</span>/posts/<span class="hljs-subst">${post.slug}</span>"&gt;
                  Keep reading
                &lt;/a&gt;
              &lt;/strong&gt;  
            &lt;/div&gt;
          &lt;/content:encoded&gt;
        &lt;/item&gt;
      `</span>
      )
      .join(<span class="hljs-string">''</span>)}</span>
  &lt;/channel&gt;
&lt;/rss&gt;`</span>
</code></pre>
<p>There's a lot to unpack in that, so what I have done is made an extensive post on <a target="_blank" href="https://scottspence.com/posts/make-an-rss-feed-with-sveltekit">setting up an RSS feed on your SvelteKit site</a>. This will give you all the information you need to get set up.</p>
<h2 id="heading-email-signup-with-revue"><strong>Email Signup with Revue</strong></h2>
<p><strong>OPTIONAL</strong>: If you want to take this a step further with endpoints, you can add a newsletter sign up page using the Revue API. I've <a target="_blank" href="https://scottspence.com/posts/email-form-submission-with-sveltekit">detailed that in a post</a> if you want to take that route.</p>
<p>There's also a great <a target="_blank" href="https://www.youtube.com/watch?v=mBXEnakkUIM">video from WebJeda</a> on collecting Google forms data in a SvelteKit project if you want to take that route.</p>
<h2 id="heading-continuous-deployment-with-vercel"><strong>Continuous Deployment with Vercel</strong></h2>
<p>If you have been following along up to this point (thank you by the way 🙏), you might be wondering why we've been making Git commits at the end of each section. Well all that has been leading up to this section.</p>
<p>I'll be using <a target="_blank" href="https://vercel.com">Vercel</a> for deployment. If you don't already have an account you can <a target="_blank" href="https://vercel.com/signup">sign up</a> with your preferred provider – I'll be using GitHub.</p>
<p>If you want to deploy your site as it is, right now, you can use the Vercel CLI using:</p>
<pre><code class="lang-bash">npx vercel
</code></pre>
<p>No need to install the CLI, as it's all done for you with the npx command. You'll be walked through the deployment by the CLI. </p>
<p>Here's the output from running the command and selecting the default for each question (enter):</p>
<pre><code class="lang-bash">? Set up and deploy “~/repos/my-developer-portfolio”? [Y/n] y
? Which scope <span class="hljs-keyword">do</span> you want to deploy to? Scott Spence
? Link to existing project? [y/N] n
? What’s your project’s name? my-developer-portfolio
? In <span class="hljs-built_in">which</span> directory is your code located? ./
Auto-detected Project Settings (SvelteKit):
- Build Command: svelte-kit build
- Output Directory: public
- Development Command: svelte-kit dev --port <span class="hljs-variable">$PORT</span>
? Want to override the settings? [y/N] n
🔗 Linked to spences10/my-developer-portfolio (created .vercel and added it to .gitignore)
🔍 Inspect: https://vercel.com/spences10/my-developer-portfolio/78bRRjiweZsipYbu8Q4Bg9JRmvGR [2s]
</code></pre>
<p>Now going to the URL from the CLI indicated with <code>🔍 Inspect</code> I can watch the project being built on Vercel. Great, our site is up and running! This is a one time deployment though, so if there are future changes then I'll need to use the CLI again.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-10.png" alt="Image" width="600" height="400" loading="lazy">
<em>Vercel deployment preview page</em></p>
<p>You may have noticed on the deploy preview page on Vercel that there is a section that says "No repository".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-11.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We can add a GitHub repository so that any future changes that are made to the project will be built when the changes are pushed to GitHub.</p>
<p>So, first up we need to add our project to GitHub – let's do that now. If you're already logged into GitHub you can go to the <a target="_blank" href="https://github.com/new">new repository link</a> that you can get to by clicking the plus icon in the far right corner in GitHub.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-12.png" alt="Image" width="600" height="400" loading="lazy">
<em>New repository link</em></p>
<p>In the new page add in the details for the project:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-13.png" alt="Image" width="600" height="400" loading="lazy">
<em>New GitHub project with description</em></p>
<p>Then click the "Create repository" button:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-14.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The next screen will give you the Git commands you need. As the project is already created, I can use the second set of commands:</p>
<pre><code class="lang-bash">git remote add origin git@github.com:spences10/my-developer-portfolio.git
git branch -M main
git push -u origin main
</code></pre>
<p>Note that if you're following along, you'll need to take the commands given to you on your repository page rather than using the ones mentioned here, as that will point to my GitHub <code>spences10</code>.</p>
<p>Now that the repo is created on GitHub, I can connect it to Vercel. From the deploy preview page I can select the project by clicking the project name in the header:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-16.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This will take me to the project dashboard:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-17.png" alt="Image" width="600" height="400" loading="lazy">
<em>Vercel project dashboard</em></p>
<p>From here I can click on the "Connect Git repository" button which will take me to the Git section in the project settings:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-18.png" alt="Image" width="600" height="400" loading="lazy">
<em>Connect a Git repository in the Vercel settings menu</em></p>
<p>Clicking GitHub will bring up a list of projects:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-19.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select the GitHub repository to link</em></p>
<p>Clicking the "Connect" button will connect the repository. You may have also noticed the "Domains" setting here as well. You can configure your domain here or change the current name with a <code>.vercel.app</code> domain.</p>
<p>One other thing to note here is the "Environment Variables" section in the settings. This will need to have the <code>VITE_GRAPHQL_API</code> environment variable added here:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-20.png" alt="Image" width="600" height="400" loading="lazy">
_Add VITE_GRAPHQL<em>API environment variable to the Vercel settings</em></p>
<p>Now any time there are any changes pushed to GitHub, Vercel will build the site.</p>
<h2 id="heading-how-to-publish-and-build-on-content-changes">How to Publish and Build on Content Changes</h2>
<p>Rather than having to push a change to GitHub to create a new build of the project when only the content has changed, you can do this with a GraphCMS integration.</p>
<p>From the Settings panel in GraphCMS, go to the integrations section:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-21.png" alt="Image" width="600" height="400" loading="lazy">
<em>Select the integrations section in the settings panel</em></p>
<p>Click on the Vercel integration – there's also integrations for Netlify and Gatsby Cloud:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-26.png" alt="Image" width="600" height="400" loading="lazy">
<em>Click on the Vercel integration</em></p>
<p>Click "Enable" for the Vercel integration:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-23.png" alt="Image" width="600" height="400" loading="lazy">
<em>Enable the Vercel integration</em></p>
<p>Click the "Connect to Vercel" button when prompted:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-24.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click "Authorise GraphCMS" to make deployments for you on Vercel:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-25.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the "Build projects" section, select your Vercel project from the dropdown selection. The "Display name" is what will appear in the side panel on your content pages. The branch name is the branch you want to deploy to Vercel from GitHub – I use <code>main</code> for the production branch.</p>
<p>There's also an option to specify what models you want to have the integration enabled on. In this case I'm using them all, so selecting "Select all" then finally clicking the "Enable" button:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-28.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If I now go to the content section on the GraphCMS project and select a content model to edit an entry, there's a "Start building Production" button that will kick off a new build any time it's clicked.</p>
<p>Here's the Author model and the Vercel integration on the right hand panel:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-29.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-google-search-console">Google Search Console</h2>
<p><strong>OPTIONAL</strong>: This is an optional step if you own your own domain. A good way to have your site ranked on the search engines is to use the Google search console.</p>
<p><a target="_blank" href="https://search.google.com/search-console">https://search.google.com/search-console</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-32.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-33.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Add the TXT record to your domain using the Vercel CLI. You can add it manually in the domains section of Vercel as well:</p>
<pre><code class="lang-bash">vercel dns add my-developer-portfolio.com @ TXT google-site-verification=g99pqa_kSHiq6AzLtk4HF00tyJhQVt1gGzfUoJQrTPQ
</code></pre>
<p>Once your site has been verified, you can add in your sitemap and click the submit button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-34.png" alt="Image" width="600" height="400" loading="lazy">
<em>Add sitemap to Google search console</em></p>
<p>That's it! You'll now need to wait for the Googlebot to do it's thing and index your site. You should start seeing search queries coming in over time.</p>
<h2 id="heading-resources"><strong>Resources</strong></h2>
<p>Here are some of the resources I used to make the content on the blog I created throughout this guide.</p>
<ul>
<li>Images with <a target="_blank" href="https://lorem.space/api">Lorem.space - placeholder image generator</a></li>
<li>Markdown with <a target="_blank" href="https://jaspervdj.be/lorem-markdownum/">Lorem Markdownum (jaspervdj.be)</a></li>
<li>Bio generation with <a target="_blank" href="https://www.character-generator.org.uk/bio/">Character Biography Generator (character-generator.org.uk)</a></li>
<li>Ideal cover image sizes: <a target="_blank" href="https://buffer.com/library/ideal-cover-photo-size/">The Ideal Cover Photo Size for Each of the Major Social Media Platforms (buffer.com)</a></li>
</ul>
<p>You can check these links for further information on Svelte and SvelteKit</p>
<ul>
<li><a target="_blank" href="https://kit.svelte.dev/docs">https://kit.svelte.dev/docs</a></li>
<li><a target="_blank" href="https://svelte.dev/docs">https://svelte.dev/docs</a></li>
</ul>
<p>If you want the source code for this project then you can check out the GitHub repo <a target="_blank" href="https://github.com/GraphCMS/graphcms-sveltekit-portfolio-and-blog-starter">for all the code</a>. If you have any issues then feel free to log an issue or reach out on <a target="_blank" href="https://twitter.com/spences10">Twitter</a>.</p>
<h2 id="heading-what-we-have-accomplished">What we have accomplished</h2>
<p>Time to recap what we've achieved here. We have gone from hello world through to fully featured portfolio and blog!</p>
<p>We covered getting data from a GraphQL API and displaying that data on a page of the project. We then implemented a GraphQL client to retrieve only the data we needed.</p>
<p>We added the all important sitemap so that the site could be discovered and indexed by search engines like Google.</p>
<p>An optional touch was adding in an RSS feed so that anyone that uses an RSS reader can be notified of any new content that is added to the site.</p>
<p>Finally we deployed our finished project to Vercel for the world to see.</p>
<h2 id="heading-thanks">Thanks</h2>
<p>Thank you so much for taking the time to go through this guide. I hope it gave you all you need to start making your own projects with Svelte.</p>
<p>If you like the content you can check out much more from me on my <a target="_blank" href="https://scottspence.com">blog</a> or you can follow me on <a target="_blank" href="https://twitter.com/spences10">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Travel Bucket List Map with Gatsby, React Leaflet, & Hygraph ]]>
                </title>
                <description>
                    <![CDATA[ Traveling is fun and we all have a lot of places we want to visit, but rarely do we have time to do it all at once. That’s what bucket lists are for! How can we create a custom mapping app that we can show all of our the destinations ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-travel-bucket-list-map-with-gatsby-react-leaflet-graphcms/</link>
                <guid isPermaLink="false">66b8e3580cedc1f2a4f7069b</guid>
                
                    <category>
                        <![CDATA[ beginners guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gatsby ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GatsbyJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ graphcms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ headless cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mapping ]]>
                    </category>
                
                    <category>
                        <![CDATA[ maps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react-leaflet ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Travel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Tue, 23 Jun 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/travel-bucket-list.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Traveling is fun and we all have a lot of places we want to visit, but rarely do we have time to do it all at once. That’s what bucket lists are for! How can we create a custom mapping app that we can show all of our the destinations on our bucket list?</p>
<p>Note: As of July 2022, GraphCMS is now <a target="_blank" href="https://hygraph.com/">Hygraph</a>.</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-step-1-creating-a-new-app-with-gatsby-starter-leaflet">Step 1: Creating a new app with Gatsby Starter Leaflet</a></li>
<li><a class="post-section-overview" href="#heading-step-2-creating-and-managing-a-list-of-travel-locations-with-graphcms">Step 2: Creating and managing a list of travel locations with GraphCMS</a></li>
<li><a class="post-section-overview" href="#heading-step-3-querying-our-graphcms-location-data-with-gatsby-and-graphql">Step 3: Querying our GraphCMS location data with Gatsby and GraphQL</a></li>
<li><a class="post-section-overview" href="#heading-step-4-creating-a-bucket-list-of-destinations-and-adding-them-to-the-map">Step 4: Creating a bucket list of destinations and adding them to the map</a></li>
<li><a class="post-section-overview" href="#heading-what-else-other-features-can-we-add-to-our-app">What else other features can we add to our app?</a></li>
<li><a class="post-section-overview" href="#heading-want-to-learn-more-about-maps">Want to learn more about maps?</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/isbr52VKjb0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We’re going to build a mapping app with <a target="_blank" href="https://www.gatsbyjs.org/">Gatsby</a> managed by a CMS that will both display markers on a map and show our locations in a simple text-based list for our bucket list locations.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/travel-bucket-list-demo.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Demo of a Travel Bucket List mapping app</em></p>
<p>We’ll spin up the app with a <a target="_blank" href="https://github.com/colbyfayock/gatsby-starter-leaflet">Gatsby Starter for Leaflet</a> and then we’ll use <a target="_blank" href="https://graphcms.com/">GraphCMS</a> to create and manage the list of locations for our map!</p>
<h2 id="heading-woah-a-mapping-app">Woah, a mapping app?</h2>
<p>Yup. If you haven't played with maps before, don't be discouraged! It's not as bad as you probably think. If you'd rather start with mapping basics, you can  <a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">read more about how mapping works</a>  first.</p>
<h2 id="heading-step-1-creating-a-new-app-with-gatsby-starter-leaflet">Step 1: Creating a new app with Gatsby Starter Leaflet</h2>
<p>We’ll start off with Gatsby Starter Leaflet. This is going to give us a basic React application with our mapping tools already built in.</p>
<h3 id="heading-creating-a-new-gatsby-app-with-gatsby-starter-leaflet">Creating a new Gatsby app with Gatsby Starter Leaflet</h3>
<p>To get started, navigate to where you want to create your new app and run:</p>
<pre><code class="lang-shell">gatsby new my-travel-bucket-list https://github.com/colbyfayock/gatsby-starter-leaflet
</code></pre>
<p><em>Note: you can replace <code>my-travel-bucket-list</code> with whatever you want. This will be used to create the new folder for the app.</em></p>
<p>Once you run that, Gatsby will pull down the Starter and install the dependencies. After it’s complete, navigate to that directory and run the development command:</p>
<pre><code class="lang-shell">cd my-travel-bucket-list
yarn develop
# or
npm run develop
</code></pre>
<p>Once it’s finished location, your app should be ready to go!</p>
<h3 id="heading-cleaning-our-some-demo-code">Cleaning our some demo code</h3>
<p>Because we’re using a Starter, it has a little bit of demo code. Let’s clean that out to avoid any confusion.</p>
<p>Open up the <code>src/pages/index.js</code> file.</p>
<p>First, remove everything inside of <code>mapEffect</code> except the first line and set up an alias for <code>leafletElement</code> to <code>map</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mapEffect</span>(<span class="hljs-params">{ leafletElement: map } = {}</span>) </span>{
  <span class="hljs-keyword">if</span> ( !map ) <span class="hljs-keyword">return</span>;
}
</code></pre>
<p>With that gone, we can remove the <code>markerRef</code> definition at the top of the <code>IndexPage</code> component, remove the <code>ref={markerRef}</code> prop from our <code>&lt;Marker&gt;</code> component, and the <code>useRef</code> import next to React.</p>
<p>Now, we can remove all of the variables that start with <code>popup</code> and <code>time</code>, including:</p>
<ul>
<li>timeToZoom</li>
<li>timeToOpenPopupAfterZoom</li>
<li>timeToUpdatePopupAfterZoom</li>
<li>popupContentHello</li>
<li>popupContentGatsby</li>
</ul>
<p>Lastly, you can remove all of the following lines:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> L <span class="hljs-keyword">from</span> <span class="hljs-string">'leaflet'</span>;
...
import { promiseToFlyTo, getCurrentLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">'lib/map'</span>;
...
import gatsby_astronaut <span class="hljs-keyword">from</span> <span class="hljs-string">'assets/images/gatsby-astronaut.jpg'</span>;
...
const ZOOM = <span class="hljs-number">10</span>;
</code></pre>
<p>Once done, we should be ready to go with a basic app with a map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/new-app-gatsby-starter-leaflet.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New app with Gatsby Starter Leaflet</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-travel-bucket-list/commit/63eed5a7a208ede6f8eeec44e0c08b594b407360">Follow along with the commit!</a></p>
<h2 id="heading-step-2-creating-and-managing-a-list-of-travel-locations-with-graphcms">Step 2: Creating and managing a list of travel locations with GraphCMS</h2>
<h3 id="heading-creating-a-graphcms-account">Creating a GraphCMS account</h3>
<p>To get started with GraphCMS, you’ll need an account. I’m not going to walk you through this part, but the good news is they have a generous free tier that makes it easy to sign up for us to use for our demo!</p>
<p><a target="_blank" href="https://app.graphcms.com/signup">Sign up for GraphCMS</a></p>
<p>Alternatively, if you already have an account, you can make sure you’re logged in.</p>
<h3 id="heading-creating-a-new-graphcms-project">Creating a new GraphCMS project</h3>
<p>Once logged in, we’ll want to create a new project. We’re going to create one manually, so once at the <a target="_blank" href="https://app.graphcms.com/">GraphCMS Dashboard</a>, select <strong>Create new project</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-create-new-project.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new project in GraphCMS</em></p>
<p>Here, you can enter whatever you’d like for the <strong>Name</strong> and <strong>Description</strong> such as:</p>
<ul>
<li>Name: My Travel Bucket List</li>
<li>Description: The locations that I want to travel to some day!</li>
</ul>
<p>Below that you’ll see a map where you’ll select a <strong>Region</strong>. This is where your database data will live, so while it probably doesn’t matter too much for our purposes, you can choose the one that’s closest to you.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-new-project.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring a new project in GraphCMS</em></p>
<p>After you select your options, go ahead and click <strong>Create Project</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-select-plan.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Selecting the Personal plan in GraphCMS</em></p>
<p>Next, you’ll be presented with billing options. Since we’re just creating a demo, under <strong>Personal</strong> select <strong>Continue</strong> at which point we’ll be dropped into our new GraphCMS project dashboard.</p>
<h3 id="heading-creating-a-new-content-model-schema-with-graphcms">Creating a new Content Model Schema with GraphCMS</h3>
<p>In GraphCMS, a Content Model refers to a specific type of data that has specific properties associated with it. In our case, our Model will be a Destination, which will be defined by a Name and a Location.</p>
<p>First, navigate to the <strong>Schema</strong> section of GraphCMS in the left sidebar and select <strong>Create Model</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-create-new-schema-model.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new Schema Model in GraphCMS</em></p>
<p>Once selected, you’ll see a popup that asks for a bit more information. Here, you can type in “Destination” as the <strong>Display Name</strong>, which will also fill in most of the other fields. We’ll leave those as is.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-new-content-model.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring a new Model in GraphCMS</em></p>
<p>Feel free to add a description if you’d like, but it’s not required. Then select <strong>Create model</strong>.</p>
<p>Now that we have our Model, we need our properties.</p>
<p>First, select <strong>Single line text</strong> in the right list of fields and add a <strong>Display Name</strong> of “Name”. This will also fill out <strong>App Id</strong> which you can leave as is. Then click <strong>Create</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-text-field.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding and configuring a new text field in GraphCMS</em></p>
<p>Next, scroll down in the field options on the right and under <strong>Location</strong> select <strong>Map</strong>. Add “Location” as the <strong>Display Name</strong>, which will set the <strong>App Id</strong> as “location” which you can leave as is. Then same as before, click <strong>Create</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-new-map-field.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding and configuring a new map field in GraphCMS</em></p>
<p>Now we have a Content Model which we’ll use to create our locations!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-destination-content-model.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Destination content Model in GraphCMS</em></p>
<h3 id="heading-creating-our-locations">Creating our locations</h3>
<p>Finally, let’s create our locations. Navigate over to <strong>Content</strong> in the GraphCMS dashboard, make sure you’ve selected <strong>Destination</strong> under <strong>System</strong> (should be the only one), and select <strong>Create New</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-add-new-content.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Create new Destination Content in GraphCMS</em></p>
<p>Now we can start adding all of our locations! First, add the name of your location in the <strong>Name</strong> field, then you can use the <strong>Search</strong> box under <strong>Location</strong> to find that location on the map.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-create-new-destination-content-item.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding a new Destination Content item in GraphCMS</em></p>
<p>Once you’re good, hit <strong>Save and publish</strong>. This will create your first location!</p>
<p>Follow those same steps and create as many locations as you want.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-destination-content-items.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>List of Destination Content items in GraphCMS</em></p>
<p>We’ll use these for our map and bucket list.</p>
<h2 id="heading-step-3-querying-our-graphcms-location-data-with-gatsby-and-graphql">Step 3: Querying our GraphCMS location data with Gatsby and GraphQL</h2>
<p>Now that we have our locations, let’s use them!</p>
<h3 id="heading-adding-a-plugin-to-gatsby-to-query-our-graphql-data">Adding a plugin to Gatsby to query our GraphQL data</h3>
<p>First, we need to <a target="_blank" href="https://www.gatsbyjs.org/packages/gatsby-source-graphql/">add a new plugin</a> to our Gatsby project to query our GraphQL data. In your terminal make sure your development server isn’t running and run:</p>
<pre><code class="lang-shell">yarn add gatsby-source-graphql
# or
npm install gatsby-source-graphql
</code></pre>
<p>Next, open up your <code>gatsby-config.js</code> file in the root of your project and add the following to your plugins:</p>
<pre><code class="lang-json">{
  resolve: 'gatsby-source-graphql',
  options: {
    typeName: 'GCMS',
    fieldName: 'gcms',
    url: '[API ENDPOINT]',
  }
}
</code></pre>
<p>This will be what sources our data from GraphCMS, but we need an endpoint.</p>
<h3 id="heading-finding-our-api-endpoint-for-graphcms">Finding our API endpoint for GraphCMS</h3>
<p>Open back up your browser and head over to your GraphCMS project. After selecting <strong>Settings</strong> in the left navigation, select <strong>API Access</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-api-access.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>API Access in GraphCMS</em></p>
<p>Before we copy our API Endpoint, first we need to update our permissions so we can query our API. Under <strong>Public API Permissions</strong>, check the box next to <strong>Content from stage Published</strong> and click <strong>Save</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-configure-api-access.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring API permissions in GraphCMS</em></p>
<p>Next, copy the URL under <strong>Endpoints</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/graphcms-copy-api-access-endpoint.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Copying API Endpoint in GraphCMS</em></p>
<p>And paste that in to your <code>gatsby-config.js</code> file that we modified above:</p>
<pre><code class="lang-json">{
  resolve: 'gatsby-source-graphql',
  options: {
    typeName: 'GCMS',
    fieldName: 'gcms',
    url: 'https:<span class="hljs-comment">//[region-id].graphcms.com/v2/[project-id]/master',</span>
  },
},
</code></pre>
<p><em>Note: your URL will have actual values inside of <code>[region-id]</code> and <code>[project-id]</code>.</em></p>
<p>Save your <code>gatsby-config.js</code> file and start your development server backup (<code>yarn develop</code>) and we’re ready to go!</p>
<h3 id="heading-querying-our-locations-via-graphql">Querying our locations via GraphQL</h3>
<p>Finally, let’s actually query our data so that we’ll be able to use it in our app.</p>
<p>We’re going to create a new <a target="_blank" href="https://reactjs.org/docs/hooks-reference.html">React Hook</a> that we’ll be able to use to grab our locations anywhere within our app.</p>
<p>Under <code>src/hooks/index.js</code>, add the following line to the existing list:</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> { <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> useDestinations } <span class="hljs-keyword">from</span> <span class="hljs-string">'./useDestinations'</span>;
</code></pre>
<p>This will allow us to more conveniently import our hook which we’ll create next.</p>
<p>Under <code>src/hooks</code>, create a new file <code>useDestinations.js</code> and paste in this code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { graphql, useStaticQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'gatsby'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useDestinations</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { gcms = {} } = useStaticQuery( graphql<span class="hljs-string">`
    query {
      gcms {
        destinations {
          id
          name
          location {
            latitude
            longitude
          }
        }
      }
    }
  `</span> );

  <span class="hljs-keyword">let</span> { destinations } = gcms;

  <span class="hljs-keyword">return</span> {
    destinations,
  };
}
</code></pre>
<p>Here, we’re:</p>
<ul>
<li>Importing the <code>graphql</code> and <code>useStaticQuery</code> utilities from Gatsby</li>
<li>We’re creating a new function (or hook) that is exported by default</li>
<li>In that function, we’re using <code>useStaticQuery</code> to create a new GraphQL query which asks GraphCMS to return the data structure we defined.</li>
<li>That query returns a value which we destructure immediately to grab the <code>gmcs</code> object</li>
<li>We destructure <code>destinations</code> from <code>gmcs</code> and return it as part of a new object from our hook</li>
</ul>
<p>With this, we can now use our hook anywhere in our app!</p>
<p>Head over to your <code>src/pages/index.js</code> file, first import our new hook:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useDestinations } <span class="hljs-keyword">from</span> <span class="hljs-string">'hooks'</span>;
</code></pre>
<p>And at the top of the <code>IndexPage</code> component, query our data:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { destinations } = useDestinations();
</code></pre>
<p>This puts all of our locations into the <code>destinations</code> variable. We can test that this works by console logging it out:</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'destinations'</span>, destinations);
</code></pre>
<p>And once we open up our browser and look in our web developer tools console, we can see our location data!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/gatsby-starter-leaflet-logging-graphcms-destinations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Logging destinations data to the web console</em></p>
<h2 id="heading-step-4-creating-a-bucket-list-of-destinations-and-adding-them-to-the-map">Step 4: Creating a bucket list of destinations and adding them to the map</h2>
<p>We’re going to start with creating a simple text list of our destinations. This will let us see all of our destinations in an easy to read format.</p>
<h3 id="heading-creating-a-text-list-of-our-destinations">Creating a text list of our destinations</h3>
<p>Inside of our <code>IndexPage</code> and above “Still Getting Started?”, let’s add the following code:</p>
<pre><code class="lang-jsx">&lt;h2&gt;My Destinations&lt;/h2&gt;
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
  { destinations.map(destination =&gt; {
    const { id, name } = destination;
    return <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>&gt;</span>{ name }<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  })}
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
</code></pre>
<p>This code:</p>
<ul>
<li>Adds a new header for our list</li>
<li>Creates a new unordered list</li>
<li>Loops through our <code>destinations</code> and creates a new list item for each destination that include’s the location’s name</li>
</ul>
<p>Once we hit save and reload, we should see our list under our map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/app-adding-list-of-destinations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New basic list of destinations in the app</em></p>
<p>The list looks a little odd though right? We probably want to format it a little better to fit into the page.</p>
<p>Open up <code>src/assets/stylesheets/pages/_home.scss</code> and inside of the <code>.home-start</code> class, add:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.home-start</span> {

  ...

  <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">list-style</span>: none;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">1.2em</span> <span class="hljs-number">0</span>;
  }
</code></pre>
<p>Let’s also modify the <code>h2</code> to space things out a little better:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.home-start</span> {

  ...

  <span class="hljs-selector-tag">h2</span> {

    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2em</span>;

    &amp;<span class="hljs-selector-pseudo">:first-child</span> {
      <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">0</span>;
    }

  }
</code></pre>
<p>Once you hit save and reload, it should look a little better.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/app-fixing-styles-list-of-destinations.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Destinations in the app with cleaned up styles</em></p>
<p>Feel free to make additional changes, but we’ll leave it there for now.</p>
<h3 id="heading-adding-our-destinations-to-the-map">Adding our destinations to the map</h3>
<p>Now we can finally add our destinations to the map!</p>
<p>Inside of our <code>&lt;Map&gt;</code> component, we already have a <code>&lt;Marker&gt;</code>. This allows us to easily add a marker to the map given a position. We’ll take this concept and combine it with our text list to add our locations to the map.</p>
<p>Let’s update our <code>&lt;Map&gt;</code> code to match the following:</p>
<pre><code class="lang-jsx">&lt;<span class="hljs-built_in">Map</span> {...mapSettings}&gt;
  { destinations.map(<span class="hljs-function"><span class="hljs-params">destination</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> { id, name, location } = destination;
    <span class="hljs-keyword">const</span> position = [location.latitude, location.longitude];
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Marker</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{position}</span> /&gt;</span></span>
  })}
&lt;/<span class="hljs-built_in">Map</span>&gt;
</code></pre>
<p>Here we:</p>
<ul>
<li>Loop through our <code>destinations</code> to dynamically create a new list of components inside our <code>&lt;Map&gt;</code></li>
<li>Inside each loop instance, we destructure our date from <code>destination</code></li>
<li>We create a new <code>position</code> array with the latitude and longitude</li>
<li>Create a new <code>Marker</code> where we use our position to add it to the map</li>
</ul>
<p>This gives us our markers on the map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/mapping-app-with-destination-markers.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Markers for each destination in the mapping app</em></p>
<p>But we want to know what each of those locations are, so let’s also add a popup to each marker that will show the name.</p>
<p>First, we need to import <code>Popup</code> from <code>react-leaflet</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Marker, Popup } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-leaflet'</span>;
</code></pre>
<p>Then, let’s update our <code>&lt;Marker&gt;</code> component to return:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">return</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Marker</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span> <span class="hljs-attr">position</span>=<span class="hljs-string">{position}</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Popup</span>&gt;</span>{ name }<span class="hljs-tag">&lt;/<span class="hljs-name">Popup</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Marker</span>&gt;</span></span>
);
</code></pre>
<p>And once we save and open back up our map, you can now click on each marker and see our destinations name!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/mapping-app-with-destination-marker-popup.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Popup for each destination marker in the mapping app</em></p>
<h3 id="heading-before-were-done-center-the-map">Before we’re done, center the map</h3>
<p>Previously, our demo map centered on Washington, DC. Let’s update that to the center of the world since our map doesn’t focus on the United States.</p>
<p>Update the <code>LOCATION</code> variable to:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> LOCATION = {
  <span class="hljs-attr">lat</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">lng</span>: <span class="hljs-number">0</span>,
};
</code></pre>
<p>And with that, we have our map!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/mapping-app-with-travel-bucket-list-markers.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Final mapping app with markers and popups for each destination</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-travel-bucket-list/commit/56dbadb74cea2770174eb8ea7c039be27ca18971">Follow along with the commit!</a></p>
<h2 id="heading-what-else-other-features-can-we-add-to-our-app">What else other features can we add to our app?</h2>
<h3 id="heading-add-a-way-to-check-off-each-location">Add a way to check off each location</h3>
<p>Inside GraphCMS, you can add a new field to your Destination content model that allows you to select whether you visited each location or not.</p>
<p>With this value, we can add it to our query and update our map with some kind of indicator like a checkmark to show that we’ve checked it off our bucket list!</p>
<h3 id="heading-customize-your-map-background-styles">Customize your map background styles</h3>
<p>We’re using a public version of <a target="_blank" href="https://www.openstreetmap.org/#map=5/38.007/-95.844">OpenStreetMap</a> which is open source, but <a target="_blank" href="https://www.mapbox.com/">Mapbox</a> offers some cool maps we can use to make it look a little more impressive.</p>
<p>If you want to get started changing your map styles, you can <a target="_blank" href="https://www.freecodecamp.org/news/how-to-set-up-a-custom-mapbox-basemap-with-gatsby-and-react-leaflet/">check out this other walkthrough</a> of mine to learn how to use Mapbox.</p>
<p><a target="_blank" href="https://www.colbyfayock.com/2020/04/how-to-set-up-a-custom-mapbox-basemap-style-with-react-leaflet-and-leaflet-gatsby-starter">Check out the blog post</a> or <a target="_blank" href="https://www.youtube.com/watch?v=KcPJr1b_rv0">watch the video</a>!</p>
<h3 id="heading-style-the-map-markers-with-a-custom-image">Style the map markers with a custom image</h3>
<p>You can check out my video walk through on how to change the markers to a custom image.</p>
<p>Take that a step further and use the feature above to dynamically show a different marker image when you’ve checked off a location.</p>
<p><a target="_blank" href="https://egghead.io/lessons/react-customize-geojson-data-markers-with-a-react-leaflet-icon-image?pl=mapping-with-react-leaflet-e0e0&amp;af=atzgap">Check out the video on Egghead.io!</a></p>
<h2 id="heading-want-to-learn-more-about-maps">Want to learn more about maps?</h2>
<p>Check out some of my other tutorials and videos:</p>
<ul>
<li><a target="_blank" href="https://egghead.io/playlists/mapping-with-react-leaflet-e0e0?af=atzgap">Mapping with React Leaflet</a> (<a target="_blank" href="https://egghead.io/?af=atzgap">egghead.io</a>)</li>
<li><a target="_blank" href="https://www.youtube.com/playlist?list=PLFsfg2xP7cbJTnTFH3OGXEAt9O1mpoqpR">Mapping Apps with React, Gatsby, &amp; Leaflet</a> (<a target="_blank" href="https://www.youtube.com/channel/UC7Wpv0Aft4NPNhHWW_JC4GQ">youtube.com</a>)</li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-coronavirus-covid-19-dashboard-map-app-with-gatsby-and-leaflet">How to create a Coronavirus (COVID-19) Dashboard &amp; Map App with Gatsby and Leaflet</a> (colbyfayock.com)</li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/how-to-create-a-summer-road-trip-mapping-app-with-gatsby-and-leaflet">How to Create a Summer Road Trip Mapping App with Gatsby and Leaflet</a> (colbyfayock.com)</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/easily-spin-up-a-mapping-app-in-react-with-leaflet/">How to build a mapping app in React the easy way with Leaflet</a> (colbyfayock.com)</li>
<li><a target="_blank" href="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping">Anyone Can Map! Inspiration and an introduction to the world of mapping</a> (colbyfayock.com)</li>
</ul>
<h2 id="heading-whats-on-your-travel-bucket-list">What’s on your travel bucket list?</h2>
<p><a target="_blank" href="https://twitter.com/colbyfayock">Let me know on Twitter!</a></p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1275441134144110595"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
