<?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[ portfolio - 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[ portfolio - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 26 May 2026 16:23:17 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/portfolio/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Portfolio Website Using Figma and AI Tools – A Guide for Developers ]]>
                </title>
                <description>
                    <![CDATA[ Ever since my article on How to Become a Full Stack Developer and Get a Job in 2025 went viral, I’ve received countless DMs, emails, and even WhatsApp messages from readers. People have been asking about everything from learning to code and mastering... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-portfolio-website-using-figma-and-ai-tools/</link>
                <guid isPermaLink="false">691b93910ca13a7c59ae7a34</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ figma ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Prankur Pandey ]]>
                </dc:creator>
                <pubDate>Mon, 17 Nov 2025 21:28:49 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763156337448/4d944077-b800-4bfd-bb70-645777eb00a2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Ever since my article on <a target="_blank" href="https://www.freecodecamp.org/news/become-a-full-stack-developer-and-get-a-job/">How to Become a Full Stack Developer and Get a Job in 2025</a> went viral, I’ve received countless DMs, emails, and even WhatsApp messages from readers. People have been asking about everything from learning to code and mastering system design to web design and how AI fits into modern development.</p>
<p>I’ve been taking it one topic at a time. My last piece on system design principles received great feedback, and that encouragement has kept me going.</p>
<p>Lately, I’ve developed a deep interest in UI/UX design – not just out of curiosity, but because so many readers have been requesting a detailed guide. They want one that explains how to learn web design, how to apply it to real-world projects, and how AI is reshaping the field.</p>
<p>Looking back, my journey started six years ago as a software tester. From there, I transitioned into full-stack development, then DevOps. I also explored data analysis, and today I manage a full-time freelancing career alongside my farming work.</p>
<p>One thing I’ve learned along the way is that a strong portfolio can take you to places you’d never imagined.</p>
<p>I’ve had a portfolio for years, but now I’m completely rebuilding it from scratch to make it cleaner, smarter, and more reflective of who I’ve become as a developer.</p>
<p>In this tutorial, I’ll walk you through how I created my new portfolio and show you how you can build yours too – with the right structure, design, and a touch of copywriting magic to make it stand out.</p>
<h3 id="heading-what-well-cover">What We’ll Cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-portfolio-website-and-why-do-you-need-one">What is a Portfolio Website and Why Do You Need One?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-important-portfolio-website-components">Important Portfolio Website Components</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-your-portfolio-website-stand-out">How to Make Your Portfolio Website Stand Out</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-figma-to-design-your-own-portfolio">How to use Figma to Design Your Own Portfolio</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tools-for-figma-to-code-generation">Tools for Figma to Code Generation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-copywriting-for-your-portfolio">Copywriting for Your Portfolio</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-testing-benchmarks-for-a-portfolio-website">Testing Benchmarks for a Portfolio Website</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hosting-your-portfolio">Hosting Your Portfolio</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-your-portfolio-effectively">How to Use Your Portfolio Effectively</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-qampa">Q&amp;A</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-notes">Final Notes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-portfolio-website-and-why-do-you-need-one">What is a Portfolio Website and Why Do You Need One?</h2>
<p>A portfolio website is your own space on the internet where you can proudly show your best work, projects, and achievements. Think of it as your digital résumé, but far more visual and personal. Instead of just listing your skills or job titles, it lets people actually see what you’ve built, designed, or created, and this tells your story better than any document can.</p>
<p>It helps employers, clients, or collaborators understand your skills, experience, and creativity at a glance. While it’s beneficial for designers, developers, writers, and photographers, anyone who wants to share their work online can create one, from students and freelancers to business professionals.</p>
<p>Here’s why having a portfolio website matters:</p>
<ul>
<li><p><strong>A collection of your best work:</strong> like a gallery where you keep your most impressive projects, artwork, or achievements all in one place.</p>
</li>
<li><p><strong>Your online identity:</strong> It’s your personal brand, showing who you are, what you do, and what makes your work stand out.</p>
</li>
<li><p><strong>A way to showcase your skills:</strong> Through images, videos, and live demos, it give people a clear picture of your talent and creativity.</p>
</li>
<li><p><strong>An open door for opportunities:</strong> People can visit your site, explore your work, and contact you directly for jobs, collaborations, or freelance work.</p>
</li>
<li><p><strong>A tool for career growth:</strong> A well-built portfolio helps you attract clients, get noticed by employers, and open doors to new partnerships.</p>
</li>
</ul>
<p>In simple words, your portfolio website is your professional story told visually – a mix of your work, passion, and personality that helps the world see what you’re capable of.</p>
<h2 id="heading-important-portfolio-website-components">Important Portfolio Website Components</h2>
<p>Now that we know why a portfolio website matters, let’s look at the key components that make it stand out. Each part plays a role in showing who you are, what you can do, and how you can help others.</p>
<p>Since I’m a developer, and the people who have reached out to me are mostly tech enthusiasts, here I’ll give a technical portfolio example. But you can apply the learnings from this guide to build any type of portfolio for any niche/business.</p>
<h3 id="heading-1-home-page-the-first-impression">1. Home Page: The First Impression</h3>
<p>Your homepage is like the front door to your digital world. It should quickly tell visitors who you are, what you do, and why you’re worth hiring or collaborating with. Keep it clean, simple, and welcoming: just a few strong lines about your skills and what kind of projects you work on.</p>
<p>Tip: Use a short headline like “Hi, I’m Prankur – I build fast, user-friendly web apps.”</p>
<h3 id="heading-2-about-section-your-story">2. About Section: Your Story</h3>
<p>This is where you make your connection with the audience. Share your journey: how you started, what drives you, and what kind of problems you love solving. Keep it conversational and honest. People love working with real humans, not buzzwords.</p>
<p>Tip: Add a professional but friendly photo (optional) here to make it more personal.</p>
<h3 id="heading-3-projects-work-section-show-dont-just-tell">3. Projects / Work Section: Show, Don’t Just Tell</h3>
<p>This is the heart of your portfolio. List your best projects – the ones that represent your strongest skills and the kind of work you want to keep doing. Each project should include:</p>
<ul>
<li><p>A short description of what it is.</p>
</li>
<li><p>The tools or technologies you used.</p>
</li>
<li><p>The challenge you solved.</p>
</li>
<li><p>A screenshot or live demo link.</p>
</li>
<li><p>GitHub link so that people can see how you code</p>
</li>
</ul>
<p>Tip: 3–6 strong projects are better than 15 average ones. If you’re actively contributing to open source, you should add that to your portfolio, too.</p>
<h3 id="heading-4-case-studies-tell-the-story-behind-the-work">4. Case Studies: Tell the Story Behind the Work</h3>
<p>Case studies take your projects one step further. They explain your thinking process and how you understood the client’s problem, what steps you took, and what results you achieved. This helps potential employers or clients see your problem-solving skills, not just your designs or code.</p>
<p>Tip: Keep it short and focus on the “before → after” transformation.</p>
<h3 id="heading-5-skills-amp-tools-section">5. Skills &amp; Tools Section</h3>
<p>List your key technical and creative skills, like HTML, CSS, JavaScript, React, Figma, or AI.<br>You can group them as:</p>
<ul>
<li><p>Frontend Tools</p>
</li>
<li><p>Backend Tools</p>
</li>
<li><p>Design Tools</p>
</li>
<li><p>AI or Productivity Tools</p>
</li>
</ul>
<p>Tip: Keep the list focused and highlight the tools you actually use, not every tool you’ve ever tried.</p>
<h3 id="heading-6-testimonials-social-proof-optional">6. Testimonials: Social Proof (Optional)</h3>
<p>People trust people. Include a few quotes or short testimonials from clients, teammates, or mentors who can vouch for your skills and professionalism. It instantly builds trust and credibility.</p>
<p>If you are just starting, you can add this section later on, as initially you won’t have testimonials. Once you start working, you can politely ask your clients to give their feedback on your work. Just make sure to get their permission to add it to your site.</p>
<p>Tip: Ask for a 2–3 sentence testimonial right after finishing a project.</p>
<h3 id="heading-7-blog-or-articles-section-optional">7. Blog or Articles Section (Optional)</h3>
<p>If you write tutorials or share knowledge, add a blog section. It helps you stand out as someone who not only builds but also teaches and communicates ideas clearly. It also boosts SEO and keeps your site fresh.</p>
<p>Tip: Even 2–3 strong, educational articles can make a huge difference.</p>
<h3 id="heading-8-contact-section-make-it-easy-to-reach-you">8. Contact Section: Make It Easy to Reach You</h3>
<p>Your contact page should be simple, visible, and welcoming. Include your email, social links (LinkedIn, GitHub, X/Twitter), and maybe a contact form. You can also add a small call to action like:</p>
<blockquote>
<p>“Have a project in mind? Let’s connect.”</p>
</blockquote>
<p>Tip: Place your contact link in the main menu. Don’t hide it.</p>
<h3 id="heading-9-resume-downloadable-cv-optional">9. Résumé / Downloadable CV (Optional)</h3>
<p>If you’re job-hunting, include a link to download your latest résumé. You can also add a “quick summary” of your experience, education, and certifications directly on the website.</p>
<h3 id="heading-10-call-to-action-cta">10. Call to Action (CTA)</h3>
<p>Every great portfolio ends with a nudge – a simple next step for your visitor. It could be:</p>
<ul>
<li><p>“Let’s build something amazing together.”</p>
</li>
<li><p>“Hire me for your next project.”</p>
</li>
<li><p>“Check out my latest work.”</p>
</li>
</ul>
<p>A clear CTA can help turn a visitor into a lead or follower.</p>
<p>Your portfolio is not just a gallery. It’s a storytelling tool. It tells the world what you can do, how you think, and what makes you unique. Make sure every section serves a purpose, looks clean, and reflects your real personality.</p>
<h2 id="heading-how-to-make-your-portfolio-website-stand-out">How to Make Your Portfolio Website Stand Out</h2>
<p><strong>1. Showcase Your Best Work</strong><br>Only include your strongest and most relevant projects. Quality matters more than quantity. A few great examples of your work will create a much better impression than a long list of average ones.</p>
<p><strong>2. Use High-Quality Images and Videos</strong><br>Make sure your work looks clear and professional. Good visuals instantly catch attention and show that you care about presentation. Use clean screenshots, mockups, or short demo videos.</p>
<p><strong>3. Write Clearly and Keep It Short</strong><br>Explain your work and skills in simple, easy-to-read language. Avoid long paragraphs — most visitors just skim. A few lines that get straight to the point are enough.</p>
<p><strong>4. Tell Your Story</strong><br>Use your “About Me” section to share your journey — how you started, what you love building, and what kind of work you enjoy most. People connect more with stories than with just titles or resumes.</p>
<p><strong>5. Keep Navigation Simple</strong><br>Make sure visitors can easily find your projects, contact info, and other key details. A clean menu and clear layout help people focus on your work instead of figuring out where to click.</p>
<p><strong>6. Keep It Updated</strong><br>Whenever you finish a new project or learn a new skill, add it to your portfolio. An up-to-date portfolio shows that you’re active, learning, and growing in your field.</p>
<p><strong>7. Optimise for SEO</strong><br>Use the right keywords — the ones people might type when searching for your kind of work (like “frontend developer” or “Figma to code expert”). This helps your site appear in search results. Many AI-powered website builders can assist with basic SEO setup.</p>
<h2 id="heading-how-to-use-figma-to-design-your-own-portfolio">How to Use Figma to Design Your Own Portfolio</h2>
<p>Designing your own portfolio might sound intimidating at first, but tools like <strong>Figma</strong> make the process surprisingly intuitive – even if you’re not a professional designer. With Figma, you can visually plan every section of your portfolio before turning it into a live website.</p>
<p>I’ve been learning web design and using Figma for a long time for my client projects. But here, I didn’t build the entire portfolio from scratch. I used Figma’s design inspiration library to get some ideas, and I built the portfolio based on that.</p>
<p>Let’s walk through how you can do that, step by step, using the layout shown below as an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763135985349/8cb05eb4-7562-4845-992e-9829ebadf6a4.png" alt="example-portfolio" class="image--center mx-auto" width="1366" height="3226" loading="lazy"></p>
<h3 id="heading-1-start-with-a-wireframe">1. Start With a Wireframe</h3>
<p>Before adding colours or fancy visuals, begin with a simple wireframe. It’s basically a rough sketch of your portfolio layout.</p>
<p>In Figma, you can create frames for each section you want on your site:</p>
<ul>
<li><p>Hero section (your name, title, and call-to-action buttons)</p>
</li>
<li><p>“What I Do Best” or skills</p>
</li>
<li><p>Tech stack</p>
</li>
<li><p>Featured projects</p>
</li>
<li><p>Blog or article previews</p>
</li>
<li><p>Services and pricing</p>
</li>
<li><p>Contact form</p>
</li>
</ul>
<p>This helps you get the structure right before focusing on design. Don’t worry about fonts or images yet – just boxes and text placeholders are enough.</p>
<p>Here’s a sample wireframe I created to visualise how everything should look before moving into Figma. It’s always a good idea to sketch or plan your design on paper first, as it helps you get a clear picture of the layout and refine details like colours, fonts, and spacing early on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762753167965/584a137e-6414-4ce6-ad1e-0408d040003f.png" alt="wireframe-figma" class="image--center mx-auto" width="1024" height="1024" loading="lazy"></p>
<p>Once the wireframe is ready, you can start adding your preferred colours, typography, and images to bring the design to life. If you want to go a step further and understand how to choose the right colour palettes and fonts, it’s worth exploring <strong>design psychology</strong> — it plays a huge role in how people perceive your work.</p>
<h3 id="heading-2-add-visual-hierarchy-and-colour">2. Add Visual Hierarchy and Colour</h3>
<p>Once your structure feels solid, start adding typography and colour to create a visual hierarchy. This helps the viewer’s eyes know where to focus first.</p>
<p>For example, in the sample design above, the <strong>hero section</strong> (at the top) instantly grabs attention with bold typography (“I build things for the web”) and a subtle background/hero image.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763135295720/87dfa444-1e80-45b2-8e57-7f88971a4742.png" alt="visual-hierachy-and-color-figma" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Use Figma’s colour styles to define a consistent palette. Try using about 3 to 4 shades that complement each other. Keep it minimal and professional. For text, you can choose neutral fonts like Poppins, Inter, or Roboto, which look great on both dark and light themes.</p>
<h3 id="heading-3-create-components-for-reuse">3. Create Components for Reuse</h3>
<p>Figma allows you to turn recurring UI elements like buttons, cards, and tags into components. In the portfolio example here, notice how every project card, article preview, and pricing box follows the same layout. By creating a single card component and reusing it, you can easily maintain consistency and update everything in one go later.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763135361258/41a8fdfe-d384-4039-8ab1-1a2499e58363.png" alt="components-reuse-figma" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>In this design, I will be using a blog featuring component multiple times, so instead of designing it again and again, I have made it once, and I am now using it multiple times.</p>
<h3 id="heading-4-add-your-content-and-images">4. Add Your Content and Images</h3>
<p>Now that your base design is ready, it’s time to make it personal. You can go ahead and replace placeholders with:</p>
<ul>
<li><p>Your best project screenshots (use mockups if needed),</p>
</li>
<li><p>Professional headshot or relevant visuals,</p>
</li>
<li><p>Real content, like your bio, skills, and service details.</p>
</li>
</ul>
<p>Make sure you keep the spacing clean. It ensures all sections stay neatly aligned, even when you adjust or add new elements later.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763135295720/87dfa444-1e80-45b2-8e57-7f88971a4742.png" alt="add-your-content-figma" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>In this hero section, I have used my name and colour and font of my choice on the left, and you can see the <strong>Styles</strong> showing all the colours I have used across the project.</p>
<h3 id="heading-5-create-interactive-prototypes">5. Create Interactive Prototypes</h3>
<p>Before exporting your design, use Figma’s <strong>Prototype Mode</strong> to link different pages together – for example, make “Projects” in the top navigation scroll smoothly to your project section. This gives you a working demo to test how users will experience your portfolio before you even write a single line of code.</p>
<p>You can also share this prototype with friends or mentors for quick feedback.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763135522546/1ef82103-13b6-4e9e-b102-b1c8cc20d03f.png" alt="create-interactive-prototype-figma" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<h3 id="heading-6-export-or-hand-off-for-development">6. Export or Hand Off for Development</h3>
<p>After testing your design in Prototype Mode when you’re satisfied, the final step is the <strong>handoff</strong>. Use the Export panel to download all required assets (icons, logos, images) in their correct formats. Then share the Figma file and go to <strong>Dev Mode</strong>, where you can inspect typography, spacing, sizes, and colour values. These exported assets and inspected specs are what developers use to create the live HTML/CSS code.</p>
<p>Note: The Dev Mode in Figma allows you to inspect CSS and export your stuff easily. Since I don’t have a paid Figma plan, I can’t show inspecting and exporting.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763134948092/1f98417c-5120-48ac-b302-84d5234a7b95.png" alt="figma-handoff" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Designing your portfolio in Figma gives you full freedom over layout, colour choices, and how you present your personality. Keep it simple, prioritise usability, and design with clarity. Once the layout is complete and tested, converting it into a functional site becomes straightforward.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763138419092/978156ba-dbb3-4a6e-bcb8-3a0c7ff66e0d.png" alt="figma-auto-layout" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>You can also use auto layout just by right-clicking on the design section components.</p>
<p>Auto Layout allows designers to define rules for spacing, direction, padding, and alignment of elements. This ensures layouts like buttons, lists, and cards instantly adapt (scale/shrink) to content changes or different screen sizes without manual adjustments.</p>
<p>Once you’ve built and tested your Figma layout, converting it into code becomes the easy part.</p>
<h2 id="heading-tools-for-figma-to-code-generation">Tools for Figma to Code Generation</h2>
<p>By the time you’re done with the wireframe and colours, and your portfolio is live on Figma, it’s time to convert that Figma design into code. There are two ways to do so:</p>
<ul>
<li><p>Manual Method</p>
</li>
<li><p>AI Method</p>
</li>
</ul>
<h3 id="heading-how-to-manually-convert-figma-designs-to-code">How to Manually Convert Figma Designs to Code</h3>
<p>When you design a website or app in Figma, the next big step is turning that design into working code. Traditionally, developers have done this manually: inspecting each element, writing HTML, CSS, and then refining the layout in frameworks like React or Tailwind CSS.</p>
<p>It’s important to understand <strong>how this process is normally done by hand</strong>. This is the foundation that every frontend developer relies on — and it’s also what helps you evaluate and improve AI-generated code later.</p>
<p>When you design a website or app in Figma, the next step is to translate that visual layout into real HTML, CSS, and JavaScript. Developers usually start by examining each element in the design – its size, spacing, typography, colour, and layout rules – and manually rebuilding it in code.</p>
<h3 id="heading-exporting-visual-assets">Exporting Visual Assets</h3>
<p>Using Figma’s <em>Export</em> panel, you download only what needs to be recreated visually in code:</p>
<ul>
<li><p>icons</p>
</li>
<li><p>illustrations</p>
</li>
<li><p>logos</p>
</li>
<li><p>images or thumbnails</p>
</li>
</ul>
<p>Most structural elements (buttons, cards, sections, containers) are <strong>not</strong> exported – you’ll need to build them using HTML and CSS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763127453427/38bbbdef-270c-4157-8a2d-8fd8591426ea.png" alt="figma-export-panel" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<h3 id="heading-rebuilding-the-layout-with-html-and-css">Rebuilding the Layout with HTML and CSS</h3>
<p>You’ll recreate the page section by section:</p>
<ul>
<li><p>Setting up the HTML structure</p>
</li>
<li><p>Adding Flexbox or CSS Grid for layouts</p>
</li>
<li><p>Applying typography exactly as specified in Figma</p>
</li>
<li><p>Matching spacing using padding, margins, and gaps</p>
</li>
<li><p>Defining colours using the hex codes from the Figma Inspector</p>
</li>
</ul>
<p>If you’re using Tailwind CSS, this becomes a matter of applying the right utility classes, but the logic remains the same: <em>everything is recreated manually</em>.</p>
<p>Once you have your Figma layout ready, the real work happens inside your code editor (which for me is VS Code). Here’s how developers traditionally rebuild the UI one section at a time.</p>
<h3 id="heading-set-up-your-project-structure">Set Up Your Project Structure</h3>
<p>In VS Code, create your project structure. It’ll look something like this:</p>
<pre><code class="lang-plaintext">project/
 ├─ index.html
 ├─ styles.css
 ├─ /assets
 │    ├─ logo.svg
 │    ├─ hero-image.png
 │    └─ icons/
</code></pre>
<p>If you’re using React, this becomes the structure:</p>
<pre><code class="lang-bash">src/
 ├─ App.jsx
 ├─ components/
 ├─ index.css
 ├─ assets/
</code></pre>
<p>This structure mirrors what you saw in Figma: each major design section becomes a container or component.</p>
<h3 id="heading-write-the-html-structure-section-by-section">Write the HTML Structure Section-by-Section</h3>
<p>Now, you’ll look at the Figma frame and convert it to raw markup.</p>
<p>As an example, let’s look at the Hero Section.</p>
<p>Figma frame shows:</p>
<ul>
<li><p>a headline</p>
</li>
<li><p>subtext</p>
</li>
<li><p>a button</p>
</li>
<li><p>an illustration</p>
</li>
</ul>
<p>So your HTML becomes this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hero"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hero-content"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Modern UI for Everyone<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Beautiful designs built for speed and accessibility.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"cta-btn"</span>&gt;</span>Get Started<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/hero-image.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Hero illustration"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hero-img"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>You're essentially mapping each Figma layer onto an HTML element.</p>
<h3 id="heading-build-layouts-using-flexbox-or-grid">Build Layouts Using Flexbox or Grid</h3>
<p>In Figma, layouts are visual. In HTML/CSS, you must <strong>translate</strong> the layout rules.</p>
<p>If Figma shows two columns (text + image), you’ll use Flexbox:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hero</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: space-between;
}
</code></pre>
<p>If Figma has a 3-card layout, you’ll use Grid:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.features</span> {
  <span class="hljs-attribute">display</span>: grid;
  <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-built_in">repeat</span>(<span class="hljs-number">3</span>, <span class="hljs-number">1</span>fr);
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">2rem</span>;
}
</code></pre>
<p>You literally reconstruct the layout <em>from scratch</em> by interpreting the Figma frame’s structure.</p>
<h3 id="heading-apply-typography-exactly-as-in-figma">Apply Typography Exactly as in Figma</h3>
<p>Next, you’ll go to Figma, select a text layer, and check the <strong>Inspector</strong>:</p>
<ul>
<li><p>Font family: Inter</p>
</li>
<li><p>Font size: 36px</p>
</li>
<li><p>Line height: 44px</p>
</li>
<li><p>Font weight: 700</p>
</li>
<li><p>Letter spacing: -1%</p>
</li>
</ul>
<p>Then recreate it like this:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hero</span> <span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Inter'</span>, sans-serif;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">44px</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
  <span class="hljs-attribute">letter-spacing</span>: -<span class="hljs-number">0.01em</span>;
}
</code></pre>
<p>This is why developers need to know their fundamentals: AI tools often miss these fine details.</p>
<h3 id="heading-recreate-spacing-with-padding-margins-gaps">Recreate Spacing With Padding, Margins, Gaps</h3>
<p>In Figma, spacing is visual. But in code, you must <em>calculate and apply</em> it.</p>
<p>Example Figma spacing:</p>
<ul>
<li><p>Padding inside a button: 16px vertical / 32px horizontal</p>
</li>
<li><p>Margin below a heading: 24px</p>
</li>
<li><p>Gap between elements: 32px</p>
</li>
</ul>
<p>So you write this code:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.cta-btn</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">16px</span> <span class="hljs-number">32px</span>;
}

<span class="hljs-selector-class">.hero</span> <span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">24px</span>;
}

<span class="hljs-selector-class">.hero-content</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">32px</span>;
}
</code></pre>
<p>Everything must match the design. And in this case, spacing is what makes UI look polished.</p>
<h3 id="heading-apply-colours-using-figma-hex-codes">Apply Colours Using Figma Hex Codes</h3>
<p>In Figma Inspector:</p>
<ul>
<li><p>Primary Blue → <code>#4A78FF</code></p>
</li>
<li><p>Background → <code>#F8FAFC</code></p>
</li>
<li><p>Text → <code>#1A1A1A</code></p>
</li>
</ul>
<p>In your CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#F8FAFC</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#1A1A1A</span>;
}

<span class="hljs-selector-class">.cta-btn</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#4A78FF</span>;
  <span class="hljs-attribute">color</span>: white;
}
</code></pre>
<p>You’ll need to manually copy each hex code into CSS, as this is where accuracy matters.</p>
<h3 id="heading-if-using-tailwind-apply-utility-classes-instead-of-writing-css">(If Using Tailwind) Apply Utility Classes Instead of Writing CSS</h3>
<p>Switching back and forth between your Figma design and a separate CSS file can slow down your whole workflow. Tailwind CSS solves this by letting you write styles right inside your HTML using small, reusable utility classes.</p>
<p>In this section, you’ll learn how to take the exact pixel values you see in the <strong>Figma Inspector</strong> and turn them into Tailwind utilities.</p>
<h4 id="heading-step-1-understand-tailwinds-spacing-scale">Step 1: Understand Tailwind’s Spacing Scale</h4>
<p>Tailwind doesn’t use raw pixel values. Instead, it uses a consistent spacing scale, usually based on a 4px grid.</p>
<p>Here’s the simple rule: Take the pixel value from Figma, divide by 4, and that number becomes your Tailwind utility.</p>
<p>Example:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Figma</td><td>Tailwind</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>margin-top: 32px</code></td><td><code>mt-8</code></td><td>32 ÷ 4 = 8 → so the class is <code>mt-8</code></td></tr>
</tbody>
</table>
</div><p>Prefixes you should know:</p>
<ul>
<li><p><code>m</code> = margin</p>
</li>
<li><p><code>p</code> = padding</p>
</li>
<li><p><code>t</code>, <code>b</code>, <code>l</code>, <code>r</code> = top, bottom, left, right</p>
</li>
<li><p><code>x</code> = horizontal (left + right)</p>
</li>
<li><p><code>y</code> = vertical (top + bottom)</p>
</li>
</ul>
<h4 id="heading-step-2-converting-complex-padding">Step 2: Converting Complex Padding</h4>
<p>If your Figma element uses different padding for X and Y, convert each side separately.</p>
<p>Example:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Figma</td><td>Tailwind</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>padding: 16px 32px</code></td><td><code>py-4 px-8</code></td><td>16 ÷ 4 = 4 → <code>py-4</code></td></tr>
<tr>
<td>32 ÷ 4 = 8 → <code>px-8</code></td><td></td></tr>
</tbody>
</table>
</div><h4 id="heading-step-3-converting-font-sizes">Step 3: Converting Font Sizes</h4>
<p>Tailwind doesn’t use pixel sizes for fonts. Instead, it uses semantic names – almost like T-shirt sizes.</p>
<p>Example:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Figma Font Size</td><td>Tailwind Class</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>36px</code></td><td><code>text-4xl</code></td><td>Tailwind’s <code>4xl</code> matches 36px in the default scale</td></tr>
</tbody>
</table>
</div><p>This encourages typography consistency, instead of manually choosing random font sizes.</p>
<h4 id="heading-why-this-method-is-so-fast">Why This Method Is So Fast</h4>
<p>Once you get used to it, the workflow becomes second nature:</p>
<pre><code class="lang-powershell">Figma value → divide by <span class="hljs-number">4</span> → apply Tailwind <span class="hljs-class"><span class="hljs-keyword">class</span></span>
</code></pre>
<p>No switching files. No naming CSS classes. No extra stylesheet bloat.</p>
<p>Just fast, direct, design-to-code translation.</p>
<h4 id="heading-try-it-yourself">Try It Yourself</h4>
<p>Open a Figma component (like a button or a card) and check its spacing, padding, and font size in the Inspector. Then convert everything using the simple rules above.</p>
<p>Before long, you’ll be turning Figma designs into clean Tailwind code in seconds.</p>
<p>You still translate Figma to code manually.</p>
<p>Alright, back to our workflow:</p>
<h3 id="heading-making-it-responsive">Making It Responsive</h3>
<p>You check how the design should behave on different screen sizes:</p>
<ul>
<li><p>stacking vs. side-by-side layouts</p>
</li>
<li><p>font scaling</p>
</li>
<li><p>spacing adjustments</p>
</li>
<li><p>collapsing navigation or rearranging grids</p>
</li>
</ul>
<p>This requires writing responsive styles and breakpoints, which AI tools can attempt but rarely perfect.</p>
<h3 id="heading-why-the-manual-way-is-important">Why the Manual Way Is Important</h3>
<p>Even though it takes more time, this approach teaches fundamentals that AI tools simply can’t replace:</p>
<ul>
<li><p>How spacing systems work</p>
</li>
<li><p>How components behave in real browsers</p>
</li>
<li><p>How layout changes across devices</p>
</li>
<li><p>How to optimise for performance and accessibility</p>
</li>
</ul>
<p>These skills allow you to <strong>inspect code intelligently</strong> and improve it wherever needed, which is <em>almost always necessary</em>, no matter which tool you use.</p>
<h3 id="heading-how-to-use-ai-tools-to-convert-figma-designs-to-code-optional">How to Use AI Tools to Convert Figma Designs to Code (Optional)</h3>
<p>To speed up this process, you can now use AI-powered and automated Figma-to-code tools. These tools analyse the Figma file and instantly generate code that you can integrate into your tech stack.</p>
<p>There are multiple tools available in the market, like V0, Builder.io, and Kombai.</p>
<ul>
<li><p><a target="_blank" href="https://v0.dev">V0</a> is A Vercel project focused on fast UI generation, integrated with Next.js workflows.</p>
</li>
<li><p><a target="_blank" href="https://Builder.io">Builder.io</a> is visual CMS with Figma integration and a visual editor that can export components for web apps.</p>
</li>
<li><p><a target="_blank" href="https://kombai.com">Kombai</a> is an IDE extension that converts selected Figma frames into React/NextJS/React Native code and offers repo-aware features.</p>
</li>
</ul>
<h4 id="heading-building-live">Building Live</h4>
<p>For demonstration purposes, I’ll be using Kombai, a VS Code extension. And since most of us developers spend a lot of time inside VS Code, it’s the perfect place to test it out.</p>
<p>To install it in VS Code, simply open the Extensions Marketplace, search for 'kombai', click 'Install' and open the extension.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761314891808/1e28092b-9011-476d-869d-08ce6b675766.png" alt="Kombai agent vs code install " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Now, at the agent panel, click on the “Let’s get started” button and sign up or log in with your credentials.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762085091479/abb9f832-b66a-42f5-990c-0a070dc33fd9.png" alt="Kombai agent vs code " class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Now it’s time to test the agent for real work. To do so, I’ll be using the Figma file attached to build this portfolio.</p>
<p>A few things to keep in mind as you get going:</p>
<ul>
<li><p>You’ll need to enable WebGL if Figma is not working on your browser.</p>
</li>
<li><p>You need to know how to copy the Figma file link and how to export the design from Figma.</p>
</li>
</ul>
<p>To copy the Figma design file link, open your Figma design file and simply select your desired design, select copy/paste, and then select copy link to selection. It will copy the complete design file URL.</p>
<p>You can also copy the link of any Figma component and follow the same approach to get the desired outcome.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762935992593/08f6443d-4a62-42fc-8294-0c8f8000bef0.png" alt="figma-design-portfolio" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>Open the agent extension in VSCode and select the Figma (icon) agent at the bottom of the agent toolbar. It will open a new pop-up. In it, paste the link you copied to the design file (check the screenshot below). Then you can prompt the agent with what you want it to do with this Figma file.</p>
<p>In my case, I wanted to replicate the same design, so I gave this prompt along with my Figma design link:</p>
<blockquote>
<p>“You are an expert UI/UX designer and your task is to build and replicate the entire Figma design in the HTML/CSS/JS code from the attached URL.“</p>
</blockquote>
<p>After confirming this, the agent will start working to replicate the same design as you’ve seen in the Figma file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761456000444/a34b5abe-cb75-4634-b090-a53d1f4df026.png" alt="figma-to-code-kombai-agent" class="image--center mx-auto" width="1366" height="768" loading="lazy"></p>
<p>And here are the results:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763136179145/eb51a1ca-7d63-4a16-ba80-80854d715066.png" alt="figma-to-code-kombai-output" class="image--center mx-auto" width="1366" height="3226" loading="lazy"></p>
<p>Now you have the full picture of both approaches and when to use each one.</p>
<p>Here is the live Project link - <a target="_blank" href="https://fcc-demo.netlify.app/">FCC FIGMA TO CODE</a></p>
<p>Here is the GitHub link for the project code - <a target="_blank" href="https://github.com/prankurpandeyy/fcc-demo-portfolio">Github Code</a></p>
<h3 id="heading-challenges-in-ai-generated-code-and-why-developer-oversight-is-essential">Challenges in AI-Generated Code (and Why Developer Oversight Is Essential)</h3>
<p>AI tools can speed up the design-to-code process, but the output is rarely production-ready. Most tools struggle with layout precision, accessibility, and responsiveness, especially in real-world designs. You should expect to review and improve various parts of the generated code, such as:</p>
<ul>
<li><p><strong>Spacing and alignment issues:</strong> AI may misinterpret padding, margins, or grid spacing, so layouts often need manual adjustment.</p>
</li>
<li><p><strong>Responsive breakpoints:</strong> Most tools generate layouts for a single screen size. You still need to add tablet/mobile breakpoints and test them across devices.</p>
</li>
<li><p><strong>Semantic HTML:</strong> AI tends to use too many <code>&lt;div&gt;</code> elements instead of meaningful tags like <code>&lt;header&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;section&gt;</code>, or <code>&lt;button&gt;</code>.</p>
</li>
<li><p><strong>Accessibility gaps:</strong> Missing alt text, improper labels, weak colour contrast choices, and a lack of ARIA(HTML Tags) attributes are common issues.</p>
</li>
<li><p><strong>Utility class overload (in Tailwind-based code):</strong> AI outputs long, repetitive class lists that need cleanup for maintainability.</p>
</li>
<li><p><strong>Inconsistent component structure:</strong> AI may generate components that aren’t reusable or follow naming conventions, so refactoring is often required.</p>
</li>
<li><p><strong>Tokens and theming:</strong> Colours, font sizes, and spacing may not map to design tokens unless manually corrected.</p>
</li>
</ul>
<p>Because of these limitations, AI-generated code should be treated as a <strong>starting point</strong>, not a final product. You’ll still need to validate the logic, refine the structure, and ensure the final result meets real-world quality standards. AI reduces repetitive work, but you, as the developer, need to ensure the code’s correctness, accessibility, and long-term maintainability.</p>
<h3 id="heading-what-i-prefer">What I Prefer</h3>
<p>The manual method gives you complete control and teaches you the fundamentals, which are essential for understanding how layouts, spacing, and components actually translate into real code.</p>
<p>When you write the HTML, CSS, and component structure yourself, you build a deeper understanding of how layouts, spacing, responsiveness, typography, and accessibility truly work under the hood. These fundamentals are what make you a strong frontend developer, and no automated tool can replace that learning.</p>
<p>AI-assisted design-to-code tools can help with speed – especially in the early stages when you just want a quick starting point. They remove some of the repetitive setup work, but the output almost always needs refinement.</p>
<p>In real projects, the most dependable workflow is a combination of both approaches: Use automation only for the boilerplate, then rely on your own frontend knowledge to clean up, reorganise, and fine-tune the interface so it meets real-world standards.</p>
<p><strong>Personally, I still prefer coding my designs manually because it keeps me connected to the craft and helps me build muscle memory.</strong> It’s the only way to fully understand how the design translates into a live, responsive interface and how each decision affects performance and user experience.</p>
<p>AI tools don’t replace frontend developers. They simply support them. They handle repetitive tasks so you can focus on the skills that truly matter: clarity of structure, accessibility, responsive behaviour, and crafting an experience that feels polished and intentional.</p>
<h2 id="heading-copywriting-for-your-portfolio">Copywriting for Your Portfolio</h2>
<p>Now that your portfolio is designed and coded, let’s turn our attention back to what it actually says.</p>
<p>Copywriting is the art of using words to convince people to take a specific action, like buying a product, signing up for a service, or visiting a website. It’s a mix of creativity and marketing, where words are used to sell ideas or products smartly and emotionally.</p>
<p>A copywriter writes many types of content, from sales pages, website text, and social media posts to ad scripts and emails. The goal is simple: to make people take action.</p>
<p>So what makes copywriting effective?</p>
<ul>
<li><p><strong>Knowing your audience:</strong> A good copywriter understands what people need, what problems they face, and what motivates them.</p>
</li>
<li><p><strong>Using persuasive words:</strong> Words that spark emotions and make readers want to act.</p>
</li>
<li><p><strong>Clarity and impact:</strong> Keeping the message short, clear, and easy to understand.</p>
</li>
<li><p><strong>Consistent brand voice:</strong> Writing in a tone that matches the brand’s personality — whether it’s fun, professional, or inspiring.</p>
</li>
</ul>
<h3 id="heading-how-to-use-copywriting-in-your-portfolio">How to Use Copywriting in Your Portfolio</h3>
<p>Your portfolio isn’t just about showing your work – it’s about telling your story in a way that connects with people. That’s where copywriting comes in. Good copy turns your portfolio from a plain showcase into something that feels personal, clear, and convincing.</p>
<p>Here’s how you can use copywriting effectively while building your portfolio:</p>
<h3 id="heading-1-start-with-a-strong-headline">1. Start With a Strong Headline</h3>
<p>The first line people see should instantly tell them <em>who you are</em> and <em>what you do</em>. For example:</p>
<blockquote>
<p>“I build modern, responsive web apps that turn ideas into digital reality.”</p>
</blockquote>
<p>Your headline is like your elevator pitch: short, powerful, and clear.</p>
<h3 id="heading-2-tell-your-story">2. Tell Your Story</h3>
<p>In the About Me section, don’t just list your skills. You should also tell visitors about your journey. Write about how you got started programming, what you’ve learned along the way, and what drives you. Keep it conversational and real, like you’re talking to a friend.</p>
<blockquote>
<p>“I started coding six years ago with simple HTML pages. Today, I design full-stack apps and help startups bring their ideas to life.”</p>
</blockquote>
<h3 id="heading-3-write-benefit-focused-descriptions">3. Write Benefit-Focused Descriptions</h3>
<p>When you show your projects, don’t just describe <em>what</em> you built. Explain <em>why</em> it matters.</p>
<blockquote>
<p>Instead of: “I built a task manager app.”<br>Try: “A simple, clutter-free task manager that helps users stay productive without distractions.”</p>
</blockquote>
<p>This small change turns your project into a <em>problem-solving story</em>, not just a tech demo.</p>
<h3 id="heading-4-add-a-call-to-action-cta">4. Add a Call-to-Action (CTA)</h3>
<p>Every portfolio should guide visitors toward an action, like contacting you, checking your GitHub, or reading your blog.</p>
<p>Here are some examples:</p>
<blockquote>
<p>“Want to collaborate? Let’s build something amazing together.”<br>“Looking for a developer who writes clean and efficient code? Reach out!”</p>
</blockquote>
<p>A clear CTA shows confidence and gives people a direction.</p>
<h3 id="heading-5-keep-it-simple-and-authentic">5. Keep It Simple and Authentic</h3>
<p>Avoid fancy words or buzzwords. Write like a human, not a brochure. Use simple, clear sentences that sound like your real voice.</p>
<p>Good copywriting is not about being clever – it’s about being <em>clear and honest</em>.</p>
<h3 id="heading-6-maintain-a-consistent-tone">6. Maintain a Consistent Tone</h3>
<p>Whether your style is formal or friendly, keep it the same across all pages – home, about, projects, and contact. A consistent tone helps build your personal brand and makes your portfolio feel professional.</p>
<p>Copywriting is the invisible thread that ties your portfolio together. It helps people not just <em>see</em> your work, but <em>feel</em> your story.</p>
<h2 id="heading-testing-benchmarks-for-a-portfolio-website">Testing Benchmarks for a Portfolio Website</h2>
<p>Before your portfolio goes live, it’s important to test it so that you know it looks and works as it’s supposed to. A fast, responsive, and accessible site leaves a strong first impression, and benchmarks help you measure whether it’s up to your standards. And a well-tested site loads faster, ranks better, and gives users (and recruiters) a seamless experience.</p>
<p>Below are the key benchmarks you should always check, along with why they matter.</p>
<h3 id="heading-1-page-load-time">1. Page Load Time</h3>
<p>This is the time it takes for your page to fully load after someone visits it. A fast-loading site feels smooth and professional, while a slow one instantly turns people away.</p>
<p>This matters because most visitors leave if a site takes more than 3 seconds to load. Tools like <a target="_blank" href="https://gtmetrix.com">GTmetrix</a> or Pingdom can help you track and improve page load speed by optimising images and reducing unnecessary scripts.</p>
<h3 id="heading-2-core-web-vitals-lcp-inp-cls">2. Core Web Vitals (LCP, INP, CLS)</h3>
<p>Google uses <strong>Core Web Vitals</strong> to measure real-world user experience.</p>
<ul>
<li><p><strong>LCP (Largest Contentful Paint):</strong> How quickly your main content becomes visible.</p>
</li>
<li><p><strong>INP (Interaction to Next Paint):</strong> How fast your site responds when users interact.</p>
</li>
<li><p><strong>CLS (Cumulative Layout Shift):</strong> How stable your layout is while loading.</p>
</li>
</ul>
<p>These metrics directly affect how users perceive your site and also impact SEO ranking. You can test them on <a target="_blank" href="https://pagespeed.web.dev">PageSpeed Insights.</a></p>
<h3 id="heading-3-mobile-friendliness">3. Mobile Friendliness</h3>
<p>Most people will view your portfolio on their phones, so it needs to look and work perfectly on smaller screens.</p>
<p>A mobile-optimised site not only improves user experience but also ranks better on Google’s mobile search results. Use responsive layouts and flexible grids to ensure smooth viewing on all devices.</p>
<h3 id="heading-4-accessibility-compliance-wcag">4. Accessibility Compliance (WCAG)</h3>
<p>Accessibility means making your portfolio usable for everyone, including people with disabilities.</p>
<p>This is important because following WCAG standards (like proper colour contrast, keyboard navigation, and alt text for images) shows professionalism and inclusivity. Tools like Lighthouse or WAVE can help you check how your portfolio ranks for key accessibility metrics.</p>
<h3 id="heading-5-seo-best-practices">5. SEO Best Practices</h3>
<p>SEO isn’t just for businesses. It can also help your personal site appear when someone searches your name or skills.</p>
<p>If you add proper meta tags, structured headings (H1, H2, H3), and descriptive URLs, these features can help recruiters or clients find you more easily. A well-optimised portfolio often performs better on job searches and tech blogs.</p>
<h3 id="heading-6-responsiveness-interaction-delay">6. Responsiveness (Interaction Delay)</h3>
<p>Responsiveness measures how quickly your website reacts when users click or scroll. And a laggy interface feels unprofessional and hurts user engagement. By minimising heavy scripts and optimising animations, you ensure smooth, immediate feedback for every action.</p>
<h3 id="heading-7-security-checks-https">7. Security Checks (HTTPS)</h3>
<p>Even a simple portfolio needs <strong>HTTPS</strong>. It builds trust and protects both you and your visitors from data breaches. Browsers like Chrome now flag non-secure sites, so enabling SSL is a must.</p>
<h3 id="heading-8-resource-size-html-css-js-images">8. Resource Size (HTML, CSS, JS, Images)</h3>
<p>Heavy resources can slow everything down. Compressing files, minifying CSS/JS, and using next-gen image formats (like WebP) can dramatically improve speed and performance.</p>
<h3 id="heading-9-browser-and-device-compatibilityhttpspagespeedwebdev">9. Browser and Device Compatibilit<a target="_blank" href="https://pagespeed.web.dev">y</a></h3>
<p>Not everyone uses Chrome on a laptop. So make sure you test your portfolio across major browsers (Chrome, Firefox, Safari, Edge) and devices (desktop, tablet, mobile). Simulated testing tools like DebugBear or Basemark Web help catch layout issues early.</p>
<h3 id="heading-10-real-user-monitoring-rum">10. Real User Monitoring (RUM)</h3>
<p>Instead of just testing in a lab, RUM captures how real visitors experience your site. This helps you understand performance in real-world scenarios – on different devices, networks, and locations – and adjust your design based on real data.</p>
<h2 id="heading-hosting-your-portfolio">Hosting Your Portfolio</h2>
<p>Now it’s time to push your changes to GitHub and deploy them on a hosting provider of your choice. Hosting services play a crucial role in showcasing your work. Depending on the type of projects you build, here are some of the best free hosting options that I have used so far for my personal and professional projects.</p>
<ul>
<li><p><strong>Vercel</strong> – Best for Next.js/React projects, offering seamless deployment inside the Vercel ecosystem.</p>
</li>
<li><p><strong>GitHub Pages</strong> – Great for hosting static websites and personal portfolios.</p>
</li>
<li><p><strong>Netlify</strong> – Ideal for frontend-heavy projects with easy deployment and CI/CD integration.</p>
</li>
</ul>
<p>Your portfolio is ready, and you can share it anywhere with just a link.</p>
<h2 id="heading-how-to-use-your-portfolio-effectively">How to Use Your Portfolio Effectively</h2>
<p>Now that your portfolio is ready, it’s time to use it the right way. Whether you’re applying for a job, pitching to a freelance client, or simply networking with like-minded people, your portfolio is your strongest asset.</p>
<p>Think of it as your digital introduction. It not only shows what you’ve built but also how you think and work. The key is to use it smartly in the right context.</p>
<p>Let’s say you come across a job post or want to reach out directly to a recruiter or a company founder. Instead of just sending a plain résumé, you can make your message stand out by attaching your portfolio link. Here’s an example of how you can do it:</p>
<p>Example email/direct message:</p>
<blockquote>
<p><strong><em>Hi [Hiring Manager/CEO Name],</em></strong></p>
<p><strong><em>I’m Prankur, a Full Stack Developer from India with over 6 years of experience building mobile and web applications.</em></strong></p>
<p><strong><em>I came across the opening for [Job Title/Role] at your company and believe my skills align perfectly with what you’re looking for.</em></strong></p>
<p><strong><em>You can explore my work here: [Portfolio URL]</em></strong></p>
<p><strong><em>I’m available to start immediately and excited about the opportunity to contribute to your team and codebase.</em></strong></p>
<p><strong><em>Best regards,</em></strong></p>
<p><strong><em>Prankur Pandey</em></strong></p>
</blockquote>
<p>This small but professional touch – including your portfolio link in every email, proposal, or LinkedIn message – increases your chances of being noticed. A well-presented portfolio speaks louder than a résumé, and it helps recruiters or clients quickly understand your skills, design sense, and coding depth.</p>
<p>So, don’t just build your portfolio. <strong>Use it actively</strong>. Share it on job boards, LinkedIn posts, your GitHub bio, or even in casual conversations with potential collaborators. Every share is a new opportunity waiting to unfold.</p>
<h2 id="heading-qampa">Q&amp;A</h2>
<p><strong>1. Is building a portfolio really important?</strong><br>Absolutely. Instead of sending out a dozen links to your GitHub, Behance, and LinkedIn, it’s much more effective to combine all your work into one clean, accessible portfolio site. It makes you look organised, professional, and easy to evaluate.</p>
<p><strong>2. Is it worth buying a domain name?</strong><br>Yes, it is, if you’re serious about your career. Having your own domain (like <a target="_blank" href="http://yourname.dev"><em>yourname.dev</em></a> or <a target="_blank" href="http://yourname.com"><em>yourname.com</em></a>) gives a personal and professional touch,</p>
<p>But if you’re still learning or just experimenting, it’s fine to start with a free domain. Once you have solid projects to show, investing in your own domain is totally worth it.</p>
<p><strong>3. Can I use AI or no-code tools to build my portfolio?</strong><br>You can, but if you’re a developer, you should try coding it yourself first. It’s a great way to showcase your creativity and technical control over design and logic. Here’s a <a target="_blank" href="https://www.freecodecamp.org/news/build-a-simple-portfolio-website-with-html-and-css/">simple and straightforward guide</a> to help you get started with that.</p>
<p>Once your design is ready, you can always use AI or no-code tools to speed up the process or automate parts of it. Think of AI as a helper, not a replacement.</p>
<p><strong>4. How can I get AI tools for testing?</strong><br>It’s easy: visit the official websites of the tools you’re interested in and sign up. Most AI tools offer free trials or limited credits that you can use to explore and test their features based on your workflow and needs.</p>
<p><strong>5. I am just starting. What should I use: AI tools or the Manual Method?</strong><br>I strongly recommend using the Manual Method as it will help you to understand your craft well, and you will also build muscle memory about a technology and how it works.</p>
<p><strong>6. Which Platform should I use for hosting?</strong><br>Most of the hosting providers offer a free plan, so you can start with that. Then, when you feel the need to expand your portfolio, switch to a paid plan.</p>
<h2 id="heading-final-notes">Final Notes</h2>
<p>Building a strong portfolio requires time, effort, and attention to detail. But it’s one of the smartest investments you can make in your tech career.</p>
<p>Tools and templates can help speed up the process, but your creativity, skills, and storytelling are what truly make a portfolio stand out. A well-crafted portfolio not only shows what you can build, it reveals how you think and why your work matters.</p>
<p>While you can use design tools, frameworks, or even AI assistance to save time, make sure you understand the basics of design, structure, and usability. The goal isn’t to create something flashy; it’s to produce something clear, professional, and authentic.</p>
<p>Your portfolio is your digital identity, so treat it like your personal brand. Keep refining it as your skills grow and let it evolve alongside your career.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>A portfolio isn’t just a website. It’s your story told through your work. It helps employers, clients, and collaborators understand what you do best and why you stand out.</p>
<p>In this article, I’ve shared everything that helped me build and refine my own portfolio, from understanding design structure and copywriting to testing and optimisation benchmarks. My goal is to help you create a portfolio that not only looks great but also opens real opportunities for you.</p>
<p>In my next tutorial, I’ll explore something new for sure.</p>
<p><strong>Design + Creativity+Development + Execution = The Ultimate Developer Stack 🔥</strong></p>
<p>Keep learning, keep building, and most importantly — keep sharing your work.</p>
<p>If you found this article useful, feel free to let me know. I’m always open to learning, collaboration, and new opportunities.</p>
<p>Now it's your turn: what are you building next? Let me know by sending me a DM!</p>
<ul>
<li><p>Follow me on <strong>𝕏</strong>: <a target="_blank" href="https://x.com/prankurpandeyy">Prankur's</a> <strong>𝕏</strong></p>
</li>
<li><p>Connect with me on LinkedIn: <a target="_blank" href="https://linkedin.com/in/prankurpandeyy">Prankur's LinkedIn</a></p>
</li>
<li><p>Follow me on GitHub: <a target="_blank" href="https://github.com/prankurpandeyy">Prankur’s GitHub</a></p>
</li>
<li><p>View my Portfolio: <a target="_blank" href="https://prankurpandeyy.is-a.dev/">Prankur's Portfolio</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How To Build A Simple Portfolio Blog With Next.js ]]>
                </title>
                <description>
                    <![CDATA[ I have written articles on different platforms including LinkedIn, The Network Bits (Substack), and freeCodeCamp. So I wanted to bring all of these articles together in a single place where someone could go and see all my work. A blog sounded like a ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-simple-portfolio-blog-with-nextjs/</link>
                <guid isPermaLink="false">6839c439ebab1f7974eac355</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Blogging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Chidiadi Anyanwu ]]>
                </dc:creator>
                <pubDate>Fri, 30 May 2025 14:44:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747235586248/7424bce0-24da-4f70-a5aa-31249d799094.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I have written articles on different platforms including LinkedIn, The Network Bits (Substack), and freeCodeCamp. So I wanted to bring all of these articles together in a single place where someone could go and see all my work.</p>
<p>A blog sounded like a good solution for this, so I set out to build one. In this article, I will walk you through how I did it with Next.js.</p>
<p>The basic idea here was to build a website where I wouldn’t need to write code in the future. I just wanted to be able to add the URL of a new article to a JSON file, and the website would extract information like the title, date, cover image, and description and then update itself with it. No database.</p>
<p>To understand how I would go about it, I checked the metadata of the HTML text from each of the platforms I considered. I used my articles, of course, like the one in the project folder. I found out that most of them used Open Graph metadata. So, that was easy to scrape. But, I also found out that some information wasn’t in the meta tags – instead, it was in the JSON-LD. At the end of the day, I ended up using both in my functions.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-the-blog-site-works">How the Blog Site Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-does-an-article-look-like">The Structure of an Article on the Blog</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-the-search-feature-work">How the Search Feature Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-structure">The Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-steps-to-build-the-blog">Steps to Build the Blog</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<p>Understanding this article requires some knowledge of programming and web development. You need to have basic knowledge of HTTP, HTML, CSS, JavaScript, and React to be able to follow along easily.</p>
<p>If you don't have those skills, you may still be able to understand the general structure and working principles.</p>
<h2 id="heading-how-the-blog-site-works">How the Blog Site Works</h2>
<p>The project consists of client components and server components. It is a website, so ideally, it's just a front-end. But it has to fetch data from URLs – and doing that from the client-side won’t work due to CORS blocking, as the requests will be emanating from a browser. So, it has to run on the server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748524048812/76baf11d-a80a-4d07-beba-065c74536541.png" alt="Home page calling the fetch articles function." class="image--center mx-auto" width="774" height="244" loading="lazy"></p>
<p>The <code>fetchArticles()</code> function runs on the server – then this happens:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748536836320/9813a669-ac07-480a-8270-6f2f36ceda22.png" alt="Rough flowchart showing what happens in the fetchArticles function" class="image--center mx-auto" width="501" height="577" loading="lazy"></p>
<p>The <code>fetchArticles()</code> function accesses the URLs, extracts and processes the HTML and JSON Linked Data objects from the response, and returns an array of Article objects to the Home page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748536952680/35fec49a-6c44-4978-9acb-755eb5ed810f.png" alt="fetch articles function gets called and returns an array of Article objects" class="image--center mx-auto" width="768" height="289" loading="lazy"></p>
<p>The <code>HomePage</code> component is a client side component that has another component in it, named <code>HomeClient</code>. This <code>HomeClient</code> is a client side component. It has to be because it has useState hooks.</p>
<p>But the <code>HomePage</code> component calls the <code>fetchArticles()</code> function and sets the <code>articles</code> constant (which is an array of <code>Article</code> objects, as defined by the interface in the <code>ArticleCard.tsx</code> file). The <code>articles</code> constant is then passed down to the <code>HomeClient</code> component as a prop.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881308147/1fedcbb0-f9d4-47dd-afea-b7f231595a58.png" alt="The HomePage component, and its child component, HomeClient." class="image--center mx-auto" width="411" height="441" loading="lazy"></p>
<p>Inside the <code>HomeClient</code> component, there are two components – the <code>Hero</code> component, and the <code>MainBody</code> component. The Hero component shows the welcome message, and also has the search bar. The MainBody component is where the tags and the article grid are. Logic for filtering articles are also in the MainBody component.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881333468/267d0158-df41-4a1d-8098-e3219fe7db4d.png" alt="The Hero and MainBody components inside the HomeClient component." class="image--center mx-auto" width="411" height="441" loading="lazy"></p>
<p>Inside the MainBody component, there is the <code>ArticleCard</code> component that takes the filtered array of Article objects from the MainBody as props, and renders an article card for each. These cards are rendered inside the grid in the MainBody component.</p>
<h3 id="heading-what-does-an-article-look-like">What does an article look like?</h3>
<p>The articles are defined by an interface:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Article {
  id: <span class="hljs-built_in">number</span>;
  title?: <span class="hljs-built_in">string</span>;
  description?: <span class="hljs-built_in">string</span>;
  publishedDate?: <span class="hljs-built_in">string</span>;
  url: <span class="hljs-built_in">string</span>;
  imgUrl?: <span class="hljs-built_in">string</span>;
  siteName?: <span class="hljs-built_in">string</span>;
  tags?: <span class="hljs-built_in">string</span>[];
}
</code></pre>
<p>The interface, as shown above, specifies that the object will have eight properties, of which only the <code>id</code> and <code>url</code> are compulsory. Those compulsory properties are actually what’s needed in the JSON file from which the web server will read.</p>
<p>When the URL is visited by the server, the title, description, and other properties (except the tags) are obtained automatically and populated. Then the object is created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881670519/a2cbdbbf-6cfd-40e7-91e5-5da711198dc7.png" alt="Article card renderings" class="image--center mx-auto" width="1342" height="585" loading="lazy"></p>
<p>The article cards consist of the article’s cover image, the name of the platform where it was published, the date published, the title, and a description. All of this is wrapped in an anchor linking to the URL. The tags are not visible on the cards, but are used in filtering operations.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881715907/7ab4ad87-7da6-44e2-8329-35bcb999b825.png" alt="Array of Article objects in browser console." class="image--center mx-auto" width="787" height="318" loading="lazy"></p>
<h3 id="heading-how-does-the-search-feature-work">How does the search feature work?</h3>
<p>There's a reason why the Hero component and the MainBody component are in the same parent component. That wasn't my initial design, but after I saw that the search bar would look better in the Hero component, and that I needed to set the <code>searchTerm</code> state in the Hero component and use it in the MainBody component, that became the best option for me: to put both of them in the same parent, so I could pass down the useState hook as props into both of them.</p>
<p>The search feature works basically by filtering the <code>articles</code> array based on the tags selected, or the search term entered. Here is what the code looks like:</p>
<pre><code class="lang-typescript"> useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> anyTagActive = isActive.some(<span class="hljs-function">(<span class="hljs-params">val</span>) =&gt;</span> val);

    <span class="hljs-keyword">const</span> filtered = articles.filter(<span class="hljs-function">(<span class="hljs-params">article</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Search term: '</span> + searchTerm || <span class="hljs-string">'searchTerm'</span>);
      <span class="hljs-keyword">const</span> searchMatch =
        article.title?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.description?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> tag.toLowerCase().includes(searchTerm.toLowerCase())) ||
        article.siteName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.publishedDate?.toLowerCase().includes(searchTerm.toLowerCase());


        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'This is the searchMarch: '</span> + searchMatch || <span class="hljs-string">'FALSE searchMatch'</span>);
        <span class="hljs-built_in">console</span>.log(article.title || <span class="hljs-string">'article.title no wan show'</span>);

      <span class="hljs-keyword">const</span> tagMatch = article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> index = tags.indexOf(tag);
        <span class="hljs-keyword">return</span> index !== <span class="hljs-number">-1</span> &amp;&amp; isActive[index];
      }) || <span class="hljs-literal">false</span>;

      <span class="hljs-keyword">if</span> (anyTagActive) {
        <span class="hljs-keyword">return</span> tagMatch &amp;&amp; searchMatch; <span class="hljs-comment">// Only return articles if tag is active and search matches</span>
      }

      <span class="hljs-keyword">return</span> searchMatch; <span class="hljs-comment">// If no tags active, return all that match the search term</span>
    });

    setFilteredArticles(filtered);
  }, [articles, searchTerm, isActive]);
</code></pre>
<p>Here, we use a <code>useEffect()</code> hook to monitor for changes in the <code>articles</code>, <code>searchTerm</code>, and <code>isActive</code> constants. <code>isActive</code> is a <code>useState()</code> hook that has an array of boolean values the length of the tags array.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [isActive, setIsActive] = useState(tags.map(<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">false</span>));
</code></pre>
<p>Here, the <code>filtered</code> constant is equal to the filtered values of <code>articles</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> filtered = articles.filter();
</code></pre>
<p>Inside the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter">filter method</a> is where the arrow function with the logic for filtering is written – <code>(article) =&gt; {//logic}</code>. We have two constants: <code>tagMatch</code> and <code>searchMatch</code>. The <code>searchMatch</code> constant is true when the title, description, tags, site name, or published date includes the search term. Else, it's false. The <code>tagMatch</code> constant is true when any tag from the article's array of tag is present in the tag list, and also has a corresponding <code>isActive</code> value of true.</p>
<p>If any tag at all is active, then the results for both <code>tagMatch</code> and <code>searchMatch</code> are returned, but if no tag at all is active, then only the <code>searchMatch</code> is returned as true.</p>
<p>The filtered article list is what is then passed into the <code>ArticleCard</code> component.</p>
<pre><code class="lang-typescript">&lt;ArticleCard articles={filteredArticles} /&gt;
</code></pre>
<h2 id="heading-project-structure">Project Structure</h2>
<p>This is what the project file structure looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1748605144895/e56eea92-8851-4717-ae82-0e15f70dc31f.png" alt="e56eea92-8851-4717-ae82-0e15f70dc31f" class="image--center mx-auto" width="404" height="692" loading="lazy"></p>
<p>At the root, we have the config files and <code>node_modules</code> which is not displayed here. The <code>public</code> folder holds all the images and icons. Then, in the <code>src</code> folder, we have <code>app</code>, <code>component</code>, and <code>utils</code>.</p>
<p>The <code>components</code> folder holds the files for the components – the nav bar, footer, hero, main body and article card. The <code>utils</code> folder has all the functions that run in the background and do not need to render anything. The <code>fetchArticles</code> function is there, along with other functions for extracting the date published, title, description, image URL, and others from HTTP responses gotten from the article URLs. The <code>app</code> folder has the favicon, the global CSS stylesheet, the <code>page</code> and <code>layout</code> files, <code>articles.json</code> which is the JSON file where I add new article URLs for rendering, a test HTML file (wsl.html), and the <code>about/</code> and <code>api/</code> directories.</p>
<p>Inside the about folder, we have the about page, and inside the API folder, we have tthe folder, <code>metadata-local-test</code> which is no longer relevant to the project. I used it initially to create an internal API to fetch from the URLs. But I later restructured the codebase.</p>
<h2 id="heading-steps-to-build-the-blog">Steps to Build the Blog</h2>
<h3 id="heading-1-install-nextjs">1. Install Next.js</h3>
<p>To install Next.js, navigate to the folder where you want the project to reside and open that location in your terminal. Then type the following:</p>
<pre><code class="lang-bash">npx create-next-app@latest
</code></pre>
<p>You're going to be met with the following prompts:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746881930473/228a02f7-6571-46f0-9503-d4606e19bd10.png" alt="Installing Next.js" class="image--center mx-auto" width="866" height="217" loading="lazy"></p>
<h3 id="heading-2-navigate-to-your-newly-created-project-folder-and-install-dependencies">2. Navigate to your newly created project folder and install dependencies</h3>
<p>In the newly created project folder, run the project in development mode to preview your newly created Next project. You will be shown a message directing you to localhost on port 3000. Now, it's time for us to start creating what we want.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882046144/df46e975-14f2-4dc9-81d7-5bbb9a344f7b.png" alt="Success! Navigating to the project directory." class="image--center mx-auto" width="866" height="90" loading="lazy"></p>
<p>Now, one more thing you'll need to do. In the project, I used lucide-react to get one of the icons, and cheerio to extract data from the HTML. So, you’ll need to install those dependencies.</p>
<p>To install lucide-react, use this command in the project folder:</p>
<pre><code class="lang-bash">npm install lucide-react
</code></pre>
<p>Then install cheerio:</p>
<pre><code class="lang-bash">npm install cheerio
</code></pre>
<h3 id="heading-3-change-the-title-and-description-in-the-page-metadata">3. Change the title and description in the page metadata</h3>
<p>The title is what shows up at the top of your browser tab when you open up the website. Right now, it should be showing 'Create Next App.' We don't want that.</p>
<p>Since this is not just HTML, there is no <code>index.html</code> to change the title in the header element. Rather, Next.js provides us a <code>Metadata</code> object we can use to change things like that. And it'll be in the <code>layout.tsx</code> file in the <code>app</code> or <code>src</code> folder. Head over there and change it to whatever you want the title to be. I’m using “Chidiadi Portfolio Blog”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882205569/b652a7de-00b7-4f0d-943c-4f80a62a7f91.png" alt="Changing metadata" width="427" height="107" loading="lazy"></p>
<h3 id="heading-4-create-the-necessary-components">4. Create the necessary components</h3>
<p>Navigate to the side panel, and under the <code>src</code> folder, create a components folder. This is where the components will live. Here, create the article card, footer, main body and nav bar.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882335350/69d1f7ed-ea67-47e7-8b0a-e25429190a3e.png" alt="Components folder" width="359" height="86" loading="lazy"></p>
<p>For the <strong>Navbar</strong>, this is the code:</p>
<pre><code class="lang-typescript"><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">Navbar</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">return</span>(
        &lt;&gt;
        &lt;div className=<span class="hljs-string">"text-3xl md:text-base flex w-[100vw] md:w-[98.2vw] lg:w-[98.8vw] h-[60px] bg-black text-white px-0 md:px-7 md:py-2 items-center justify-center md:justify-between"</span>&gt;
            &lt;h1 className=<span class="hljs-string">"font-bold"</span>&gt;CHIDIADI   ANYANWU&lt;/h1&gt;
            &lt;div className=<span class="hljs-string">"hidden md:block flex space-x-4"</span>&gt;
                &lt;a href=<span class="hljs-string">"/"</span> className=<span class="hljs-string">"hover:text-gray-400"</span>&gt;Blog&lt;/a&gt;
                &lt;a href=<span class="hljs-string">"/about"</span> className=<span class="hljs-string">"hover:text-gray-400"</span>&gt;About&lt;/a&gt;    
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;/&gt;
    );
}
</code></pre>
<p>Here's what the <strong>Hero</strong> component looks like:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { Search } <span class="hljs-keyword">from</span> <span class="hljs-string">'lucide-react'</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">interface</span> HeroProps {
    searchTerm: <span class="hljs-built_in">string</span>;
    setSearchTerm: React.Dispatch&lt;React.SetStateAction&lt;<span class="hljs-built_in">string</span>&gt;&gt;;
  }
<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">Hero</span>(<span class="hljs-params">{ searchTerm, setSearchTerm }: HeroProps</span>) </span>{
    <span class="hljs-keyword">const</span> [buttonColor, setButtonColor] = useState(<span class="hljs-string">''</span>);

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"bg-[url('/img-one-1.jpg')] bg-cover bg-center bg-no-repeat flex flex-col items-center justify-center h-[400px] relative"</span>&gt;
           &lt;div className=<span class="hljs-string">" absolute inset-0 bg-black opacity-60"</span>&gt;&lt;/div&gt; 
            &lt;h1 className=<span class="hljs-string">"text-4xl text-white font-bold text-center z-10"</span>&gt;My Portfolio Blog&lt;/h1&gt;
            &lt;p className=<span class="hljs-string">"mt-4 mx-4 text-xlarge text-white md:text-xl text-justify md:text-center z-10"</span> style={{ fontFamily: <span class="hljs-string">"Cormorant Garamond"</span> }}&gt;
                My name is Chidiadi Anyanwu. I am a technical writer <span class="hljs-keyword">with</span> a strong background <span class="hljs-keyword">in</span> networking. 
                I write about Networking, Cloud, DevOps, and even sometimes web development like <span class="hljs-built_in">this</span> one. I built <span class="hljs-built_in">this</span>
                website <span class="hljs-keyword">with</span> Next.js, and there<span class="hljs-string">'s also an &lt;a href="/" className="text-blue-500 hover:text-blue-700 hover:underline"&gt;article about that.&lt;/a&gt;
                  This website holds my technical articles in one place. It is a repository of my written works.
            &lt;/p&gt;
            &lt;div id="searchbar" className="h-9xl mt-4 flex align-items-center justify-center w-full" &gt;

                &lt;form onSubmit={(e) =&gt; {e.preventDefault();  setSearchTerm(searchTerm);}} className="group mt-4 relative w-[70%] md:w-[50%]"&gt;
                    &lt;input  value={searchTerm} onChange={(e) =&gt; setSearchTerm(e.target.value) } onFocus={()=&gt;{setButtonColor('</span>bg-blue<span class="hljs-number">-500</span><span class="hljs-string">'); console.log('</span>input focused<span class="hljs-string">')}} onBlur={()=&gt;{setButtonColor('</span><span class="hljs-string">');}}type="search" placeholder="Search Chidiadi'</span>s articles<span class="hljs-string">" className="</span>h-[<span class="hljs-number">50</span>px] w-full px-[<span class="hljs-number">48</span>px] border<span class="hljs-number">-3</span> border-blue<span class="hljs-number">-300</span> rounded-[<span class="hljs-number">25</span>px] focus:outline-none focus:border-blue<span class="hljs-number">-500</span> text-black bg-white<span class="hljs-string">"/&gt;
                    &lt;button className={`h-[42px] w-[42px] absolute right-0 mr-1.5 mt-1 rounded-[50%] bg-blue-300 ${buttonColor}`}&gt;
                        &lt;Search  className='m-auto text-white'/&gt;
                    &lt;/button&gt;
                &lt;/form&gt;

            &lt;/div&gt;
        &lt;/div&gt;
    );
}</span>
</code></pre>
<p>In this file, we created the HeroProps interface to accept the search props. Then we deconstructed both <code>searchTerm</code> and <code>setSearchTerm</code> from it as props to the Hero component. We’ll make it a client component <code>'use client'</code> because of the buttonColor <code>useState()</code> hook that changes when the search bar is clicked and sets the search button background color.</p>
<p>The <strong>MainBody</strong> component looks like this:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> ArticleCard, { Article } <span class="hljs-keyword">from</span> <span class="hljs-string">'./ArticleCard'</span>;

<span class="hljs-keyword">interface</span> MainBodyProps {
  searchTerm: <span class="hljs-built_in">string</span>;
  articles: Article[];
}

<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">MainBody</span>(<span class="hljs-params">{ searchTerm, articles }: MainBodyProps</span>) </span>{
  <span class="hljs-comment">// Get articles from JSON file and create array of article objects</span>

  <span class="hljs-keyword">const</span> [filteredArticles, setFilteredArticles] = useState&lt;Article[]&gt;([]);

  <span class="hljs-keyword">const</span> tags = [<span class="hljs-string">"Networking"</span>, <span class="hljs-string">"Cloud"</span>, <span class="hljs-string">"DevOps"</span>, <span class="hljs-string">"Web Dev"</span>, <span class="hljs-string">"Cybersecurity"</span>];
  <span class="hljs-keyword">const</span> [isActive, setIsActive] = useState(tags.map(<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">false</span>));


  <span class="hljs-comment">// Filter articles based on search term and active tags</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> anyTagActive = isActive.some(<span class="hljs-function">(<span class="hljs-params">val</span>) =&gt;</span> val);

    <span class="hljs-keyword">const</span> filtered = articles.filter(<span class="hljs-function">(<span class="hljs-params">article</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Search term: '</span> + searchTerm || <span class="hljs-string">'searchTerm'</span>);
      <span class="hljs-keyword">const</span> searchMatch =
        article.title?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.description?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> tag.toLowerCase().includes(searchTerm.toLowerCase())) ||
        article.siteName?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        article.publishedDate?.toLowerCase().includes(searchTerm.toLowerCase());


        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'This is the searchMarch: '</span> + searchMatch || <span class="hljs-string">'FALSE searchMatch'</span>);
        <span class="hljs-built_in">console</span>.log(article.title || <span class="hljs-string">'article.title no wan show'</span>);

      <span class="hljs-keyword">const</span> tagMatch = article.tags?.some(<span class="hljs-function">(<span class="hljs-params">tag</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> index = tags.indexOf(tag);
        <span class="hljs-keyword">return</span> index !== <span class="hljs-number">-1</span> &amp;&amp; isActive[index];
      }) || <span class="hljs-literal">false</span>;

      <span class="hljs-keyword">if</span> (anyTagActive) {
        <span class="hljs-keyword">return</span> tagMatch &amp;&amp; searchMatch; <span class="hljs-comment">// Only return articles if tag is active and search matches</span>
      }

      <span class="hljs-keyword">return</span> searchMatch; <span class="hljs-comment">// If no tags active, return all that match the search term</span>
    });

    setFilteredArticles(filtered);
  }, [articles, searchTerm, isActive]); 

  <span class="hljs-built_in">console</span>.log(filteredArticles);

  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">'scroll-smooth'</span>&gt;
      &lt;div id=<span class="hljs-string">"tags"</span> className=<span class="hljs-string">"flex w-full h-[200px] md:h-[60px] justify-center gap-5 py-4 flex-wrap max-w-[100vw] scroll-smooth"</span>&gt;
        {tags.map(<span class="hljs-function">(<span class="hljs-params">tag, index</span>) =&gt;</span> (
          &lt;p
            key={index}
            onClick={<span class="hljs-function">() =&gt;</span> {
              <span class="hljs-keyword">const</span> newIsActive = [...isActive];
              newIsActive[index] = !newIsActive[index];
              setIsActive(newIsActive);
            }}
            className={<span class="hljs-string">`h-[48px] w-[140px] border-3 rounded-[40px] px-2 py-2 text-center font-bold <span class="hljs-subst">${
              isActive[index]
                ? <span class="hljs-string">'bg-black border-black text-white hover:bg-gray-700 hover:border-gray-700'</span>
                : <span class="hljs-string">'border-blue-500 hover:bg-blue-500 hover:text-white'</span>
            }</span>`</span>}&gt;
            {tag}
          &lt;/p&gt;
        ))}
      &lt;/div&gt;

      &lt;div id=<span class="hljs-string">"articlegrid"</span> className=<span class="hljs-string">"w-[100vw] md:w-[98vw] grid gap-2 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 mt-5 px-3 py-3"</span>&gt;
        &lt;ArticleCard articles={filteredArticles} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Here, we have props from the parent component too, but we only need the articles fetched and the search term. We don't need to set the fetch term from this component.</p>
<p>To render the tags, I first created the array of tags and an array of boolean values to record the states of the tags (whether they're active or inactive).</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> tags = [<span class="hljs-string">"Networking"</span>, <span class="hljs-string">"Cloud"</span>, <span class="hljs-string">"DevOps"</span>, <span class="hljs-string">"Web Dev"</span>, <span class="hljs-string">"Cybersecurity"</span>];
<span class="hljs-keyword">const</span> [isActive, setIsActive] = useState(tags.map(<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">false</span>));
</code></pre>
<p>Then, inside the return statement, I mapped through the tag array to render them one by one. The onClick event handler also works here to make sure that the <code>isActive</code> state for that particular tag is toggled when it is clicked.</p>
<p>So how does this work? It creates a new array called <code>newIsActive</code> that is a copy of the <code>isActive</code> array. It then gets the particular tag by index number and inverts it. Then it sets the <code>isActive</code> array to this new array.</p>
<pre><code class="lang-typescript">{tags.map(<span class="hljs-function">(<span class="hljs-params">tag, index</span>) =&gt;</span> (
          &lt;p
            key={index}
            onClick={<span class="hljs-function">() =&gt;</span> {
              <span class="hljs-keyword">const</span> newIsActive = [...isActive];
              newIsActive[index] = !newIsActive[index];
              setIsActive(newIsActive);
            }} . . .
</code></pre>
<p>This is the code for the <strong>ArticleCard</strong>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Image  <span class="hljs-keyword">from</span> <span class="hljs-string">'next/image'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Article {
  id: <span class="hljs-built_in">number</span>;
  title?: <span class="hljs-built_in">string</span>;
  description?: <span class="hljs-built_in">string</span>;
  publishedDate?: <span class="hljs-built_in">string</span>;
  url: <span class="hljs-built_in">string</span>;
  imgUrl?: <span class="hljs-built_in">string</span>;
  siteName?: <span class="hljs-built_in">string</span>;
  tags?: <span class="hljs-built_in">string</span>[];
}

<span class="hljs-keyword">interface</span> ArticleProps {
  articles: Article[];
}

<span class="hljs-keyword">const</span> ArticleCard = <span class="hljs-function">(<span class="hljs-params">{ articles }: ArticleProps</span>) =&gt;</span> {

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
    {articles ?

      (articles.map(<span class="hljs-function">(<span class="hljs-params">item, id</span>) =&gt;</span> (
        <span class="hljs-comment">//anchor tag for the link</span>
        &lt;a key={id}  href={item.url} className=<span class="hljs-string">'max-w-[350px] mx-auto mb-5'</span>&gt;
            &lt;div className=<span class="hljs-string">"sm:w-[350px] hover:brightness-70"</span> data-title={item.title} data-description={item.description} data-published-date={item.publishedDate} data-tag=<span class="hljs-string">"Networking"</span> data-site-name={item.siteName}&gt;
            &lt;Image
              src={item.imgUrl || <span class="hljs-string">'/img-2.jpg'</span>} 
              alt={item.title || <span class="hljs-string">'Article Image'</span>}
              width={<span class="hljs-number">350</span>}
              height={<span class="hljs-number">400</span>}
              className=<span class="hljs-string">"object-cover rounded-[10px]"</span>
            /&gt;
            &lt;div className=<span class="hljs-string">"flex h-[43px] text-[14px] text-gray-500 gap-2"</span>&gt;
                &lt;p id=<span class="hljs-string">"Platform"</span> className=<span class="hljs-string">"py-2 h-[42px] md:text-sm mt-auto mb-auto"</span>&gt;{item.siteName}&lt;/p&gt;
                &lt;div className=<span class="hljs-string">"h-1 w-1 bg-black rounded-full mt-auto mb-auto bg-gray-500"</span>&gt;&lt;/div&gt;
                &lt;p id=<span class="hljs-string">"publishedDate"</span> className=<span class="hljs-string">"py-2 h-[42px] mt-auto mb-auto"</span>&gt;{item.publishedDate}&lt;/p&gt;
            &lt;/div&gt;
            &lt;h1 id=<span class="hljs-string">"titleOfArticle"</span> className=<span class="hljs-string">"font-bold text-base md:text-3xl"</span>&gt;{item.title}&lt;/h1&gt;
            &lt;br/&gt;
            &lt;p className=<span class="hljs-string">'w-full md:w-[350px]'</span>&gt;{item.description}&lt;/p&gt;
            &lt;/div&gt;
        &lt;/a&gt;
      )))
      :

      ( <span class="hljs-built_in">Array</span>(<span class="hljs-number">6</span>).fill(<span class="hljs-number">0</span>).map(<span class="hljs-function">(<span class="hljs-params">item, id</span>) =&gt;</span> (
        &lt;div key={id} className=<span class="hljs-string">"w-full md:w-[350px] h-[350px] bg-gray-500 mx-auto mb-5 hover:brightness-80 rounded-[10px] animate-pulse"</span>&gt;&lt;/div&gt;
      )))
    }
    &lt;/&gt;
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ArticleCard;
</code></pre>
<p>Here, we defined and exported the <code>Article</code> interface so that we can create <code>Article</code> objects in the <code>MainBody</code>. Then, we created an interface to pass down the props of an array of <code>Article</code> objects.</p>
<p>Next, there's this part to ensure it renders something even if for some reason no Article object was passed:</p>
<pre><code class="lang-typescript">{
    article?
    ( {<span class="hljs-comment">/*If article exists, render this*/</span>} )
    :
    ( {<span class="hljs-comment">/*Else, render this */</span>} )
}
</code></pre>
<p>Our fail-safe here is an empty array of six objects with the Tailwind <code>animate-pulse</code>:</p>
<pre><code class="lang-typescript"> ( <span class="hljs-built_in">Array</span>(<span class="hljs-number">6</span>).fill(<span class="hljs-number">0</span>).map(<span class="hljs-function">(<span class="hljs-params">item, id</span>) =&gt;</span> (
        &lt;div key={id} className=<span class="hljs-string">"w-full md:w-[350px] h-[350px] bg-gray-500 mx-auto mb-5 hover:brightness-80 rounded-[10px] animate-pulse"</span>&gt;&lt;/div&gt;
      )))
</code></pre>
<p>I could have made this part much better, but I was feeling a little lazy. I also used the <code>Image</code> from Next, instead of the regular <code>img</code>. This requires that you edit the <code>next.config.ts</code> file. I had to go add all the paths that the images could possibly be loaded from:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882422600/e3ad4762-1199-4276-a524-d27519a37c52.png" alt="next.config.ts" class="image--center mx-auto" width="742" height="632" loading="lazy"></p>
<p>Just like in the screenshot above, the syntax is:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { NextConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;

<span class="hljs-keyword">const</span> nextConfig: NextConfig = {
  images: {
    remotePatterns: [
        {
            protocol:<span class="hljs-string">"https"</span>,
            hostname:<span class="hljs-string">"licdn.com"</span>,
            pathname:<span class="hljs-string">"/**"</span>
        },
        {
            protocol:<span class="hljs-string">""</span>,
            hostname:<span class="hljs-string">""</span>,
            pathname:<span class="hljs-string">""</span>
        }
        ],
    },
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> nextConfig;
</code></pre>
<p>It takes a <code>remotePatterns</code> array that consists of remote pattern objects, which have a protocol, hostname, and pathname property. Make sure the protocol and hostname properties are not empty like in the second object in the code sample above. That would cause errors. It’s either the objects are populated properly or they’re deleted.</p>
<p>The <strong>Footer</strong> looks like this:</p>
<pre><code class="lang-typescript"><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">Footer</span>(<span class="hljs-params"></span>) </span>{

    <span class="hljs-keyword">return</span> (
        &lt;footer className=<span class="hljs-string">"bg-gray-100 text-center py-4 mt-10"</span>&gt;
            &lt;div className=<span class="hljs-string">"flex align-items-center justify-center text-sm text-blue-400 font-bold"</span>&gt;
                &lt;a href=<span class="hljs-string">"/"</span> className=<span class="hljs-string">"hover:text-blue-600"</span>&gt;Home&lt;/a&gt;
                &lt;p&gt; &amp;nbsp; &amp;nbsp; | &amp;nbsp; &amp;nbsp; &lt;/p&gt;
                &lt;a href=<span class="hljs-string">"/about"</span> className=<span class="hljs-string">"hover:text-blue-600"</span>&gt;About&lt;/a&gt;
            &lt;/div&gt;
            &lt;p className=<span class="hljs-string">"text-sm text-gray-600"</span>&gt;© {<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear()} Chidiadi Anyanwu. All rights reserved.&lt;/p&gt;
            &lt;p className=<span class="hljs-string">"text-sm text-gray-600"</span>&gt;Built <span class="hljs-keyword">with</span> Next.js and Tailwind CSS&lt;/p&gt;
        &lt;/footer&gt;
    );

}
</code></pre>
<p>This <code>new Date().getFullYear()</code> helps me get the current year all the time.</p>
<h3 id="heading-5-place-the-components-properly">5. Place the components properly</h3>
<p>The nav bar and footer components are things that will not change no matter the page you visit. So they should be placed in a more permanent and untouched location. We can put both of them in the root <code>layout.tsx</code> file like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746882493175/3d7381ca-48c6-43c1-becc-8692c6b090c4.png" alt="layout.tsx file" class="image--center mx-auto" width="929" height="481" loading="lazy"></p>
<pre><code class="lang-typescript"> &lt;body className={<span class="hljs-string">`<span class="hljs-subst">${geistSans.variable}</span> <span class="hljs-subst">${geistMono.variable}</span> antialiased scroll-smooth`</span>}&gt;
      &lt;Navbar /&gt;
      {children}
      &lt;Footer /&gt;
  &lt;/body&gt;
</code></pre>
<p><code>{children}</code> is where the contents from <code>page.tsx</code> will enter. So, we sandwiched all the other content in the Nav bar and footer. Apart from adding <code>&lt;link /&gt;</code> tags for fonts (because this is where the root HTML is), we really don't have business with this file again.</p>
<p>Now, in the same <code>app/</code> folder where this layout file is, create the <code>&lt;HomeClient /&gt;</code> file. This is how it looks:</p>
<pre><code class="lang-typescript"><span class="hljs-string">'use client'</span>;

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Hero <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/hero'</span>;
<span class="hljs-keyword">import</span> MainBody <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/mainbody'</span>;
<span class="hljs-keyword">import</span> { Article } <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/ArticleCard'</span>;

<span class="hljs-keyword">interface</span> Props {
  initialArticles: Article[];
}

<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">HomeClient</span>(<span class="hljs-params">{ initialArticles }: Props</span>) </span>{
  <span class="hljs-keyword">const</span> [searchTerm, setSearchTerm] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [articles, setArticles] = useState&lt;Article[]&gt;(initialArticles);

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;Hero searchTerm={searchTerm} setSearchTerm={setSearchTerm} /&gt;
      &lt;MainBody searchTerm={searchTerm} articles={articles} /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Then, put the <code>HomeClient</code> component inside the <code>page.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { fetchArticles } <span class="hljs-keyword">from</span> <span class="hljs-string">'../utils/fetchArticles'</span>;
<span class="hljs-keyword">import</span> HomeClient <span class="hljs-keyword">from</span> <span class="hljs-string">'./HomeClient'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> revalidate = <span class="hljs-number">3600</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HomePage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> articles = <span class="hljs-keyword">await</span> fetchArticles(); 

  <span class="hljs-keyword">return</span> &lt;HomeClient initialArticles={articles} /&gt;;
}
</code></pre>
<p>The server is set to fetch the articles at build time, and fetch again (revalidate) every hour (3600s). So, it doesn't fetch the articles from the URLs upon user request of the page.</p>
<p>Initially, it worked by fetching any time the component was mounted, but I noticed that this caused the page to load very slowly. The articles didn't pop-up on time, because there's a lot of fetching to be done.</p>
<p>In that same <code>app/</code> directory, create an <code>about/</code> folder, and create the <code>page.tsx</code> for that route:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</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">About</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;div className=<span class="hljs-string">"flex items-center justify-center"</span>&gt;
                &lt;div className=<span class="hljs-string">"margin-auto w-[90vw] md:w-[60vw] lg:w-[50vw] h-[450px] hover:bg-gray-100 border-1 md:border-2 border-gray-200 shadow-sm flex flex-wrap  items-center justify-center gap-2 mt-10 mb-10 rounded-lg"</span>&gt;
                    &lt;Image
                        src=<span class="hljs-string">"/MyPhotoChidiadi.jpg"</span> 
                        alt=<span class="hljs-string">"Avatar"</span>
                        className=<span class="hljs-string">"rounded-[50%] h-30 w-30"</span>
                        width={<span class="hljs-number">120</span>} 
                        height={<span class="hljs-number">120</span>} 
                    /&gt;
                    &lt;div className=<span class="hljs-string">"w-[90%] mx-auto"</span>&gt;
                        &lt;h1 className=<span class="hljs-string">"text-xl text-center my-1 font-bold"</span>&gt;About Me&lt;/h1&gt;
                        &lt;p className=<span class="hljs-string">"text-justify my-3"</span>&gt;
                            My name is Chidiadi Anyanwu. I love breaking down complex concepts.
                            I write about Networking, Cloud, DevOps, and even sometimes web development. 
                            You can connect <span class="hljs-keyword">with</span> me by following <span class="hljs-built_in">any</span> <span class="hljs-keyword">of</span> the links below.
                        &lt;/p&gt;
                        &lt;hr className=<span class="hljs-string">"border-gray-300 my-3"</span> /&gt;
                        &lt;div className=<span class="hljs-string">"flex gap-7 w-full my-3 justify-center"</span>&gt; 
                            &lt;a href=<span class="hljs-string">"https://github.com/chidiadi01"</span>&gt;
                                &lt;Image src=<span class="hljs-string">'/github-icon.svg'</span> alt=<span class="hljs-string">"github logo"</span> width={<span class="hljs-number">24</span>} height={<span class="hljs-number">24</span>} /&gt;
                            &lt;/a&gt;
                            &lt;a href=<span class="hljs-string">"https://linkedin.com/in/chidiadi-anyanwu"</span>&gt;
                                &lt;Image src=<span class="hljs-string">'linkedin-icon.svg'</span> alt=<span class="hljs-string">"linkedin logo"</span> width={<span class="hljs-number">24</span>} height={<span class="hljs-number">24</span>}/&gt;
                            &lt;/a&gt;
                            &lt;a href=<span class="hljs-string">"https://x.com/chidiadi01"</span>&gt;
                                &lt;Image src=<span class="hljs-string">'x-2.svg'</span> alt=<span class="hljs-string">"x logo"</span> width={<span class="hljs-number">24</span>} height={<span class="hljs-number">24</span>}/&gt; 
                            &lt;/a&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/&gt;
    );
}
</code></pre>
<h3 id="heading-6-create-the-utils-folder-and-all-the-functions">6. Create the utils folder and all the functions</h3>
<p>The next step is to create all these files.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947058303/01b6fd3d-3666-46fe-8928-1ad5b1532625.png" alt="utils folder" class="image--center mx-auto" width="359" height="136" loading="lazy"></p>
<p>Under the same <code>app/</code> directory, create the <code>utils/</code> folder. <code>app/utils/</code>. Then start with the <code>fetchArticles()</code> function. The <code>fetchArticles()</code> function is what accesses the API route in the project to obtain the array of Article objects from an array of URLs. The <code>fetchArticles()</code> function returns an array of those objects which are then stored in the <code>articles</code> variable. It looks like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { getPublishedDate } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getPublishedDate'</span>;
<span class="hljs-keyword">import</span> { getTitle } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getTitle'</span>;
<span class="hljs-keyword">import</span> { getImageURL } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getImageURL'</span>;
<span class="hljs-keyword">import</span> { getDescription } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getDescription'</span>;
<span class="hljs-keyword">import</span> { getPlatform } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getPlatform'</span>;
<span class="hljs-keyword">import</span> articleFile <span class="hljs-keyword">from</span> <span class="hljs-string">'../app/articles.json'</span>;
<span class="hljs-keyword">import</span> { Article } <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/ArticleCard'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchArticles</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Article</span>[]&gt; </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fetching articles...'</span>);
  <span class="hljs-keyword">const</span> results = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(
    articleFile.articles.map(<span class="hljs-keyword">async</span> (item) =&gt; {
      <span class="hljs-comment">//Validate URL first</span>
       <span class="hljs-keyword">if</span> (!item.url || <span class="hljs-keyword">typeof</span> item.url !== <span class="hljs-string">'string'</span> || item.url.trim() === <span class="hljs-string">''</span>) {
        <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">`Invalid URL: <span class="hljs-subst">${item.url}</span>`</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; <span class="hljs-comment">// Skip this item</span>
      } 
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The URL: '</span> + item.url);
      <span class="hljs-keyword">let</span> data;
      <span class="hljs-keyword">try</span> {

        <span class="hljs-comment">// Fetch metadata and HTML from the URL</span>
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(item.url, {
          headers: {
            <span class="hljs-string">'User-Agent'</span>: <span class="hljs-string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36'</span>,
            <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'</span>,
            <span class="hljs-string">'Accept-Language'</span>: <span class="hljs-string">'en-US,en;q=0.5'</span>,
            <span class="hljs-string">'Referer'</span>: <span class="hljs-string">'https://www.google.com/'</span>, 
          },
        });

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Fetched: '</span>+ item.url);

        <span class="hljs-keyword">if</span> (!response.ok) {
          <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`HTTP error! Status: <span class="hljs-subst">${response.status}</span> for URL: <span class="hljs-subst">${item.url}</span>`</span>);
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`HTTP error! Status: <span class="hljs-subst">${response.status}</span>`</span>);
        }

        <span class="hljs-keyword">const</span> html = <span class="hljs-keyword">await</span> response.text();
        <span class="hljs-keyword">const</span> $ = cheerio.load(html);
        <span class="hljs-keyword">const</span> jsonScript = $(<span class="hljs-string">'script[type="application/ld+json"]'</span>).html();

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Gotten HTML response'</span>);

        <span class="hljs-keyword">if</span> (!jsonScript) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'No JSON-LD script found on page'</span>);
        }

        <span class="hljs-keyword">const</span> metadata = <span class="hljs-built_in">JSON</span>.parse(jsonScript);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Gotten metadata'</span>);


        <span class="hljs-comment">// Combine metadata and HTML into a single object</span>
        data = { metadata, html };
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Failed to fetch metadata for URL: <span class="hljs-subst">${item.url}</span>`</span>, error);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The default empty object has been returned here'</span>);
      }

      <span class="hljs-comment">// Use the combined data (metadata and HTML) to construct the article object</span>
      <span class="hljs-keyword">if</span>(getTitle(data) &amp;&amp; getDescription(data) &amp;&amp;
         getPublishedDate(data) &amp;&amp; getImageURL(data) &amp;&amp;
         getPlatform(data) || (item.title &amp;&amp; item.description &amp;&amp;
         item.image)) {
        <span class="hljs-keyword">return</span> {
        ...item,
        id: item.id ?? <span class="hljs-number">0</span>,
        tags: item.tags ?? [],
        title: getTitle(data) || item.title || <span class="hljs-string">'No title'</span>,
        description: item.description || getDescription(data) || <span class="hljs-string">'No description'</span>,
        publishedDate: getPublishedDate(data) ?? <span class="hljs-string">'No date'</span>,
        imgUrl: getImageURL(data) || item.image || <span class="hljs-string">'/img-2.jpg'</span>,
        siteName: getPlatform(data) || data.metadata?.publisher?.name || <span class="hljs-string">'Unknown site'</span>,
        url: item.url || <span class="hljs-string">''</span>,
      } <span class="hljs-keyword">as</span> Article;
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Proper item returned'</span>);
      } <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; }
    })
  );

  <span class="hljs-comment">// Filter out null values and sort the articles by published date in descending order</span>
  <span class="hljs-keyword">const</span> filteredResults = results.filter((article): article is Article =&gt; article !== <span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> sortedResults = filteredResults.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> dateA = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(a.publishedDate || <span class="hljs-string">''</span>).getTime();
    <span class="hljs-keyword">const</span> dateB = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(b.publishedDate || <span class="hljs-string">''</span>).getTime();
    <span class="hljs-keyword">return</span> dateB - dateA;
  });
  <span class="hljs-built_in">console</span>.log(sortedResults);
  <span class="hljs-keyword">return</span> sortedResults;
}
</code></pre>
<p>It maps through the articles in the articleFile, which is the JSON file with an array of objects with article URLs. For each of them, it sends a request to the URL, and from the data gotten, returns an Article object. Then, the array of objects created, <code>results</code>, is first filtered to remove null objects, and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort">sorted</a> in descending order by their date properties. So, the latest article shows up first.</p>
<p>It’s then assigned in the <code>HomeClient</code> component:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> articles = <span class="hljs-keyword">await</span> fetchArticles();
</code></pre>
<p>In the <code>fetchArticles()</code> code above, you can see that some other functions were used to extract the properties from the URLs, and assign them. Also, during deployment, I found that Substack couldn’t be accessed by the server, so I’m going to add code to allow creation of Article objects from an RSS feed. That will be in the <a target="_blank" href="https://github.com/chidiadi01/simple-writer-portfolio/tree/main/01-simple-blog">project repository</a>.</p>
<p>Now, let's talk about the other functions.</p>
<h4 id="heading-the-gettitle-function"><strong>The</strong> <code>getTitle()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTitle</span>(<span class="hljs-params">data:<span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span>(!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Title Loading . . .'</span>;

    <span class="hljs-keyword">if</span> (data?.html) {
        <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
        <span class="hljs-keyword">const</span> ogTitle = $(<span class="hljs-string">'meta[property="og:title"]'</span>).attr(<span class="hljs-string">'content'</span>) || $(<span class="hljs-string">'title'</span>).text();
        <span class="hljs-keyword">return</span> ogTitle;
    }

        <span class="hljs-keyword">return</span> <span class="hljs-string">'The Title of The Article'</span>;
    }
</code></pre>
<p>This is a very simple function. It takes the <code>data</code> parameter, and if there's no data, it returns <code>Title loading . . .</code>. But if there is data, it checks to see if there's HTML in the data. If there is, it then uses cheerio to load the HTML text and extract the title from the Open Graph <code>title</code> metadata or from the <code>&lt;title&gt;</code> tag in the HTML header. Else, it returns <code>The Title of the Article</code>.</p>
<p>Here, we use jQuery-like syntax <code>$</code> to select the HTML elements, like in <code>$('title')</code>. The data taken as a parameter is the response gotten from a HTTP request to the article's URL.</p>
<h4 id="heading-the-getdescription-function"><strong>The</strong> <code>getDescription()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getDescription</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Description Loading . . .'</span>;

  <span class="hljs-keyword">if</span> (data?.metadata || data?.html) {
    <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html || <span class="hljs-string">''</span>);
    <span class="hljs-keyword">const</span> description = data?.metadata?.description ?? $(<span class="hljs-string">'meta[property="og:description"]'</span>).attr(<span class="hljs-string">'content'</span>) ?? <span class="hljs-string">'No description found'</span>;
    <span class="hljs-keyword">return</span> description;
  }

  <span class="hljs-keyword">return</span> <span class="hljs-string">'No description found'</span>;
}
</code></pre>
<h4 id="heading-the-geturl-function"><strong>The</strong> <code>getURL()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getURL</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span></span>{
    <span class="hljs-keyword">if</span>(!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'url'</span>;

    <span class="hljs-keyword">if</span>(data?.metadata || data?.html){
        <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
        <span class="hljs-keyword">const</span> url = data?.metadata.url || $(<span class="hljs-string">'meta[property="og:url"]'</span>).attr(<span class="hljs-string">'content'</span>);
        <span class="hljs-keyword">return</span> url;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-string">'url'</span>;
}
</code></pre>
<p>This function is not really used to get the URL of the article for use in the object. Rather, it is used to get the URL for another function, <code>getPlatform()</code>. It works the same way as the ones we discussed before.</p>
<h4 id="heading-the-getplatform-function"><strong>The</strong> <code>getPlatform()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { getURL } <span class="hljs-keyword">from</span> <span class="hljs-string">'./getURL'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPlatform</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Platform1'</span>;

    <span class="hljs-keyword">const</span> url = getURL(data);

    <span class="hljs-keyword">if</span> (data?.html) {
      <span class="hljs-keyword">const</span> regex = <span class="hljs-regexp">/^(?:https?:\/\/)?(?:www\.)?([^\/\n]+)\.(?:[a-zA-Z]{2,})/</span>;
      <span class="hljs-keyword">const</span> platform = url.match(regex);
      <span class="hljs-keyword">return</span> platform?.[<span class="hljs-number">1</span>].toUpperCase() || <span class="hljs-string">'Platform2'</span>; 
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">'Platform3'</span>;
  }
</code></pre>
<p>This function is meant to extract the name of the platform where the article is posted. I toyed with various ideas for how this should work. One of them was using the <code>siteName</code> property in the OG meta tags, but I realised from my inspection that not all platforms had it populated in a helpful way. So, the results gotten from that method would be too unpredictable.</p>
<p>So I decided to use <a target="_blank" href="https://www.freecodecamp.org/news/practical-regex-guide-with-real-life-examples/">regex (Regular Expressions)</a> to extract the site name from the URL. As you can see from the code, I didn't achieve a perfect result, but it is usable.</p>
<p>First of all, it gets the URL of the article with the <code>getURL()</code> function. Then, it uses regex:</p>
<pre><code class="lang-bash">/^(?:https?:\/\/)?(?:www\.)?([^\/\n]+)\.(?:[a-zA-Z]{2,})/
</code></pre>
<p>Here, <code>/</code> and <code>/</code> at the beginning and end are to start and end the regex string. The caret <code>^</code> marks the beginning of a line.</p>
<p>Then, we have four groups <code>()()()()</code>. The first one is a non-captured group <code>(?: )</code>. That means whatever text that matches that should be grouped together in a string, but should not be captured to be assigned to the variable. It captures any text with a 'http' in it, with or without the s <code>s?</code>, and with two slashes after. The forward slashes were escaped with backward slashes so they can be recognised as literal characters. Then, the whole group itself is made optional by adding the question mark after it <code>(...)?</code>. So, whether such a group is matched or not, the code works.</p>
<p>The second group is also a non-capturing group, also denoted by <code>?:</code> being the first thing inside the bracket. This one matches any 'www.' in the string. It's also optional. A URL may not necessarily be written with it.</p>
<p>The third group is a capturing group as it doesn't have <code>?:</code> inside the brackets. Rather, it has a character class in it <code>[]</code>. But it's a negated class <code>[^ ]</code>. It makes sure that the class does not contain a newline character <code>n</code> (the newline character n is not a string of letter n – that's why it is escaped) or a forward slash <code>/</code>, because a URL is supposed to be one line, and not multiple lines. The <code>+</code> means one or more characters, <code>([^\/\n]+)</code>. Whatever is in this group will get captured in the variable.</p>
<p>Then, the next one matches a dot (it is escaped with a backslash <code>\.</code>). After that is the last group which is also non-capturing and matches any character which is alphanumeric, capital or small letter <code>[a-zA-Z]</code>, that occurs more than two times <code>{2, }</code>.</p>
<p>So, if we have '<a target="_blank" href="https://www.linkedin,com'">https://www.linkedin,com'</a> we would have an array of captured groups ['<a target="_blank" href="https://www.linkedin.com','https://','www.','linkedin','com'%5D">https://www.linkedin.com','https://','www.','linkedin','com']</a>. Group 1 = 'https://', group 2 = 'www.', group 3 ='linkedin', group 4 = 'com'. But since only group 3 is a captured group, others will be discarded, and we have an array with only two items, the full string, and the captured group: ['<a target="_blank" href="https://www.linkedin.com','linkedin'%5D">https://www.linkedin.com','linkedin']</a><em>.</em></p>
<p>So, here, we return the second item in the array. The first item is always the full string we matched.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> platform?.[<span class="hljs-number">1</span>].toUpperCase()
</code></pre>
<p>This doesn't account for sub domains, though. This is tricky because sometimes you want to use the name of the subdomain (as in my Substack), and sometimes you want to use the name of the domain. So, I left it like that.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947172558/7f0f47c0-a1d5-4db7-a8e5-d45d1116aaac.png" alt="Image of an article with platform showing THENETWORKBITS.SUBSTACK" class="image--center mx-auto" width="467" height="180" loading="lazy"></p>
<h4 id="heading-the-getimageurl-function"><strong>The</strong> <code>getImageURL()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getImageURL</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'/img-2.jpg'</span>; 

    <span class="hljs-keyword">if</span> (data?.metadata || data?.html) {
        <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
        <span class="hljs-keyword">const</span> ogImage = $(<span class="hljs-string">'meta[property="og:image"]'</span>).attr(<span class="hljs-string">'content'</span>) || data?.metadata.image;
        <span class="hljs-keyword">return</span> ogImage || <span class="hljs-string">'/img-2.jpg'</span>; 
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">'/img-2.jpg'</span>; 
}
</code></pre>
<p>This function works just like others, and obtains the cover image URL from either the Open Graph image meta tag <code>$('meta[property="og:image"]').attr('content')</code> or <code>||</code> the image property in the JSON-LD data <code>data?.metadata.image</code>.</p>
<h4 id="heading-the-getpublisheddate-function"><strong>The</strong> <code>getPublishedDate()</code> <strong>function:</strong></h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> cheerio <span class="hljs-keyword">from</span> <span class="hljs-string">'cheerio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPublishedDate</span>(<span class="hljs-params">data: <span class="hljs-built_in">any</span></span>): <span class="hljs-title">string</span> </span>{

  <span class="hljs-keyword">if</span> (!data) <span class="hljs-keyword">return</span> <span class="hljs-string">'Date'</span>;

  <span class="hljs-keyword">const</span> publishedDate = data?.metadata?.datePublished;

  <span class="hljs-keyword">if</span> (publishedDate) {
    <span class="hljs-keyword">const</span> options: <span class="hljs-built_in">Intl</span>.DateTimeFormatOptions = { year: <span class="hljs-string">'numeric'</span>, month: <span class="hljs-string">'long'</span>, day: <span class="hljs-string">'numeric'</span> };
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(publishedDate).toLocaleDateString(<span class="hljs-string">'en-US'</span>, options);
  }

  <span class="hljs-keyword">if</span> (data?.html) {
    <span class="hljs-keyword">const</span> $ = cheerio.load(data?.html);
    <span class="hljs-keyword">const</span> ogPublishedTime = $(<span class="hljs-string">'meta[property="article:published_time"]'</span>).attr(<span class="hljs-string">'content'</span>) ||
                            $(<span class="hljs-string">'meta[property="og:published_time"]'</span>).attr(<span class="hljs-string">'content'</span>) || 
                            $(<span class="hljs-string">'meta[name="pubdate"]'</span>).attr(<span class="hljs-string">'content'</span>);

    <span class="hljs-keyword">if</span> (ogPublishedTime) {
      <span class="hljs-keyword">const</span> options: <span class="hljs-built_in">Intl</span>.DateTimeFormatOptions = { year: <span class="hljs-string">'numeric'</span>, month: <span class="hljs-string">'long'</span>, day: <span class="hljs-string">'numeric'</span> };
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(ogPublishedTime).toLocaleDateString(<span class="hljs-string">'en-US'</span>, options);
    }
  }
  <span class="hljs-keyword">return</span> <span class="hljs-string">'Date'</span>;
}
</code></pre>
<p>This function is especially useful because of the need to convert the date from the ISO 8601 format (2025-04-07T10:47:19+00:00) to the more readable format I want (April 7, 2025). Here, I used the <code>.toLocaleDateString()</code> JavaScript function to make it work (see the (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString">MDN</a>).</p>
<h3 id="heading-7-create-your-json-file">7. Create your JSON file</h3>
<p>Now, remember that we're building this to be able to pull URLs from a JSON file to put together and render the web page. That JSON file is the starting point of everything. I believe by now you're getting an error concerning that. So we need to create the JSON file.</p>
<p>In the <code>app/</code> directory, create a new file and name it <code>articles.json</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947297567/ed6b282b-971e-428b-a7b6-9d6bf5e44520.png" alt="The app/ directory" class="image--center mx-auto" width="359" height="244" loading="lazy"></p>
<p>Then populate it like in this file below – an array of objects with id, URL, tags, and so on. Even though we are not trying to get the title, description, and everything from this file directly, I put in that feature. If you go back to our <code>fetchArticles()</code> function, you'll see that for most of the properties, whatever you write here will override what was gotten from the URLs.</p>
<p>It was partly a fail-safe because I thought that LinkedIn would block all requests, and as you can see from my blog already, some description tags were not well organized. So, we can replace them later with a cleaner description just by modifying this file.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"articles"</span>: [
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
            <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://thenetworkbits.substack.com/p/an-overview-of-json"</span>,
            <span class="hljs-attr">"tags"</span>: [<span class="hljs-string">"Web Dev"</span>, <span class="hljs-string">"DevOps"</span>, <span class="hljs-string">"Cloud"</span>],
            <span class="hljs-attr">"title"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"image"</span>: <span class="hljs-string">""</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
            <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://websecuritylab.org/how-safe-is-public-wi-fi-a-network-engineer-explains/"</span>,
            <span class="hljs-attr">"tags"</span>: [<span class="hljs-string">"Networking"</span>, <span class="hljs-string">"Cybersecurity"</span>],
            <span class="hljs-attr">"title"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"image"</span>: <span class="hljs-string">""</span>
        },
        {
            <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>,
            <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://www.freecodecamp.org/news/automate-cicd-with-github-actions-streamline-workflow/"</span>,
            <span class="hljs-attr">"tags"</span>: [<span class="hljs-string">"DevOps"</span>],
            <span class="hljs-attr">"title"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"image"</span>: <span class="hljs-string">""</span>
        }
    ]
}
</code></pre>
<p>Here, we have an "articles" object with an array of objects, each of which have "id", "url", "tags", "title", "description", and "image" properties. You don't necessarily need the values of all of these except the ID and URL, but the keys have to be there to prevent errors.</p>
<h3 id="heading-8-add-the-finishing-touches">8. Add the finishing touches</h3>
<p>Now you can add your own favicon in the app directory. It could be a 24px by 24px file, or 48px by 48px file. It doesn't necessarily have to be in the app directory or be an icon file or be named 'favicon' – but I did it that way. You can just add this in the HTML header of your layout.tsx file which is your Next.js version of <code>index.html</code>. The favicon is the icon that shows on the tab in your browser when you open the page.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"icon"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/favicon.ico"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"any"</span> /&gt;</span>
</code></pre>
<p>You can also read the Next.js documentations on that here: <a target="_blank" href="https://nextjs.org/docs/app/api-reference/file-conventions/metadata/app-icons">Metadata Files: favicon, icon, and apple-icon | Next.js</a>. Then add your images to your <code>public/</code> directory. Be sure to name them correctly, and reference them correctly.</p>
<p>Now, if your development server was down, spin it up again to see your end results!</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947429494/1134babc-6b26-4f88-99df-7c3c8a2e8ba6.png" alt="The blog above the fold" class="image--center mx-auto" width="1360" height="599" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947441422/5b87e888-a040-4ca1-bc27-7d09e50561ce.png" alt="Articles on the blog" class="image--center mx-auto" width="1364" height="597" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746947455924/64e57b54-feb9-46c9-99c9-8a7260aff45d.png" alt="About page" class="image--center mx-auto" width="1362" height="595" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If you've read this far, then you must be really interested in seeing the results of all this :) I already have that covered. <a target="_blank" href="https://chidiadi-portfolio.vercel.app/">Here's the blog</a>. You can go through it and interact with it.</p>
<p>Also, <a target="_blank" href="https://github.com/chidiadi01/simple-writer-portfolio/tree/main/01-simple-blog">this is the codebase</a>. Feel free to fork it, clone it, and interact with it as well. If you enjoyed the article, please share it with others. You can also connect with me on <a target="_blank" href="https://linkedin.com/in/chidiadi-anyanwu">LinkedIn</a> or <a target="_blank" href="https://x.com/chidiadi01">X</a>. Thanks for reading.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Simple Portfolio Website With HTML and CSS ]]>
                </title>
                <description>
                    <![CDATA[ While browsing through some of my old projects, I stumbled upon a portfolio I worked on a while back. The funny thing? I never used it. So, I decided to give it a fresh look and thought, why not share it with you? As a developer, having a portfolio i... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-simple-portfolio-website-with-html-and-css/</link>
                <guid isPermaLink="false">67859c6915988a75c229dee2</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Spruce Emmanuel ]]>
                </dc:creator>
                <pubDate>Mon, 13 Jan 2025 23:06:17 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736809428369/d28ee1e5-c11b-48fa-9ccd-b7fbaedd52da.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>While browsing through some of my old projects, I stumbled upon a portfolio I worked on a while back. The funny thing? I never used it. So, I decided to give it a fresh look and thought, why not share it with you?</p>
<p>As a developer, having a portfolio is important. It’s your personal space to showcase your skills, share projects, and impress potential employers. Whether you’re aiming to build an online presence, share your work with friends, or take your career to the next level, a portfolio is the perfect way to highlight what you can do.</p>
<p>In this article, I’ll guide you through building a simple portfolio website using just HTML and CSS. By the end, you’ll have a fully functional portfolio site ready to share with anyone.</p>
<h3 id="heading-what-the-finished-portfolio-looks-like">What the Finished Portfolio Looks Like:</h3>
<p>Before we get started, here’s a preview of what your portfolio will look like once we’re done:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736721383725/d8c88f72-5e79-466d-9fe0-d8814be7f1cb.png" alt="d8c88f72-5e79-466d-9fe0-d8814be7f1cb" class="image--center mx-auto" width="2412" height="4696" loading="lazy"></p>
<p>If you’d prefer to skip ahead, you can get the full code on <a target="_blank" href="https://github.com/iamspruce/Simple-HTML-CSS-Portfolio">GitHub</a>.</p>
<h2 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-overview-and-setup">Project Overview and Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-header-and-hero-section">How to Build the Header and Hero Section</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-about-section">How to Build the About Section</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-work-section">How to Build the Work Section</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-contact-section-and-footer">How to Build the Contact Section and Footer</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-project-overview-and-setup">Project Overview and Setup</h2>
<p>Before we start coding, let’s take a look at the basic structure of the project. Here's how everything will be organized:</p>
<pre><code class="lang-bash">/assets
  - background_image.jpg
  - user.png
  - icon-github.svg
  - icon-twitter.svg
  - logo1.png
  - logo2.png
  - logo3.png
index.html
styles.css
</code></pre>
<p>This folder structure keeps things neat and organized. The <code>/assets</code> folder will hold all of your images and icons that will be used throughout the website, while the <code>index.html</code> file will contain the structure of your portfolio, and <code>styles.css</code> will control how everything looks.</p>
<h3 id="heading-setting-up-the-html-file">Setting Up the HTML File</h3>
<p>Let's start with the basic HTML structure. Open your <code>index.html</code> file and add the following code:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"styles.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Spruce - Dev Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Content will go here --&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’s a breakdown of what this code does:</p>
<ol>
<li><p><strong>DOCTYPE</strong>: This tells the browser that this is an HTML5 document.</p>
</li>
<li><p><strong>meta charset</strong>: Defines the character encoding, which helps display text properly.</p>
</li>
<li><p><strong>meta viewport</strong>: Ensures the website is responsive on different screen sizes.</p>
</li>
<li><p><strong>link to styles.css</strong>: Links the <code>styles.css</code> file for styling your website.</p>
</li>
<li><p><strong>title</strong>: This is the text that appears in the browser tab when you open the website.</p>
</li>
</ol>
<p>Now that our HTML file is set up, we’ll move on to the CSS.</p>
<h3 id="heading-setting-up-the-css-file">Setting Up the CSS File</h3>
<p>Let’s now add some basic styling using a CSS reset. This helps eliminate browser inconsistencies by normalizing styles across different browsers.</p>
<p>Here’s the basic CSS reset you’ll need:</p>
<pre><code class="lang-css"><span class="hljs-comment">/**
 * CORE CONFIG
 * This powers everything from utility class generation to breakpoints
 * to enabling/disabling pre-built components/utilities.
 */</span>
<span class="hljs-comment">/* Box sizing rules */</span>
*,
*<span class="hljs-selector-pseudo">::before</span>,
*<span class="hljs-selector-pseudo">::after</span> {
  <span class="hljs-attribute">box-sizing</span>: border-box;
  <span class="hljs-attribute">font-family</span>: sans-serif;
}

<span class="hljs-comment">/* Remove default margin */</span>
<span class="hljs-selector-tag">body</span>,
<span class="hljs-selector-tag">h1</span>,
<span class="hljs-selector-tag">h2</span>,
<span class="hljs-selector-tag">h3</span>,
<span class="hljs-selector-tag">h4</span>,
<span class="hljs-selector-tag">p</span>,
<span class="hljs-selector-tag">figure</span>,
<span class="hljs-selector-tag">blockquote</span>,
<span class="hljs-selector-tag">dl</span>,
<span class="hljs-selector-tag">dd</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-comment">/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */</span>
<span class="hljs-selector-tag">ul</span><span class="hljs-selector-attr">[role=<span class="hljs-string">"list"</span>]</span>,
<span class="hljs-selector-tag">ol</span><span class="hljs-selector-attr">[role=<span class="hljs-string">"list"</span>]</span> {
  <span class="hljs-attribute">list-style</span>: none;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-comment">/* Set core root defaults */</span>
<span class="hljs-selector-tag">html</span><span class="hljs-selector-pseudo">:focus-within</span> {
  <span class="hljs-attribute">scroll-behavior</span>: smooth;
}

<span class="hljs-comment">/* Set core body defaults */</span>
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">text-rendering</span>: optimizeSpeed;
  <span class="hljs-attribute">letter-spacing</span>: -<span class="hljs-number">0.01em</span>;
}

<span class="hljs-comment">/* All elements that don't have a class get default styles */</span>
<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-attr">[class]</span>) {
  <span class="hljs-attribute">text-decoration-skip-ink</span>: auto;
}

<span class="hljs-comment">/* Make images easier to work with */</span>
<span class="hljs-selector-tag">img</span>,
<span class="hljs-selector-tag">picture</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">display</span>: block;
}

<span class="hljs-comment">/* Inherit fonts for inputs and buttons */</span>
<span class="hljs-selector-tag">input</span>,
<span class="hljs-selector-tag">button</span>,
<span class="hljs-selector-tag">textarea</span>,
<span class="hljs-selector-tag">select</span> {
  <span class="hljs-attribute">font</span>: inherit;
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-reduced-motion:</span> reduce) {
  <span class="hljs-selector-tag">html</span><span class="hljs-selector-pseudo">:focus-within</span> {
    <span class="hljs-attribute">scroll-behavior</span>: auto;
  }
}

<span class="hljs-selector-pseudo">:focus</span> {
  <span class="hljs-attribute">outline</span>: <span class="hljs-number">2px</span> dashed <span class="hljs-number">#00ff19</span>;
  <span class="hljs-attribute">outline-offset</span>: <span class="hljs-number">0.25rem</span>;
}

<span class="hljs-selector-tag">main</span><span class="hljs-selector-pseudo">:focus</span> {
  <span class="hljs-attribute">outline</span>: none;
}
</code></pre>
<p>This reset clears any default margins and paddings, ensuring that your layout remains consistent across browsers. You can find more info about what the code does in the comments.</p>
<h3 id="heading-sizing-and-spacing-with-utopia">Sizing and Spacing with Utopia</h3>
<p>For spacing and typography, we’ll use a flexible system from Utopia, a tool designed to make responsive typography easier to implement. Here’s the CSS that will give us scalable, adjustable spacing and font sizes:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* @link https://utopia.fyi/type/calculator?c=320,18,1.2,1240,20,1.25,5,2,&amp;s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&amp;g=s,l,xl,12 */</span>

<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--step--2</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">0.7813rem</span>, <span class="hljs-number">0.7747rem</span> + <span class="hljs-number">0.0326vw</span>, <span class="hljs-number">0.8rem</span>);
  <span class="hljs-attribute">--step--1</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">0.9375rem</span>, <span class="hljs-number">0.9158rem</span> + <span class="hljs-number">0.1087vw</span>, <span class="hljs-number">1rem</span>);
  <span class="hljs-attribute">--step-0</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.125rem</span>, <span class="hljs-number">1.0815rem</span> + <span class="hljs-number">0.2174vw</span>, <span class="hljs-number">1.25rem</span>);
  <span class="hljs-attribute">--step-1</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.35rem</span>, <span class="hljs-number">1.2761rem</span> + <span class="hljs-number">0.3696vw</span>, <span class="hljs-number">1.5625rem</span>);
  <span class="hljs-attribute">--step-2</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.62rem</span>, <span class="hljs-number">1.5041rem</span> + <span class="hljs-number">0.5793vw</span>, <span class="hljs-number">1.9531rem</span>);
  <span class="hljs-attribute">--step-3</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.944rem</span>, <span class="hljs-number">1.771rem</span> + <span class="hljs-number">0.8651vw</span>, <span class="hljs-number">2.4414rem</span>);
  <span class="hljs-attribute">--step-4</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">2.3328rem</span>, <span class="hljs-number">2.0827rem</span> + <span class="hljs-number">1.2504vw</span>, <span class="hljs-number">3.0518rem</span>);
  <span class="hljs-attribute">--step-5</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">2.7994rem</span>, <span class="hljs-number">2.4462rem</span> + <span class="hljs-number">1.7658vw</span>, <span class="hljs-number">3.8147rem</span>);
}

<span class="hljs-comment">/* @link https://utopia.fyi/space/calculator?c=320,18,1.2,1240,20,1.25,5,2,&amp;s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&amp;g=s,l,xl,12 */</span>

<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--space-3xs</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">0.3125rem</span>, <span class="hljs-number">0.3125rem</span> + <span class="hljs-number">0vw</span>, <span class="hljs-number">0.3125rem</span>);
  <span class="hljs-attribute">--space-2xs</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">0.5625rem</span>, <span class="hljs-number">0.5408rem</span> + <span class="hljs-number">0.1087vw</span>, <span class="hljs-number">0.625rem</span>);
  <span class="hljs-attribute">--space-xs</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">0.875rem</span>, <span class="hljs-number">0.8533rem</span> + <span class="hljs-number">0.1087vw</span>, <span class="hljs-number">0.9375rem</span>);
  <span class="hljs-attribute">--space-s</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.125rem</span>, <span class="hljs-number">1.0815rem</span> + <span class="hljs-number">0.2174vw</span>, <span class="hljs-number">1.25rem</span>);
  <span class="hljs-attribute">--space-m</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">1.6875rem</span>, <span class="hljs-number">1.6223rem</span> + <span class="hljs-number">0.3261vw</span>, <span class="hljs-number">1.875rem</span>);
  <span class="hljs-attribute">--space-l</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">2.25rem</span>, <span class="hljs-number">2.163rem</span> + <span class="hljs-number">0.4348vw</span>, <span class="hljs-number">2.5rem</span>);
  <span class="hljs-attribute">--space-xl</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">3.375rem</span>, <span class="hljs-number">3.2446rem</span> + <span class="hljs-number">0.6522vw</span>, <span class="hljs-number">3.75rem</span>);
  <span class="hljs-attribute">--space-2xl</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">4.5rem</span>, <span class="hljs-number">4.3261rem</span> + <span class="hljs-number">0.8696vw</span>, <span class="hljs-number">5rem</span>);
  <span class="hljs-attribute">--space-3xl</span>: <span class="hljs-built_in">clamp</span>(<span class="hljs-number">6.75rem</span>, <span class="hljs-number">6.4891rem</span> + <span class="hljs-number">1.3043vw</span>, <span class="hljs-number">7.5rem</span>);
}
</code></pre>
<p>These CSS variables give you flexible, responsive font sizes and spacing, based on the viewport size. For instance, <code>--step-5</code> corresponds to the largest font size, which you will use for main headings (<code>h1</code>), and smaller sizes like <code>--step-1</code> for text that's less prominent.</p>
<h3 id="heading-how-typography-and-spacing-work-together">How Typography and Spacing Work Together</h3>
<p>To apply these scalable values, we’ll use the following for headings:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">h1</span>,
<span class="hljs-selector-class">.h1</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">5</span>);
}

<span class="hljs-selector-tag">h2</span>,
<span class="hljs-selector-class">.h2</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">4</span>);
}

<span class="hljs-selector-tag">h3</span>,
<span class="hljs-selector-class">.h3</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">3</span>);
}

<span class="hljs-selector-tag">h4</span>,
<span class="hljs-selector-class">.h4</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">2</span>);
}

<span class="hljs-selector-tag">h5</span>,
<span class="hljs-selector-class">.h5</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">1</span>);
}
</code></pre>
<p>Here, the <code>h1</code> will be the largest size (using <code>--step-5</code>), and <code>h2</code> will be a little smaller (<code>--step-4</code>), continuing down to <code>h5</code>. These variables ensure the text adjusts to different screen sizes and maintains consistent visual hierarchy.</p>
<h3 id="heading-defining-constants-for-layouts-and-spacing">Defining Constants for Layouts and Spacing</h3>
<p>We can also define some utility classes and wrappers for more consistent layouts across the website. Here's how we can define some constants for layout styling:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* define some constants */</span>
<span class="hljs-selector-class">.flex</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">gap</span>: <span class="hljs-built_in">var</span>(--space-s);
}
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">468px</span>) {
  <span class="hljs-selector-class">.flex</span> {
    <span class="hljs-attribute">flex-wrap</span>: wrap;
  }
}
<span class="hljs-selector-class">.flex_center</span> {
  <span class="hljs-attribute">justify-content</span>: center;
}
<span class="hljs-selector-class">.flex_around</span> {
  <span class="hljs-attribute">justify-content</span>: space-around;
}
<span class="hljs-selector-class">.flex_between</span> {
  <span class="hljs-attribute">justify-content</span>: space-between;
}
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">468px</span>) {
  <span class="hljs-selector-class">.flex_around</span> {
    <span class="hljs-attribute">justify-content</span>: center;
  }
  <span class="hljs-selector-class">.flex_between</span> {
    <span class="hljs-attribute">justify-content</span>: center;
  }
}
<span class="hljs-selector-class">.text_center</span> {
  <span class="hljs-attribute">text-align</span>: center;
}
<span class="hljs-selector-class">.text_right</span> {
  <span class="hljs-attribute">text-align</span>: right;
}

<span class="hljs-comment">/* define spacing */</span>
<span class="hljs-selector-class">.padding-top-3xs</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-<span class="hljs-number">3</span>xs);
}
</code></pre>
<p>These classes allow you to quickly apply flexible layouts and spacing throughout your design. The <code>.flex</code> class will apply Flexbox, while <code>.flex_center</code> centers the content. There are also responsive adjustments for smaller screens like wrapping flex items when necessary.</p>
<h3 id="heading-creating-the-wrappers">Creating the Wrappers</h3>
<p>To help structure your layout neatly and consistently, we’ll use wrappers. Wrappers ensure that content is centered and doesn’t stretch too far on wide screens:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* create the wrappers */</span>
<span class="hljs-selector-class">.wrapper</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1240px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
}
<span class="hljs-selector-class">.wrapper_inner</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">800px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
}
</code></pre>
<p>The <code>.wrapper</code> class ensures that the content doesn’t get too wide, while <code>.wrapper_inner</code> adds another layer of control for inner sections like a main content area or smaller containers.</p>
<h2 id="heading-how-to-build-the-header-and-hero-section">How to Build the Header and Hero Section</h2>
<p>Now that our project structure is ready, let’s get into building the first major sections: the Header and Hero. These sections are your site’s first impression, giving visitors a sense of who you are while offering easy navigation.</p>
<h3 id="heading-html-for-the-header-and-hero">HTML for the Header and Hero</h3>
<p>Here’s the HTML structure for both sections:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"styles.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Spruce - Dev Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"site_header"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper_inner"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"site_header_nav"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"list"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex_center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#0"</span>&gt;</span>HOME<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#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">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#work"</span>&gt;</span>WORK<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</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">li</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">nav</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">header</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">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"hero"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper_inner"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text_center hero_content"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h2"</span>&gt;</span>Spruce Emmanuel<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h5"</span>&gt;</span>WEB DEVELOPER &amp;&amp; TECHNICAL WRITER<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">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"list"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex flex_between hero_social"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">address</span>&gt;</span>Port Harcourt, Nigeria<span class="hljs-tag">&lt;/<span class="hljs-name">address</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"list"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Find me online<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://github.com/iamspruce"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/icon-github.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"GitHub"</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> /&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://x.com/sprucekhalifa"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/icon-twitter.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Twitter"</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> /&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&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">section</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">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Header breakdown:</p>
<ul>
<li><p><strong>Navigation</strong>: The navbar lets users quickly jump to key sections like Home, About, Work, and Contact. It’s centered using the <code>.flex</code> and <code>.flex_center</code> classes, making it clean and accessible.</p>
</li>
<li><p><strong>Flex layout</strong>: Links are laid out horizontally using <code>.flex</code> for a smooth, modern look.</p>
</li>
</ul>
<p>Hero breakdown:</p>
<ul>
<li><p><strong>Hero content</strong>: This is where you introduce yourself. Your name and title are centered with padding to give it space and clarity.</p>
</li>
<li><p><strong>Hero social</strong>: Below your bio, we’ve added your location and social links. These are aligned with <code>.flex</code> to keep everything neat and easy to navigate.</p>
</li>
</ul>
<h3 id="heading-css-for-header-and-hero">CSS for Header and Hero</h3>
<p>Here’s the CSS to style these sections:</p>
<h4 id="heading-header-styling">Header styling:</h4>
<pre><code class="lang-css"><span class="hljs-comment">/* Style the header */</span>
<span class="hljs-selector-class">.site_header</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-s) <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(rgba(<span class="hljs-number">26</span>, <span class="hljs-number">26</span>, <span class="hljs-number">64</span>, <span class="hljs-number">0.9</span>), <span class="hljs-built_in">rgba</span>(<span class="hljs-number">26</span>, <span class="hljs-number">26</span>, <span class="hljs-number">64</span>, <span class="hljs-number">0.9</span>));
}

<span class="hljs-selector-class">.site_header_nav</span> <span class="hljs-selector-tag">ul</span> <span class="hljs-selector-tag">li</span> <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
}
</code></pre>
<ul>
<li><p><code>.site_header</code>: Adds padding and a dark gradient for a sleek background.</p>
</li>
<li><p><code>.site_header_nav ul li a</code>: Removes link underlines and makes the text white for a modern, bold look.</p>
</li>
</ul>
<h4 id="heading-hero-styling">Hero styling:</h4>
<pre><code class="lang-css"><span class="hljs-comment">/* Hero section styling */</span>
<span class="hljs-selector-class">.hero_content</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-<span class="hljs-number">3</span>xl) <span class="hljs-number">0</span>;
}

<span class="hljs-selector-class">.hero_content</span> <span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-built_in">var</span>(--space-s);
}

<span class="hljs-selector-class">.hero_social</span> {
  <span class="hljs-attribute">padding-top</span>: <span class="hljs-built_in">var</span>(--space-<span class="hljs-number">3</span>xl);
}

<span class="hljs-comment">/* General styling for the hero section */</span>
<span class="hljs-selector-tag">section</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-<span class="hljs-number">2</span>xl) <span class="hljs-built_in">var</span>(--space-l);
}

<span class="hljs-selector-tag">section</span><span class="hljs-selector-id">#hero</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(rgba(<span class="hljs-number">26</span>, <span class="hljs-number">26</span>, <span class="hljs-number">64</span>, <span class="hljs-number">0.9</span>), <span class="hljs-built_in">rgba</span>(<span class="hljs-number">26</span>, <span class="hljs-number">26</span>, <span class="hljs-number">64</span>, <span class="hljs-number">0.9</span>)),
    <span class="hljs-built_in">url</span>(<span class="hljs-string">"/assets/background_image.jpg"</span>);
  <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">4px</span>;
  <span class="hljs-attribute">background-size</span>: cover;
  <span class="hljs-attribute">background-position</span>: top;
  <span class="hljs-attribute">background-attachment</span>: scroll;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fafafa</span>;
  <span class="hljs-attribute">--color-accent</span>: <span class="hljs-number">#8a2be2</span>;
}
</code></pre>
<ul>
<li><p><code>.hero_content</code>: Adds padding to keep the content spacious.</p>
</li>
<li><p><code>.hero_social</code>: Makes room for the social media links to stand out.</p>
</li>
<li><p><code>section#hero</code>: Sets a stylish gradient background with an image, ensuring the hero section feels engaging and visually striking.</p>
</li>
</ul>
<p>Here’s what it looks like at this point:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735812582003/a755c5b5-b5b9-4bdd-9d1b-c0ff850f066d.png" alt="Portfolio after adding the hero and header sections" class="image--center mx-auto" width="2635" height="1425" loading="lazy"></p>
<h2 id="heading-how-to-build-the-about-section">How to Build the About Section</h2>
<p>The About Section lets you introduce yourself and share your professional journey, giving visitors a glimpse of who you are and what you do.</p>
<h3 id="heading-html-for-the-about-section">HTML for the About Section</h3>
<p>Here’s the HTML structure for the About Section:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"about"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text_center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>
        I am a web developer and technical writer. I write articles on web development and programming.
      <span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">"wrapper_inner"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"about_content flex flex_around"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">figure</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"about_figure text_center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"200px"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"200px"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/user.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Developer"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"about_img"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">figcaption</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"about_caption"</span>&gt;</span>Spruce ❤️<span class="hljs-tag">&lt;/<span class="hljs-name">figcaption</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h5"</span>&gt;</span>
          Born in Nigeria, I've spent 4+ years building with and writing about JavaScript. I help companies market their products and services through technical writing.
        <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">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">section</span>&gt;</span>
</code></pre>
<p>Breakdown of the About section:</p>
<ul>
<li><p><strong>Introduction</strong>: A short and clear statement about your role as a developer.</p>
</li>
<li><p><strong>About content</strong>: The profile image and bio are positioned side by side with a flex container for balance.</p>
</li>
<li><p><strong>Figure and image</strong>: The profile image is displayed within a <code>&lt;figure&gt;</code> tag, with a caption below it.</p>
</li>
<li><p><strong>Text</strong>: A brief bio explaining your background and expertise.</p>
</li>
</ul>
<h3 id="heading-css-for-the-about-section">CSS for the About Section</h3>
<p>Here’s the CSS styling for the About section:</p>
<h4 id="heading-about-section-styling">About section styling:</h4>
<pre><code class="lang-css"><span class="hljs-comment">/* About section */</span>
<span class="hljs-selector-class">.about_content</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-xl) <span class="hljs-number">0</span>;
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">skewX</span>(-<span class="hljs-number">10deg</span>);
}

<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">468px</span>) {
  <span class="hljs-selector-class">.about_content</span> {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">skewX</span>(<span class="hljs-number">0deg</span>);
  }
}

<span class="hljs-selector-class">.about_figure</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-xl) <span class="hljs-number">0</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">4px</span> <span class="hljs-number">6px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-number">#ccc</span>;
  <span class="hljs-attribute">display</span>: inline-block;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">5px</span>;
}

<span class="hljs-selector-class">.about_img</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">300px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">200px</span>;
  <span class="hljs-attribute">object-fit</span>: cover;
}

<span class="hljs-selector-class">.about_caption</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-<span class="hljs-number">3</span>xs) <span class="hljs-number">0</span>;
}

<span class="hljs-selector-tag">section</span><span class="hljs-selector-id">#about</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#ffffff</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
  <span class="hljs-attribute">--color-accent</span>: <span class="hljs-number">#ffd700</span>;
}
</code></pre>
<p>Breakdown of the CSS:</p>
<ul>
<li><p><strong>Skew effect</strong>: The <code>.about_content</code> class gives the section a dynamic look with a slight skew, which is removed on small screens.</p>
</li>
<li><p><strong>Figure styling</strong>: The profile image gets padding and a subtle box shadow to help it stand out.</p>
</li>
<li><p><strong>Image styling</strong>: The image fills the space proportionally, ensuring it doesn't distort.</p>
</li>
<li><p><strong>Caption styling</strong>: Padding around the caption provides better spacing.</p>
</li>
</ul>
<h3 id="heading-about-section-preview">About section preview:</h3>
<p>This is what your About Section will look like after applying these styles:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735812908296/509c6cf0-a8c5-448b-8bb4-a466a76a2cbe.png" alt="Portfolio after adding the About section" class="image--center mx-auto" width="2636" height="1842" loading="lazy"></p>
<h2 id="heading-how-to-build-the-work-section">How to Build the Work Section</h2>
<p>The <strong>Work Section</strong> is where you can showcase the companies or projects you’ve worked with or contributed to. This section helps to demonstrate your experience and the impact you've made, while providing visitors with visual examples of your professional background.</p>
<h3 id="heading-html-for-the-work-section">HTML for the Work Section</h3>
<p>Here’s the HTML structure for the <strong>Work Section</strong>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"work"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper_inner"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text_center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h5"</span>&gt;</span>
        So far I've helped 20+ companies build their web presence and
        improve their technical documentation.
      <span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"list"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"work_logos flex flex_center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/logo1.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Acme Corp"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/logo2.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Acme Innovations"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/logo1.png"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">"Acme Solutions"</span>
            <span class="hljs-attr">width</span>=<span class="hljs-string">"50"</span>
            <span class="hljs-attr">height</span>=<span class="hljs-string">"50"</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/logo3.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Acme Technologies"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/assets/logo1.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Acme Enterprises"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&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">section</span>&gt;</span>
</code></pre>
<p>Breakdown of the Work section:</p>
<ul>
<li><p><strong>Introduction</strong>: The <code>&lt;h4&gt;</code> element introduces the work experience with a statement highlighting how you've helped companies with their web presence and technical documentation.</p>
</li>
<li><p><strong>Logos</strong>: The logos of the companies you've worked with are placed inside an unordered list (<code>&lt;ul&gt;</code>). Each logo is wrapped in a list item (<code>&lt;li&gt;</code>) for better structure and layout.</p>
</li>
<li><p><strong>Flexbox layout</strong>: The logos are positioned in a flexible layout using the <code>flex</code> and <code>flex_center</code> classes. This ensures that the logos are evenly spaced and centered horizontally within the section.</p>
</li>
</ul>
<h3 id="heading-css-styling-for-the-work-section">CSS Styling for the Work Section</h3>
<p>Now let’s add some style to the Work section to make it visually appealing and responsive.</p>
<h4 id="heading-work-section-styling">Work section styling:</h4>
<pre><code class="lang-css"><span class="hljs-comment">/* work section */</span>
<span class="hljs-selector-class">.work_logos</span> {
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-built_in">var</span>(--space-xl);
}

<span class="hljs-selector-tag">section</span><span class="hljs-selector-id">#work</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#87ceeb</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#0c0c0c</span>;
  <span class="hljs-attribute">--color-accent</span>: <span class="hljs-number">#2e8b57</span>;
}
</code></pre>
<h2 id="heading-how-to-build-the-contact-section-and-footer">How to Build the Contact Section and Footer</h2>
<p>The <strong>Contact Section</strong> and <strong>Footer</strong> provide a space for visitors to reach out to you and a final touch for the webpage. It’s essential to have an easy-to-use form for inquiries, along with a footer that includes your copyright information.</p>
<h3 id="heading-html-for-the-contact-section-and-footer">HTML for the Contact Section and Footer</h3>
<p>Here’s the HTML structure for the <strong>Contact Section</strong> and <strong>Footer</strong>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text_left"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        Want to work together? <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
        I'd love to hear from you.
      <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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper_inner"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h3"</span>&gt;</span>Contact Me<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">""</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"contact_form"</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"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>NAME<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</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">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>EMAIL<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</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">"form_group"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"message"</span>&gt;</span>Message<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>SEND MESSAGE<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">section</span>&gt;</span>


<span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"footer"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text_right"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-symbol">&amp;copy;</span> 2025 Spruce Emmanuel<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">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</code></pre>
<p>Breakdown of the HTML:</p>
<ul>
<li><p><strong>Contact section</strong>: This section includes a welcoming text that invites users to reach out, followed by a contact form with fields for the user’s name, email, and a message.</p>
</li>
<li><p><strong>Footer</strong>: The footer includes copyright information, ensuring that visitors know who owns the content. It’s placed at the bottom of the page and gives a professional touch to the site.</p>
</li>
</ul>
<h3 id="heading-css-for-the-contact-section-and-footer">CSS for the Contact Section and Footer</h3>
<p>Now let's style the <strong>Contact Section</strong> and <strong>Footer</strong> to make them both user-friendly and visually appealing.</p>
<h4 id="heading-contact-section-styling">Contact section styling:</h4>
<pre><code class="lang-css"><span class="hljs-comment">/* contact section */</span>
<span class="hljs-selector-class">.contact_form</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-class">.form_group</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
}

<span class="hljs-selector-tag">label</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"text"</span>]</span>,
<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"email"</span>]</span>,
<span class="hljs-selector-tag">textarea</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ccc</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">4px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-tag">textarea</span> {
  <span class="hljs-attribute">resize</span>: vertical;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">150px</span>;
}

<span class="hljs-selector-tag">button</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"submit"</span>]</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#007bff</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-tag">button</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"submit"</span>]</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#0056b3</span>;
}
</code></pre>
<h4 id="heading-footer-section-styling">Footer section styling:</h4>
<pre><code class="lang-css"><span class="hljs-comment">/* footer section */</span>
<span class="hljs-selector-tag">section</span><span class="hljs-selector-id">#footer</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--space-l) <span class="hljs-built_in">var</span>(--space-l);
}
</code></pre>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>And there you have it! You've just built a simple portfolio website using only HTML and CSS. You've learned how to structure your project, apply a CSS reset, and create responsive typography and layout systems that scale beautifully across different screen sizes.</p>
<p>By following this guide, you now have a portfolio that you can be proud of and share with friends, family, or potential employers. The beauty of this project is that you can continue to expand and personalize it by adding new sections, showcasing more of your work, or experimenting with JavaScript to add interactive elements.</p>
<p>If you're interested in exploring more advanced features or learning how to host your portfolio online, check out my tutorial on <a target="_blank" href="https://www.freecodecamp.org/news/host-your-first-project-on-github/">How to Host Your Project on GitHub – Explained With Examples</a>.</p>
<p>If you have any questions, feel free to find me on Twitter at <a target="_blank" href="https://x.com/sprucekhalifa">@sprucekhalifa</a>, and don’t forget to follow me for more tips and updates. Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Your Personalized  GitHub Profile Page – GitHub Profile README Guide ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wanted to spruce up your GitHub profile page and make it stand out? Well, you're in luck!  With just a few simple steps, you can create a personalized GitHub profile that showcases your skills, projects, and personality. Let's dive in. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-personalized-github-profile-page/</link>
                <guid isPermaLink="false">66bb8802c32849d18c5cdc9b</guid>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sahil ]]>
                </dc:creator>
                <pubDate>Wed, 01 May 2024 18:51:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Neon-Green-Bold-Quote-Motivational-Tweet-Instagram-Post.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wanted to spruce up your GitHub profile page and make it stand out? Well, you're in luck! </p>
<p>With just a few simple steps, you can create a personalized GitHub profile that showcases your skills, projects, and personality. Let's dive in. </p>
<h2 id="heading-create-a-new-repository">Create A New Repository</h2>
<p>Go to <a target="_blank" href="https://github.com">GitHub</a> and click on the "New" button to create a new repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/github_new.png" alt="Image" width="600" height="400" loading="lazy">
<em>button to create new repository</em></p>
<h2 id="heading-setup-your-repository">Setup Your Repository</h2>
<p>You should see a new page where you can create the new repo and it should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/new_repo_page.png" alt="Image" width="600" height="400" loading="lazy">
<em>new repository form page</em></p>
<p>Make sure the name of the repository is the same as your GitHub username, otherwise this won't work.  </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/special_note.png" alt="Image" width="600" height="400" loading="lazy">
<em>after filling the details</em></p>
<p>You can see that after entering your username, there's a new note showing right below the repository name field that says, "this will create a special repository that you can use to add a <strong>README.md</strong> to your GitHub profile. Make sure it’s public and initialize it with a README to get started."</p>
<p>The error for the repository name is showing because I already have my profile, you won't face this issue.</p>
<p>So, lets do this. First, keep the GitHub repository public and not private.</p>
<p>Then click on the add a README file checkbox so that after creating the repository, GitHub will automatically add a README file to that repository.</p>
<p>Next, click the "Create repository" button.</p>
<p>After successfully creating the repository, you should be redirected to the repository page, and you should see the <strong>README.md</strong> file.  </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/repo_main_page.png" alt="Image" width="600" height="400" loading="lazy">
<em>repository page after successfully creating the repository</em></p>
<h2 id="heading-add-readme-content">Add ReadMe Content</h2>
<p>Now, click on the "Edit README" button that should be on the right side of the page</p>
<p>Paste the following text into your <strong>README.md</strong> file</p>
<pre><code class="lang-md"><span class="hljs-section"># Hi, I'm <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">YOUR</span> <span class="hljs-attr">NAME</span>&gt;</span></span>! 👋</span>

I am a passionate M.Tech CS student at IIT Kharagpur, driven by a curiosity for technology and a love for writing. My journey involves delving into the world of computer science, exploring new ideas, and sharing my insights with the community.

![<span class="hljs-string">&lt;username&gt;'s Stats</span>](<span class="hljs-link">https://github-readme-stats.vercel.app/api?username=&lt;username&gt;&amp;theme=vue-dark&amp;show_icons=true&amp;hide_border=true&amp;count_private=true</span>)

<span class="hljs-section">## 🚀 About Me</span>

<span class="hljs-bullet">-</span> 🔭 I'm currently pursuing my Master's in Computer Science at IIT Kharagpur.
<span class="hljs-bullet">-</span> 📝 I write in-depth, long-form articles on my website [<span class="hljs-string">theenthusiast.dev</span>](<span class="hljs-link">https://theenthusiast.dev</span>), accumulating over 20k views within just 2 months.
<span class="hljs-bullet">-</span> 🌐 Proud member of the [<span class="hljs-string">Hackernoon Blogging Fellowship</span>](<span class="hljs-link">https://hackernoon.com/</span>), contributing to the tech community.
<span class="hljs-bullet">-</span> ✍️ Content Writer at [<span class="hljs-string">freeCodeCamp</span>](<span class="hljs-link">https://www.freecodecamp.org/</span>), gearing up to share valuable insights with the global coding community.

<span class="hljs-section">## My Articles</span>
<span class="hljs-bullet">-</span> [<span class="hljs-string">JavaScript Engine and Runtime Explained</span>](<span class="hljs-link">https://www.freecodecamp.org/news/javascript-engine-and-runtime-explained/</span>)


<span class="hljs-section">## Tech Stack</span>
[<span class="hljs-string">![My Skills</span>](<span class="hljs-link">https://skillicons.dev/icons?i=js,html,css,wasm</span>)](<span class="hljs-link">https://skillicons.dev</span>)

<span class="hljs-section">## 🌱 Currently Exploring</span>

<span class="hljs-bullet">-</span> 🚀 Learning Full Stack Web Development
<span class="hljs-bullet">  -</span> Exploring the ins and outs of React and Redux for dynamic front-end experiences.
<span class="hljs-bullet">  -</span> Navigating through the world of React Router for seamless page transitions.
<span class="hljs-bullet">  -</span> Styling with Tailwind CSS to create modern and responsive user interfaces.
<span class="hljs-bullet">  -</span> Building server-side applications with Django, a powerful Python web framework.
<span class="hljs-bullet">  -</span> Diving into PostgreSQL for efficient and scalable database management.

 ## 🏆 Achievements

<span class="hljs-bullet">-</span> 🌟 Completed Hacktoberfest 2023 - Contributed to open source projects and celebrated the spirit of collaboration.


<span class="hljs-section">## 📬 Get in Touch</span>

<span class="hljs-bullet">-</span> Connect with me on [<span class="hljs-string">Twitter</span>](<span class="hljs-link">https://twitter.com/introvertedbot</span>)
<span class="hljs-bullet">-</span> Read more of my articles on [<span class="hljs-string">theenthusiast.dev</span>](<span class="hljs-link">https://theenthusiast.dev</span>)

Thanks for stopping by! Let's connect and explore the fascinating world of technology together. 🚀



<span class="xml"><span class="hljs-comment">&lt;!--

Here are some ideas to get you started:

- 🔭 I’m currently working on ...
- 🌱 I’m currently learning ...
- 👯 I’m looking to collaborate on ...
- 🤔 I’m looking for help with ...
- 💬 Ask me about ...
- 📫 How to reach me: ...
- 😄 Pronouns: ...
- ⚡ Fun fact: ...
--&gt;</span></span>
</code></pre>
<p>In the above content, there are three places where you need to add your username:</p>
<ul>
<li>The first one is in the first line: put your name instead of <code>&lt;YOUR NAME&gt;</code></li>
<li>In the second statement, there are two places where you should put your username instead of <code>&lt;username&gt;</code></li>
<li>In the tech stack section I have added very few tech stacks just for this tutorial but you can add even more by adding the names of the tech. Head over to their <a target="_blank" href="https://github.com/tandpfun/skill-icons?tab=readme-ov-file#example">docs to learn more about adding other icons</a>.</li>
</ul>
<p>Also, modify all the other fields according to your needs, all the information is personal so you have to edit most of the information. I have given my information here, so that you'll just need to fill up your details and get an updated profile pretty fast.</p>
<h2 id="heading-commit-changes">Commit Changes</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/commit_changes_confirmation_modal.png" alt="Image" width="600" height="400" loading="lazy">
<em>committing changes</em></p>
<p>That's it. Click on "Commit changes" and you should have a great looking GitHub profile page.</p>
<p>Head over to your Profile page by replacing your username in the following URL <code>https://github.com/&lt;username&gt;</code> and you should see your updated GitHub profile page.</p>
<p>My profile page looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/final_profile_page.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now your GitHub profile page is personalized and ready to impress! </p>
<p>Share your updated profile on Twitter or LinkedIn and tag me—I'd love to check them out. </p>
<p>If you have any feedback or questions, feel free DM me on <a target="_blank" href="https://twitter.com/introvertedbot">twitter</a> or <a target="_blank" href="https://linkedin.com/in/sahil-mahapatra">LinkedIn</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create an Interactive Terminal Portfolio Website ]]>
                </title>
                <description>
                    <![CDATA[ In this article, you will learn how to create an interactive terminal-based portfolio and a résumé in JavaScript. We'll use the jQuery Terminal library (and a few other tools) to create a website that looks like a real terminal. This article will sho... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-an-interactive-terminal-portfolio-website/</link>
                <guid isPermaLink="false">66ba5abdf4ac8da2b2c2e865</guid>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ terminal ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jakub T. Jankiewicz ]]>
                </dc:creator>
                <pubDate>Mon, 29 Apr 2024 14:49:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730895455049/8fefc48c-761d-4ec5-8f60-b6eb2f97a42a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, you will learn how to create an interactive terminal-based portfolio and a résumé in JavaScript. We'll use the <a target="_blank" href="https://terminal.jcubic.pl/">jQuery Terminal library</a> (and a few other tools) to create a website that looks like a real terminal.</p>
<p>This article will show more advanced usage of the jQuery Terminal library. If you want something more basic, you can check this article: <a target="_blank" href="https://itnext.io/how-to-create-interactive-terminal-like-website-888bb0972288">How to create interactive terminal like website with JavaScript</a> that is written for more entry level programmers. You can also read it (or skim it) first before you begin reading this one.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-the-terminal-and-its-history">What is the terminal and its history?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-jquery-terminal">What is jQuery Terminal?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-base-html-file">Base html file</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-initialize-the-terminal">How to Initialize the Terminal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-welcome-message">Welcome message</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-line-gaps">Line Gaps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-colors-to-ascii-art">How to Add Colors to ASCII Art</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-terminal-formatting">Terminal Formatting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-lolcat-library">How to Use the Lolcat Library</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-rainbow-ascii-art-greetings">Rainbow ASCII Art Greetings</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-the-greeting-text-white">How to Make the Greeting Text White</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-your-first-command">How to Make Your First Command</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-default-commands">Default Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-help-commands-executable">How to Make Help Commands Executable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-syntax-highlighting">Syntax Highlighting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tab-completion">Tab Completion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-shell-commands">How to Add Shell Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-completion">How to Improve Completion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-typing-animation-command">Typing Animation Command</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-credits-command">Credits Command</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prefilled-commands">Prefilled Commands</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sharing-link-to-terminal-session">Sharing Link to Terminal Session</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-working-terminal-portfolio-demo">Working Terminal Portfolio Demo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-executables-to-home-directory">Adding Executables to Home Directory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-next">What Next?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-share-what-youve-created">Share what you’ve created</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-terminal-and-its-history">What is the terminal and its history?</h2>
<p>Terminals have a long history. It started as an upgrade from <a target="_blank" href="https://en.wikipedia.org/wiki/Punched_card">punch cards</a>. Computers back in the day used teletypes, which was just a keyboard and a printer. You'd type on the keyboard, and the keystrokes would be sent to the computer (usually mainframe) and the output got printed on a printer.</p>
<p>Later on, the teletypes were replaced with terminals. A terminal was like the dump computer we see today. It was a CRT Monitor with a keyboard. So instead of getting the output on the printer, it would be displayed on the monitor.</p>
<p>Today we still use this type of interface (the Command Line) to talk with computers.</p>
<p>The command line is a terminal emulator and is a big part of Unix systems, like GNU/Linux or MacOS. On Windows, you have PowerShell or cmd.exe file that allows you to type commands and get responses in the form of text. You can also install GNU/Linux system on Windows in the form of WSL. CLI interfaces are used mostly by power users, developers, and system administrators.</p>
<p>If you're new to the command line, you can read this article: <a target="_blank" href="https://www.freecodecamp.org/news/command-line-for-beginners/">Command Line for Beginners – How to Use the Terminal Like a Pro [Full Handbook]</a>.</p>
<h2 id="heading-what-is-jquery-terminal">What is jQuery Terminal?</h2>
<p>jQuery Terminal is a JavaScript library. It's a plugin for the <a target="_blank" href="https://en.wikipedia.org/wiki/JQuery">jQuery library</a>. jQuery Terminal is more like a framework that has jQuery as its dependency. We'll mostly use JavaScript and very little jQuery in this article.</p>
<p>Let's create our terminal-based portfolio using jQuery Terminal.</p>
<h3 id="heading-base-html-file">Base HTML file</h3>
<p>The first thing you need to do is to include jQuery and jQuery Terminal library in your project.</p>
<p>This is a basic HTML 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>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery.terminal/css/jquery.terminal.min.css"</span>/&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery.terminal/js/jquery.terminal.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"my-terminal.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>Then inside the <strong>my-terminal.js</strong> file, we will write our code in JavaScript.</p>
<h3 id="heading-how-to-initialize-the-terminal">How to Initialize the Terminal</h3>
<p>To create a basic terminal, you need to put in this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {};

<span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands);
</code></pre>
<p>The string <code>'body'</code> indicates the CSS selector where terminal should be created. Here we use <code>'body'</code> so the terminal will be the only thing on the page. But it doesn't have to be full screen. You can create a website where the terminal is only part of the page, like in a window that looks like part of the Operating System.</p>
<p>The first argument to the terminal method is called an interpreter. It's a way to add your commands. An object is the simplest way to create them. See <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Getting-Started#creating-the-interpreter">creating the interpreter</a> to learn more.</p>
<p>If the terminal font is too small, you can make it a little bit bigger with CSS custom properties (also known as CSS variables):</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
    <span class="hljs-attribute">--size</span>: <span class="hljs-number">1.2</span>;
}
</code></pre>
<h3 id="heading-welcome-message">Welcome Message</h3>
<p>The first thing we need to do is to get rid of the default greeting message and replace it with nice-looking custom <a target="_blank" href="https://en.wikipedia.org/wiki/ASCII_art">ASCII Art</a>. We will use the <a target="_blank" href="https://en.wikipedia.org/wiki/FIGlet">Filget library</a> written in JavaScript to do this.</p>
<p>There are a few Figlet libraries on npm. We will use a package named <a target="_blank" href="https://www.npmjs.com/package/figlet">figlet</a>.</p>
<p>The first thing you should do is pick the right font. Go to <a target="_blank" href="https://patorjk.com/software/taag/">figlet playground</a> and write the text you want for your greeting. We will use "Terminal Portfolio" and click "Test All". It should display your text with all the fonts. Scroll through the list and pick the font you like.</p>
<p>I picked a font "slant" that looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Przechwycenie-obrazu-ekranu_2024-04-26_22-18-26.png" alt="Terminal Portfolio ASCII Art" width="600" height="400" loading="lazy"></p>
<p>You can copy this text and put into a string, but you will have issues like with the backslash that needs to be escaped using quote characters.</p>
<pre><code class="lang-lisp">const greetings = `  ______                    _             __   ____             __  ____      ___     
 /_  __/__  _________ ___  (<span class="hljs-name">_</span>)___  ____ _/ /  / __ \\____  _____/ /_/ __/___  / (<span class="hljs-name">_</span>)___ 
  / / / _ \\/ ___/ __ \`__ \\/ / __ \\/ __ \`/ /  / /_/ / __ \\/ ___/ __/ /_/ __ \\/ / / __ \\
 / / /  __/ /  / / / / / / / / / / /_/ / /  / ____/ /_/ / /  / /_/ __/ /_/ / / / /_/ /
/_/  \\___/_/  /_/ /_/ /_/_/_/ /_/\\__,_/_/  /_/    \\____/_/   \\__/_/  \\____/_/_/\\____/`

const term = $('body').terminal(<span class="hljs-name">commands</span>, {
    greetings
})<span class="hljs-comment">;</span>
</code></pre>
<p><strong>NOTE</strong>: The second argument to the jQuery Terminal is an object with options – we used a single option <code>greetings</code>.</p>
<p>This doesn't look good and it's hard to modify. Also, if you create the greeting by hardcoding a string, it may get distorted on smaller screens. That's why we will use the figlet library in JavaScript.</p>
<p>First, we need to include the figlet library in HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/figlet/lib/figlet.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>To initialize the library in JavaScript, we need to load the fonts:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> font = <span class="hljs-string">'Slant'</span>;

figlet.defaults({ <span class="hljs-attr">fontPath</span>: <span class="hljs-string">'https://unpkg.com/figlet/fonts/'</span> });
figlet.preloadFonts([font], ready);
</code></pre>
<p>This code will load the <code>'Slant'</code> font and call the function <code>ready</code> when the font is loaded.</p>
<p>So we need to write this function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{

}
</code></pre>
<p>Now we can do two things, we can put the initialization of jQuery Terminal inside that function:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> term;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term =  $(<span class="hljs-string">'body'</span>).terminal(commands, {
      greetings
   });
}
</code></pre>
<p>With this, we can use the <code>greeting</code> option. But we can also use the <code>echo</code> method to render the greeting, and when initializing the terminal we will put <code>null</code> or <code>false</code> as a <code>greetings</code> to disable the default one:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(greetings);
}
</code></pre>
<p>This will work better because the library will initialize the terminal immediately and will not need to wait for loading the fonts.</p>
<p>Note that we still need to define the greetings using figlet. To do this we can write this function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> cols = term.cols();
    <span class="hljs-keyword">return</span> figlet.textSync(text, {
        <span class="hljs-attr">font</span>: font,
        <span class="hljs-attr">width</span>: cols,
        <span class="hljs-attr">whitespaceBreak</span>: <span class="hljs-literal">true</span>
    });
}
</code></pre>
<p>This function uses the <code>figlet::textSync()</code> method to return a string and use <code>terminal::cols()</code>, to get the number of characters per line. With this, we can make our text responsive.</p>
<p>This function can be used inside <code>ready</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(render(<span class="hljs-string">'Terminal Portfolio'</span>));
}
</code></pre>
<p>This will create a string and pass it to the <code>echo</code> method. But this will be the same as with:</p>
<pre><code class="lang-javascript">term.echo(greeting);
</code></pre>
<p>And our hard-coded greetings. So if you resize the terminal, the greetings can still get distorted. To make the text responsive, you need to <code>echo</code> a function. This function will be called on each re-render of the terminal, which will happen when you resize the page.</p>
<p>We can use the arrow function for this:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> render(<span class="hljs-string">'Terminal Portfolio'</span>));
}
</code></pre>
<p>If you want to add some text below the ASCII art, you can do this by concatenating the string after render:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> {
     <span class="hljs-keyword">const</span> ascii = render(<span class="hljs-string">'Terminal Portfolio'</span>);
     <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${ascii}</span>\nWelcome to my Terminal Portfolio\n`</span>;
   });
}
</code></pre>
<p><strong>NOTE</strong>: If you run this code, you will notice that there is an empty line after the ASCII art. This is because the figlet library adds some spaces after the text.</p>
<p>To get rid of this, you can use <code>string::replace()</code> with a regular expression that will remove all spaces and newlines from the end.</p>
<p>We can't use <code>string::trim()</code>, because we don't want to remove the leading lines:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> cols = term.cols();
    <span class="hljs-keyword">return</span> trim(figlet.textSync(text, {
        <span class="hljs-attr">font</span>: font,
        <span class="hljs-attr">width</span>: cols,
        <span class="hljs-attr">whitespaceBreak</span>: <span class="hljs-literal">true</span>
    }));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">trim</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">return</span> str.replace(<span class="hljs-regexp">/[\n\s]+$/</span>, <span class="hljs-string">''</span>);
}
</code></pre>
<p>You can also pause the terminal when it loads the fonts:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>
});

term.pause();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> render(<span class="hljs-string">'Terminal Portfolio'</span>)).resume();
}
</code></pre>
<p>You can can chain terminal methods, the same as with jQuery.</p>
<h3 id="heading-line-gaps">Line Gaps</h3>
<p>If the font you pick creates gaps between the lines, like in this image with font ANSI Shadow:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/Przechwycenie-obrazu-ekranu_2024-05-08_14-06-41.png" alt="Image: ASCII Art with line gaps" width="600" height="400" loading="lazy"></p>
<p>You can remove the gaps by adding the <code>ansi</code> option set to <code>true</code>. The option was added specifically to fix an issue with displaying <a target="_blank" href="https://en.wikipedia.org/wiki/ANSI_art">ANSI Art</a>.</p>
<pre><code class="lang-javascript">term.echo(<span class="hljs-function">() =&gt;</span> render(<span class="hljs-string">'Terminal Portfolio'</span>), { <span class="hljs-attr">ansi</span>: <span class="hljs-literal">true</span> });
</code></pre>
<p>The above ASCII Art will look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/Przechwycenie-obrazu-ekranu_2024-05-08_14-57-16.png" alt="Image: ASCII Art with gaps removed" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-add-colors-to-ascii-art">How to Add Colors to ASCII Art</h2>
<p>You can spice up your ASCII Art by using a library called lolcat. lolcat is a Linux command that can style text in the terminal with rainbow colors. There is also a library called <a target="_blank" href="https://www.npmjs.com/package/isomorphic-lolcat">isomorphic-lolcat</a>, that you can use in JavaScript to make your ASCII Art in rainbow colors.</p>
<h3 id="heading-terminal-formatting">Terminal Formatting</h3>
<p>To use the lolcat library, you first need to know how to change the colors of the terminal.</p>
<p>You can do this using low-level formatting that looks like this:</p>
<pre><code class="lang-lisp">[[b<span class="hljs-comment">;red;]some text]</span>
</code></pre>
<p>The whole text is wrapped in brackets and the formatting of the text is in additional brackets, where each argument is separated by a semicolon. To learn more about the syntax, you can read the Wiki Article: <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Formatting-and-Syntax-Highlighting">Formatting and Syntax Highlighting</a>.</p>
<p>Here, we'll only use a basic change of color. Instead of red, you can use CSS color names, hex color, or <code>rgb()</code>.</p>
<h3 id="heading-how-to-use-the-lolcat-library">How to Use the Lolcat Library</h3>
<p>To use the library, we first need to include it in HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/isomorphic-lolcat"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>To format the string with colors, we can use this function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rainbow</span>(<span class="hljs-params">string</span>) </span>{
    <span class="hljs-keyword">return</span> lolcat.rainbow(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">char, color</span>) </span>{
        char = $.terminal.escape_brackets(char);
        <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;<span class="hljs-subst">${hex(color)}</span>;]<span class="hljs-subst">${char}</span>]`</span>;
    }, string).join(<span class="hljs-string">'\n'</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hex</span>(<span class="hljs-params">color</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">'#'</span> + [color.red, color.green, color.blue].map(<span class="hljs-function"><span class="hljs-params">n</span> =&gt;</span> {
        <span class="hljs-keyword">return</span> n.toString(<span class="hljs-number">16</span>).padStart(<span class="hljs-number">2</span>, <span class="hljs-string">'0'</span>);
    }).join(<span class="hljs-string">''</span>);
}
</code></pre>
<p>The <code>lolcat.rainbow</code> will call a function in every character from the input string, and pass color as an object with RGB values and the character.</p>
<h3 id="heading-rainbow-ascii-art-greetings">Rainbow ASCII Art Greetings</h3>
<p>To use this code, you need to wrap the call to render with <code>rainbow</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> {
     <span class="hljs-keyword">const</span> ascii = rainbow(render(<span class="hljs-string">'Terminal Portfolio'</span>));
     <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${ascii}</span>\nWelcome to my Terminal Portfolio\n`</span>;
   }).resume();
}
</code></pre>
<p>You can also use two calls to echo, since only the Figlet message needs to be executed inside the function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   term.echo(<span class="hljs-function">() =&gt;</span> rainbow(render(<span class="hljs-string">'Terminal Portfolio'</span>)))
       .echo(<span class="hljs-string">'Welcome to my Terminal Portfolio\n'</span>).resume();
}
</code></pre>
<p>You'll notice that when you resize the window, the rainbow changes randomly. This is the default behavior of lolcat. To change it, you need to set the <a target="_blank" href="https://en.wikipedia.org/wiki/Random_seed">random seed</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rand</span>(<span class="hljs-params">max</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * (max + <span class="hljs-number">1</span>));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ready</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">const</span> seed = rand(<span class="hljs-number">256</span>);
   term.echo(<span class="hljs-function">() =&gt;</span> rainbow(render(<span class="hljs-string">'Terminal Portfolio'</span>), seed))
       .echo(<span class="hljs-string">'Welcome to my Terminal Portfolio\n'</span>).resume();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rainbow</span>(<span class="hljs-params">string, seed</span>) </span>{
    <span class="hljs-keyword">return</span> lolcat.rainbow(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">char, color</span>) </span>{
        char = $.terminal.escape_brackets(char);
        <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;<span class="hljs-subst">${hex(color)}</span>;]<span class="hljs-subst">${char}</span>]`</span>;
    }, string, seed).join(<span class="hljs-string">'\n'</span>);
}
</code></pre>
<p>The <code>rand</code> function returns a pseudo-random number from 0 to max value. Here we created a random value from 0 to 256.</p>
<h3 id="heading-how-to-make-the-greeting-text-white">How to Make the Greeting Text White</h3>
<p>As we showed previously, you can make the text white with terminal formatting. You can use:</p>
<ul>
<li><p><code>[[;white;]Welcome to my Terminal Portfolio]</code></p>
</li>
<li><p><code>[[;#fff;]Welcome to my Terminal Portfolio]</code></p>
</li>
<li><p><code>[[;rgb(255,255,255);]Welcome to my Terminal Portfolio]</code></p>
</li>
</ul>
<p>Moreover, if you include additional file XML formatting, you can use XML-like syntax. That makes formatting much easier.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/jquery.terminal/js/xml_formatting.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>After including the above file in HTML, you can use CSS named colors as XML tags:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">white</span>&gt;</span>Welcome to my Terminal Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">white</span>&gt;</span>
</code></pre>
<p>The XML formatting supports more tags like links and images. See <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Formatting-and-Syntax-Highlighting#extension-xml-formatter">Extension XML Formatter</a> for more info.</p>
<p><strong>NOTE</strong>: XML formatter is a function added to <code>$.terminal.defaults.formatters</code>, which transforms the input XML-like text into terminal formatting. You can add the same to your own formatters.</p>
<h2 id="heading-how-to-make-your-first-command">How to Make Your First Command</h2>
<p>After the greeting, we can write our first command. It will be helpful and will work with any commands we add later.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commanns = {
    help() {

    }
};
</code></pre>
<p>This will be our help command where we'll add a list of commands available to our terminal portfolio. We will use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat">Intl.ListFormat</a>, which creates a list of elements with and before the last element.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formatter = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.ListFormat(<span class="hljs-string">'en'</span>, {
  <span class="hljs-attr">style</span>: <span class="hljs-string">'long'</span>,
  <span class="hljs-attr">type</span>: <span class="hljs-string">'conjunction'</span>,
});
</code></pre>
<p>To create a list, we need to use <code>formatter.format()</code> and pass an array of commands. To get that array we can use <code>Object.keys()</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    help() {
        term.echo(<span class="hljs-string">`List of available commands: <span class="hljs-subst">${help}</span>`</span>);
    }
};

<span class="hljs-keyword">const</span> command_list = <span class="hljs-built_in">Object</span>.keys(commands);
<span class="hljs-keyword">const</span> help = formatter.format(command_list);
</code></pre>
<p>When you type help you should see:</p>
<pre><code class="lang-lisp">List of available commands: help
</code></pre>
<p>You also need to add the <code>echo</code> command:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    help() {
        term.echo(<span class="hljs-string">`List of available commands: <span class="hljs-subst">${help}</span>`</span>);
    },
    echo(...args) {
        term.echo(args.join(<span class="hljs-string">' '</span>));
    }
};
</code></pre>
<p>Now the help command works:</p>
<pre><code class="lang-lisp">List of available commands: help and echo
</code></pre>
<p>But if you try to execute 'echo hello' you will get an error:</p>
<pre><code class="lang-lisp">[Arity] Wrong number of arguments. The function 'echo' expects <span class="hljs-number">0</span> got <span class="hljs-number">1</span>!
</code></pre>
<p>By default, jQuery Terminal checks the number of arguments and the number of parameters the function accepts. The problem is that the <code>rest</code> operator makes all arguments optional and the length function property is 0. To fix the issue we need to disable the <code>Arity</code> check with an option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>
});
</code></pre>
<p>Now the echo commands should work.</p>
<h2 id="heading-default-commands">Default Commands</h2>
<p>By default, the jQuery Terminal has two default commands:</p>
<ul>
<li><p><code>clear</code>: this command clears everything on the terminal.</p>
</li>
<li><p><code>exit</code>: this command exits from nested interpreters.</p>
</li>
</ul>
<p>You can disable them by passing the name to the option and setting it to false. Since we won't use nested interpreters, we can disable <code>exit</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">exit</span>: <span class="hljs-literal">false</span>
});
</code></pre>
<p>But <code>clear</code> can be useful. So we can add it to the list of commands:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> command_list = [<span class="hljs-string">'clear'</span>].concat(<span class="hljs-built_in">Object</span>.keys(commands));
</code></pre>
<h2 id="heading-how-to-make-help-commands-executable">How to Make Help Commands Executable</h2>
<p>We can make the UX better to allow clicking on the command and execute it just like when the user types it.</p>
<p>We will need a few things to do this. First, we need to add formatting to each command and add an HTML class attribute. We can also make the command white so it's more visible.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> command_list = <span class="hljs-built_in">Object</span>.keys(commands);
<span class="hljs-keyword">const</span> formatted_list = command_list.map(<span class="hljs-function"><span class="hljs-params">cmd</span> =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;white class="command"&gt;<span class="hljs-subst">${cmd}</span>&lt;/white&gt;`</span>;
});
<span class="hljs-keyword">const</span> help = formatter.format(formatted_list);
</code></pre>
<p>Next is to add <a target="_blank" href="https://en.wikipedia.org/wiki/Affordance">affordance</a>. To indicate that the user can click the command, we need to change the cursor in CSS:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.command</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
}
</code></pre>
<p>The last step is to execute the command when the user clicks the command. We need to add an event handler with jQuery (jQuery Terminal dependency) or we can use the native browser <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener</a>. Here we use jQuery:</p>
<pre><code class="lang-javascript">term.on(<span class="hljs-string">'click'</span>, <span class="hljs-string">'.command'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">const</span> command = $(<span class="hljs-built_in">this</span>).text();
   term.exec(command);
});
</code></pre>
<p><code>terminal::exec()</code> is a way to execute a command programmatically, just like user would type it and press enter.</p>
<p>You can test it by typing <code>help</code> and clicking <code>help</code> again.</p>
<p>Clicking <code>echo</code> will print an empty line. We can fix it by checking if the array of arguments is not empty, before executing <code>terminal::echo()</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    echo(...args) {
        <span class="hljs-keyword">if</span> (args.length &gt; <span class="hljs-number">0</span>) {
            term.echo(args.join(<span class="hljs-string">' '</span>));
        }
    }
};
</code></pre>
<p>Now clicking on <code>echo</code> will only show the executed command.</p>
<p><strong>NOTE</strong>: If for any reason you don't want to show the prompt and the command that has been executed, you can silence the <code>exec</code> by passing <code>true</code> as the second argument.</p>
<pre><code class="lang-javascript">term.exec(<span class="hljs-string">'help'</span>, <span class="hljs-literal">true</span>);
</code></pre>
<h2 id="heading-syntax-highlighting">Syntax Highlighting</h2>
<p>As we discussed earlier, we can use custom syntax highlighting of our shell by pushing a function into <code>$.terminal.defaults.formatters</code>.We can also use the <code>$.terminal.new_formatter</code> helper function.</p>
<p>Let's make our commands white as we type them. The formatter can be an array (of regex and replacement), or a function. We have a fixed number of commands and we only want to make those that are on the list white. We can do this by adding a regular expression:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> any_command_re = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">`^\s*(<span class="hljs-subst">${command_list.join(<span class="hljs-string">'|'</span>)}</span>)`</span>);
</code></pre>
<p>This regular expression will check if, at the beginning of the string, there is an optional whitespace and one of the commands. Right now the regex will look like this: <code>/^\s*(help|echo)/</code>. This is how to create new formatter:</p>
<pre><code class="lang-javascript">$.terminal.new_formatter([any_command_re, <span class="hljs-string">'&lt;white&gt;$1&lt;/white&gt;'</span>]);
</code></pre>
<p>If you would like to make command arguments in different colors, you'll need a function, where you will use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace">String::replace()</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> re = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">`^\s*(<span class="hljs-subst">${command_list.join(<span class="hljs-string">'|'</span>)}</span>) (.*)`</span>);

$.terminal.new_formatter(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">string</span>) </span>{
    <span class="hljs-keyword">return</span> string.replace(re, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_, command, args</span>) </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;white&gt;<span class="hljs-subst">${command}</span>&lt;/white&gt; &lt;aqua&gt;<span class="hljs-subst">${args}</span>&lt;/aqua&gt;`</span>;
    });
});
</code></pre>
<p>This is just an example of using <code>String::replace</code>. If you have just one replacement, you can use an array. This will be the same:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> re = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">`^\s*(<span class="hljs-subst">${command_list.join(<span class="hljs-string">'|'</span>)}</span>)(\s?.*)`</span>);

$.terminal.new_formatter([re, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_, command, args</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;white&gt;<span class="hljs-subst">${command}</span>&lt;/white&gt;&lt;aqua&gt;<span class="hljs-subst">${args}</span>&lt;/aqua&gt;`</span>;
}]);
</code></pre>
<p><strong>NOTE</strong>: If you add the class <code>&lt;white class="command"&gt;</code> to the formatter, you will be able to click on the typed command to execute it again.</p>
<h2 id="heading-tab-completion">Tab Completion</h2>
<p>Another feature we can add is to complete the command when you press the tab key. This is super easy – we only need to add the completion option set to true:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">exit</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">completion</span>: <span class="hljs-literal">true</span>
});
</code></pre>
<p>Now when you type <code>h</code> and press tab, it will complete the command <code>help</code> for you.</p>
<h2 id="heading-how-to-add-shell-commands">How to Add Shell Commands</h2>
<p>Now we can add the most important commands that allow us to navigate through the portfolio. We will implement directories as the main entry point so the user will need to type the <code>ls</code> command to see a list of things, <code>cd</code> into that directory, and <code>ls</code> again to see the contents.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> directories = {
    <span class="hljs-attr">education</span>: [
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;education&lt;/white&gt;'</span>,

        <span class="hljs-string">'* &lt;a href="https://en.wikipedia.org/wiki/Kielce_University_of_Technology"&gt;Kielce University of Technology&lt;/a&gt; &lt;yellow&gt;"Computer Science"&lt;/yellow&gt; 2002-2007 / 2011-2014'</span>,
        <span class="hljs-string">'* &lt;a href="https://pl.wikipedia.org/wiki/Szko%C5%82a_policealna"&gt;Post-secondary&lt;/a&gt; Electronic School &lt;yellow&gt;"Computer Systems"&lt;/yellow&gt; 2000-2002'</span>,
        <span class="hljs-string">'* Electronic &lt;a href="https://en.wikipedia.org/wiki/Technikum_(Polish_education)"&gt;Technikum&lt;/a&gt; with major &lt;yellow&gt;"RTV"&lt;/yellow&gt; 1995-2000'</span>,
        <span class="hljs-string">''</span>
    ],
    <span class="hljs-attr">projects</span>: [
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;Open Source projects&lt;/white&gt;'</span>,
        [
            [<span class="hljs-string">'jQuery Terminal'</span>,
             <span class="hljs-string">'https://terminal.jcubic.pl'</span>,
             <span class="hljs-string">'library that adds terminal interface to websites'</span>
            ],
            [<span class="hljs-string">'LIPS Scheme'</span>,
             <span class="hljs-string">'https://lips.js.org'</span>,
             <span class="hljs-string">'Scheme implementation in JavaScript'</span>
            ],
            [<span class="hljs-string">'Sysend.js'</span>,
             <span class="hljs-string">'https://jcu.bi/sysend'</span>,
             <span class="hljs-string">'Communication between open tabs'</span>
            ],
            [<span class="hljs-string">'Wayne'</span>,
             <span class="hljs-string">'https://jcu.bi/wayne'</span>,
             <span class="hljs-string">'Pure in browser HTTP requests'</span>
            ],
        ].map(<span class="hljs-function">(<span class="hljs-params">[name, url, description = <span class="hljs-string">''</span>]</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-string">`* &lt;a href="<span class="hljs-subst">${url}</span>"&gt;<span class="hljs-subst">${name}</span>&lt;/a&gt; &amp;mdash; &lt;white&gt;<span class="hljs-subst">${description}</span>&lt;/white&gt;`</span>;
        }),
        <span class="hljs-string">''</span>
    ].flat(),
    <span class="hljs-attr">skills</span>: [
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;languages&lt;/white&gt;'</span>,

        [
            <span class="hljs-string">'JavaScript'</span>,
            <span class="hljs-string">'TypeScript'</span>,
            <span class="hljs-string">'Python'</span>,
            <span class="hljs-string">'SQL'</span>,
            <span class="hljs-string">'PHP'</span>,
            <span class="hljs-string">'Bash'</span>
        ].map(<span class="hljs-function"><span class="hljs-params">lang</span> =&gt;</span> <span class="hljs-string">`* &lt;yellow&gt;<span class="hljs-subst">${lang}</span>&lt;/yellow&gt;`</span>),
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;libraries&lt;/white&gt;'</span>,
        [
            <span class="hljs-string">'React.js'</span>,
            <span class="hljs-string">'Redux'</span>,
            <span class="hljs-string">'Jest'</span>,
        ].map(<span class="hljs-function"><span class="hljs-params">lib</span> =&gt;</span> <span class="hljs-string">`* &lt;green&gt;<span class="hljs-subst">${lib}</span>&lt;/green&gt;`</span>),
        <span class="hljs-string">''</span>,
        <span class="hljs-string">'&lt;white&gt;tools&lt;/white&gt;'</span>,
        [
            <span class="hljs-string">'Docker'</span>,
            <span class="hljs-string">'git'</span>,
            <span class="hljs-string">'GNU/Linux'</span>
        ].map(<span class="hljs-function"><span class="hljs-params">lib</span> =&gt;</span> <span class="hljs-string">`* &lt;blue&gt;<span class="hljs-subst">${lib}</span>&lt;/blue&gt;`</span>),
        <span class="hljs-string">''</span>
    ].flat()
};
</code></pre>
<p>This is our basic structure. You can edit it and add your own information. First, we will add a <code>cd</code> command that changes the directory.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> root = <span class="hljs-string">'~'</span>;
<span class="hljs-keyword">let</span> cwd = root;

<span class="hljs-keyword">const</span> commands = {
    cd(dir = <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">if</span> (dir === <span class="hljs-literal">null</span> || (dir === <span class="hljs-string">'..'</span> &amp;&amp; cwd !== root)) {
            cwd = root;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir.startsWith(<span class="hljs-string">'~/'</span>) &amp;&amp; dirs.includes(dir.substring(<span class="hljs-number">2</span>))) {
            cwd = dir;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir.startsWith(<span class="hljs-string">'../'</span>) &amp;&amp; cwd !== root &amp;&amp;
                   dirs.includes(dir.substring(<span class="hljs-number">3</span>))) {
            cwd = root + <span class="hljs-string">'/'</span> + dir.substring(<span class="hljs-number">3</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dirs.includes(dir)) {
            cwd = root + <span class="hljs-string">'/'</span> + dir;
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Wrong directory'</span>);
        }
    }
};
</code></pre>
<p>This will handle all the cases of changing the directory. Next is to add a prompt.</p>
<p>To see what directory we are in, we need to add a custom <code>prompt</code>. We can create a function as a <code>prompt</code> option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> user = <span class="hljs-string">'guest'</span>;
<span class="hljs-keyword">const</span> server = <span class="hljs-string">'freecodecamp.org'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">prompt</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;green&gt;<span class="hljs-subst">${user}</span>@<span class="hljs-subst">${server}</span>&lt;/green&gt;:&lt;blue&gt;<span class="hljs-subst">${cwd}</span>&lt;/blue&gt;$ `</span>;
}
</code></pre>
<p>And use it as an option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">completion</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">exit</span>: <span class="hljs-literal">false</span>,
    prompt
});
</code></pre>
<p>The green color don't look very good, so we can use a color from Ubuntu to make the terminal look more real. We can overwrite the default XML color tags like this:</p>
<pre><code class="lang-javascript">$.terminal.xml_formatter.tags.green = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;#44D544;]`</span>;
};
</code></pre>
<p>Next is the <code>ls</code> command.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">print_home</span>(<span class="hljs-params"></span>) </span>{
     term.echo(dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> {
         <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;blue class="directory"&gt;<span class="hljs-subst">${dir}</span>&lt;/blue&gt;`</span>;
     }).join(<span class="hljs-string">'\n'</span>));
}

<span class="hljs-keyword">const</span> commands = {
    ls(dir = <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">if</span> (dir) {
            <span class="hljs-keyword">if</span> (dir.match(<span class="hljs-regexp">/^~\/?$/</span>)) {
                <span class="hljs-comment">// ls ~ or ls ~/</span>
                print_home();
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir.startsWith(<span class="hljs-string">'~/'</span>)) {
                <span class="hljs-keyword">const</span> path = dir.substring(<span class="hljs-number">2</span>);
                <span class="hljs-keyword">const</span> dirs = path.split(<span class="hljs-string">'/'</span>);
                <span class="hljs-keyword">if</span> (dirs.length &gt; <span class="hljs-number">1</span>) {
                    <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Invalid directory'</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">const</span> dir = dirs[<span class="hljs-number">0</span>];
                    <span class="hljs-built_in">this</span>.echo(directories[dir].join(<span class="hljs-string">'\n'</span>));
                }
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (cwd === root) {
                <span class="hljs-keyword">if</span> (dir <span class="hljs-keyword">in</span> directories) {
                    <span class="hljs-built_in">this</span>.echo(directories[dir].join(<span class="hljs-string">'\n'</span>));
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Invalid directory'</span>);
                }
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (dir === <span class="hljs-string">'..'</span>) {
                print_home();
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error(<span class="hljs-string">'Invalid directory'</span>);
            }
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (cwd === root) {
            print_home();
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">const</span> dir = cwd.substring(<span class="hljs-number">2</span>);
            <span class="hljs-built_in">this</span>.echo(directories[dir].join(<span class="hljs-string">'\n'</span>));
        }
    }
};
</code></pre>
<p>Similar to the green we had before, the blue color is not that great. So we can use the color from Ubuntu again. To do this, we need to use the same custom XML tags for the color blue:</p>
<pre><code class="lang-javascript">$.terminal.xml_formatter.tags.blue = <span class="hljs-function">(<span class="hljs-params">attrs</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;#55F;;<span class="hljs-subst">${attrs.<span class="hljs-keyword">class</span>}</span>]`</span>;
};
</code></pre>
<p>We added the HTML class for a reason. Let's change directory when the user clicks the directory. Just like we did with commands, we can invoke the <code>cd</code> command the same way as as a user would type it.</p>
<pre><code class="lang-javascript">term.on(<span class="hljs-string">'click'</span>, <span class="hljs-string">'.directory'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> dir = $(<span class="hljs-built_in">this</span>).text();
    term.exec(<span class="hljs-string">`cd ~/<span class="hljs-subst">${dir}</span>`</span>);
});
</code></pre>
<p><strong>NOTE</strong>: if you have long command and want to get the text for that command, it's better to use: <code>$(this).data('text')</code>. When the single formatting is wrapped (when text is longer than the width of the terminal) the <code>.text()</code> will no longer have full text, but the full text is always in the <code>data-text</code> HTML attribute.</p>
<p>We also need to update our CSS to change the cursor:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.command</span>, <span class="hljs-selector-class">.directory</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
}
</code></pre>
<h2 id="heading-how-to-improve-completion">How to Improve Completion</h2>
<p>Our completion is not perfect as it only completes the commands. If you'd like to have completion that also handles directories, you need to use a function:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-attr">greetings</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">checkArity</span>: <span class="hljs-literal">false</span>,
    completion(string) {
        <span class="hljs-comment">// in every function we can use `this` to reference term object</span>
        <span class="hljs-keyword">const</span> cmd = <span class="hljs-built_in">this</span>.get_command();
        <span class="hljs-comment">// we process the command to extract the command name</span>
        <span class="hljs-comment">// and the rest of the command (the arguments as one string)</span>
        <span class="hljs-keyword">const</span> { name, rest } = $.terminal.parse_command(cmd);
        <span class="hljs-keyword">if</span> ([<span class="hljs-string">'cd'</span>, <span class="hljs-string">'ls'</span>].includes(name)) {
            <span class="hljs-keyword">if</span> (rest.startsWith(<span class="hljs-string">'~/'</span>)) {
                <span class="hljs-keyword">return</span> dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> <span class="hljs-string">`~/<span class="hljs-subst">${dir}</span>`</span>);
            }
            <span class="hljs-keyword">if</span> (rest.startsWith(<span class="hljs-string">'../'</span>) &amp;&amp; cwd != root) {
                <span class="hljs-keyword">return</span> dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> <span class="hljs-string">`../<span class="hljs-subst">${dir}</span>`</span>);
            }
            <span class="hljs-keyword">if</span> (cwd === root) {
                <span class="hljs-keyword">return</span> dirs;
            }
        }
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.keys(commands);
    },
    prompt
});
</code></pre>
<p><strong>NOTE</strong>: The string argument was left as documentation. It can be used if you only want to complete a single word.</p>
<h2 id="heading-typing-animation-command">Typing Animation Command</h2>
<p>Another command that we will add is an animated joke. We will print random jokes using an API that looks like the user typing.</p>
<p>We will use the <a target="_blank" href="https://jokeapi.dev/">Joke API</a> for this purpose.</p>
<p>The API returns JSON with two types of responses: <code>twopart</code> and a <code>single</code>. This is the code that prints the text on the terminal:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// we use programming jokes so it fit better</span>
<span class="hljs-comment">// developer portfolio</span>
<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://v2.jokeapi.dev/joke/Programming'</span>;
<span class="hljs-keyword">const</span> commands = {
    <span class="hljs-keyword">async</span> joke() {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(url);
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
        <span class="hljs-keyword">if</span> (data.type == <span class="hljs-string">'twopart'</span>) {
            <span class="hljs-comment">// as said before in every function, passed directly</span>
            <span class="hljs-comment">// to the terminal, you can use `this` object</span>
            <span class="hljs-comment">// to reference terminal instance</span>
            <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`Q: <span class="hljs-subst">${data.setup}</span>`</span>);
            <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`A: <span class="hljs-subst">${data.delivery}</span>`</span>);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (data.type === <span class="hljs-string">'single'</span>) {
            <span class="hljs-built_in">this</span>.echo(data.joke);
        }
    },
}
</code></pre>
<p>To add typing animation, you need to add an option to the <code>echo</code> method:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">this</span>.echo(data.joke, { <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>, <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span> });
</code></pre>
<p>There is one caveat: if you have a sequence of typing animations, you need to await for the previous one to finish (the echo will return a promise when animating). When creating such animation you can wrap your code with <code>animation</code> method:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// we use programming jokes so it fits better</span>
<span class="hljs-comment">// developer portfolio</span>
<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://v2.jokeapi.dev/joke/Programming'</span>;
<span class="hljs-keyword">const</span> commands = {
    <span class="hljs-keyword">async</span> joke() {
        <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(url);
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
        <span class="hljs-keyword">if</span> (data.type == <span class="hljs-string">'twopart'</span>) {
            <span class="hljs-comment">// this method allow to create sequence of animations</span>
            <span class="hljs-built_in">this</span>.animation(<span class="hljs-keyword">async</span> () =&gt; {
                <span class="hljs-comment">// as said before in every function, passed </span>
                <span class="hljs-comment">// directly to terminal, you can use `this` object</span>
                <span class="hljs-comment">// to reference terminal instance</span>
                <span class="hljs-comment">// and since we use arrow function we reference</span>
                <span class="hljs-comment">// this from joke function/command</span>
                <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`Q: <span class="hljs-subst">${data.setup}</span>`</span>, {
                    <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>,
                    <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>
                });
                <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.echo(<span class="hljs-string">`A: <span class="hljs-subst">${data.delivery}</span>`</span>, {
                    <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>,
                    <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>
                });
            });
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (data.type === <span class="hljs-string">'single'</span>) {
            <span class="hljs-built_in">this</span>.echo(data.joke, {
                <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span>,
                <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>
            });
        }
    }
};
</code></pre>
<p>You can read more about typing animation in this article: <a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Typing-Animation">Typing Animation</a><a target="_blank" href="https://github.com/jcubic/jquery.terminal/wiki/Typing-Animation#sequence-of-animations">.</a></p>
<h2 id="heading-credits-command">Credits Command</h2>
<p>The last command we will add is a credits command where we will list the JavaScript libraries we used:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> commands = {
    credits() {
        <span class="hljs-keyword">return</span> [
            <span class="hljs-string">''</span>,
            <span class="hljs-string">'&lt;white&gt;Used libraries:&lt;/white&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://terminal.jcubic.pl"&gt;jQuery Terminal&lt;/a&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://github.com/patorjk/figlet.js/"&gt;Figlet.js&lt;/a&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://github.com/jcubic/isomorphic-lolcat"&gt;Isomorphic Lolcat&lt;/a&gt;'</span>,
            <span class="hljs-string">'* &lt;a href="https://jokeapi.dev/"&gt;Joke API&lt;/a&gt;'</span>,
            <span class="hljs-string">''</span>
        ].join(<span class="hljs-string">'\n'</span>);
    }
};
</code></pre>
<p>This is an example of another way to print something on the terminal – if you return something from a function it will be printed. You can also return a <a target="_blank" href="https://www.freecodecamp.org/news/javascript-promises-explained/">Promise</a>, so you can send an <a target="_blank" href="https://en.wikipedia.org/wiki/Ajax_\(programming\)">AJAX</a> request to the server and print the results.</p>
<h2 id="heading-prefilled-commands">Prefilled Commands</h2>
<p>You can make it easier for users to know what to do with the terminal, especially if they are not that familiar with Unix. You can do this by executing example commands:</p>
<pre><code class="lang-javascript">term.exec(command)
</code></pre>
<p>You can also use animation with <code>exec</code>:</p>
<pre><code class="lang-javascript">term.exec(command, { <span class="hljs-attr">typing</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">delay</span>: <span class="hljs-number">50</span> });
</code></pre>
<h2 id="heading-sharing-link-to-terminal-session">Sharing Link to Terminal Session</h2>
<p>Another cool thing that I will show you is recording commands in the URL. You can create whole terminal session and save it in a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/URL/hash">URL hash</a>. To start recoding a session you need to execute the following:</p>
<pre><code class="lang-javascript">term.history_state(<span class="hljs-literal">true</span>);
</code></pre>
<p>When you execute the command <code>echo x</code>, it should create a URL hash that looks like this: <code>#[[0,1,"echo%20x"]]</code>.</p>
<p>To stop recording, you can use:</p>
<pre><code class="lang-javascript">term.history_state(<span class="hljs-literal">false</span>);
</code></pre>
<p>You can write this into a command <code>record start | stop</code>, so it will be easier to record sessions.</p>
<p>The last thing to do to restore the session is to use the option <code>execHash: true</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-comment">/* rest of the options */</span>
    <span class="hljs-attr">execHash</span>: <span class="hljs-literal">true</span>
});
</code></pre>
<p>When you do this and refresh the page, while having the URL hash with the session, it should replay the session and you should see same output as you did when you recorded it.</p>
<p>If you want the <code>exec</code> to be animated you can use this option:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> term = $(<span class="hljs-string">'body'</span>).terminal(commands, {
    <span class="hljs-comment">/* rest of the options */</span>
    <span class="hljs-attr">execHash</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">execAnimation</span>: <span class="hljs-literal">true</span>
});
</code></pre>
<p>To share the link, it’s better to use a URL shortener like <a target="_blank" href="https://tinyurl.com/"><strong>TinyURL</strong></a><strong>.</strong> Make sure you test the shortened URL to see if it works.</p>
<h2 id="heading-how-to-add-executables-to-the-home-directory">How to Add Executables to the Home Directory</h2>
<p>Another thing you can do to improve the portfolio is to help your visitor learn what commands they can use, by introducing executable when running ls. They will look like binaries on the Linux system.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// not every command needs to be binary</span>
<span class="hljs-comment">// we picked those three that works more like real programs</span>
<span class="hljs-keyword">const</span> files = [
    <span class="hljs-string">'joke'</span>,
    <span class="hljs-string">'credits'</span>,
    <span class="hljs-string">'record'</span>
];

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">print_home</span>(<span class="hljs-params"></span>) </span>{
     term.echo(dirs.map(<span class="hljs-function"><span class="hljs-params">dir</span> =&gt;</span> {
         <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;blue class="directory"&gt;<span class="hljs-subst">${dir}</span>&lt;/blue&gt;`</span>;
     }).join(<span class="hljs-string">'\n'</span>));
     term.echo(files.map(<span class="hljs-function"><span class="hljs-params">file</span> =&gt;</span> {
         <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;green class="command"&gt;<span class="hljs-subst">${file}</span>&lt;/green&gt;`</span>;
     }).join(<span class="hljs-string">'\n'</span>));
}
</code></pre>
<p>With this, you will be able to click the command and execute it. So your visitors will know that they can run <code>joke</code> command without the need to type <code>help</code> command. For this to work, we need one last change, adding class to the green XML tag:</p>
<pre><code class="lang-javascript">$.terminal.xml_formatter.tags.green = <span class="hljs-function">(<span class="hljs-params">attrs</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">`[[;#44D544;;<span class="hljs-subst">${attrs.<span class="hljs-keyword">class</span>}</span>]`</span>;
};
</code></pre>
<h2 id="heading-working-terminal-portfolio-demo">Working Terminal Portfolio Demo</h2>
<p>Here is a fully working Demo of our <a target="_blank" href="https://codepen.io/jcubic/full/ZEZPWRY">Interactive Terminal Portfolio Website</a>.</p>
<h2 id="heading-what-next">What Next?</h2>
<p>You can add a lot of commands to this portfolio. The only limitation is your imagination.</p>
<p>You can check these examples for inspiration:</p>
<ul>
<li><p>CodePen Collection with <a target="_blank" href="https://codepen.io/collection/LPjoaW">jQuery Terminal Demos</a>.</p>
</li>
<li><p><a target="_blank" href="https://codepen.io/jcubic/pen/BwBYOZ">Retro (Vintage) Terminal CodePen Demo</a>.</p>
</li>
<li><p><a target="_blank" href="https://terminal.jcubic.pl/examples.php">jQuery Terminal Examples Page</a>.</p>
</li>
<li><p><a target="_blank" href="https://terminal.jcubic.pl/404">Terminal 404 Error Page</a>.</p>
</li>
<li><p><a target="_blank" href="https://fake.terminal.jcubic.pl/">Fake GNU/Linux Terminal</a>.</p>
</li>
</ul>
<p>If you have an idea that is not listed here, you can ask on <a target="_blank" href="https://stackoverflow.com/questions/tagged/jquery-terminal">StackOverflow with jquery-terminal tag</a>. If you have something more time consuming, you can also ask for <a target="_blank" href="https://support.jcubic.pl/">paid support</a>.</p>
<h2 id="heading-share-what-youve-created">Share what you’ve created</h2>
<p>If you create a cool terminal portfolio, you can <a target="_blank" href="http://twitter.com/jcubic">share it and tag me on Twitter</a>. I would love to take a look. Especially if you create something more than what’s included in the tutorial. You can also share on a <a target="_blank" href="https://jcu.bi/chat">terminal chat on my website</a> (it’s a similar terminal portfolio, but with chat).</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Great Personal Portfolio Page – a Handbook for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ As a developer, you'll want to create a digital space that mirrors your unique skills and persona. And a stellar personal portfolio website can help you make your mark in the digital universe.  Imagine a space that not only highlights your finest ach... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-great-personal-portfolio-page-a-step-by-step-guide/</link>
                <guid isPermaLink="false">66b99afb4ed1a5964b770073</guid>
                
                    <category>
                        <![CDATA[ Career development  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ personal development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vahe Aslanyan ]]>
                </dc:creator>
                <pubDate>Fri, 22 Mar 2024 21:40:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/How-to-Create-a-Great-Personal-Portfolio-Page-Cover--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As a developer, you'll want to create a digital space that mirrors your unique skills and persona. And a stellar personal portfolio website can help you make your mark in the digital universe. </p>
<p>Imagine a space that not only highlights your finest achievements but also attracts potential clients and makes a memorable impact on every visitor.</p>
<p>This guide will help you navigate creating an outstanding personal portfolio. We'll dissect the process from choosing an eye-catching layout to crafting content that tells your story. It'll all be aimed at building a page that resonates with who you are and the value you bring. </p>
<p>But the journey doesn't end here. Diving deeper, you'll learn more strategies and insights to help you elevate your portfolio beyond the ordinary. Are you ready? Let's step into the realm of crafting a portfolio that's not just seen but remembered.</p>
<h2 id="heading-table-of-contents">Table Of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-why-is-a-personal-portfolio-website-important">The Importance of a Personal Portfolio Website</a></li>
<li><a class="post-section-overview" href="#heading-how-to-choose-the-right-domain-name">How to Choose the Right Domain Name</a></li>
<li><a class="post-section-overview" href="#heading-how-to-design-your-portfolio-site">How to Design Your Portfolio Site</a></li>
<li><a class="post-section-overview" href="#heading-how-to-pick-the-right-tools">How to Pick the Right Tools</a></li>
<li><a class="post-section-overview" href="#heading-how-to-showcase-your-best-work">How to Showcase Your Best Work</a></li>
<li><a class="post-section-overview" href="#heading-how-to-craft-your-brand-story-and-unique-voice">How to Craft Your Brand Story and Unique Voice</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-additional-content">How to Add Additional Content</a></li>
<li><a class="post-section-overview" href="#heading-what-pages-you-need-in-your-portfolio">Essential Pages for Your Portfolio</a></li>
<li><a class="post-section-overview" href="#heading-how-to-organize-the-home-page">How to Organize the Home Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-keep-your-portfolio-fresh">How to Keep Your Portfolio Fresh</a></li>
<li><a class="post-section-overview" href="#11-conclusion">Conclusion</a></li>
</ol>
<h3 id="heading-short-summary">Short Summary</h3>
<ol>
<li>Building a great personal portfolio page is crucial for showcasing your skills and personality in the digital realm.</li>
<li>This step-by-step guide will help you overcome common challenges like starting, structuring, and designing your portfolio effectively.</li>
<li>Learn how to choose the right domain, design your site, showcase your best work, and craft a unique voice to make a lasting impression.</li>
<li>Discover the essential elements, additional content options, social media strategies, and SEO techniques to maximize your portfolio's impact.</li>
</ol>
<h2 id="heading-why-is-a-personal-portfolio-website-important">Why is a Personal Portfolio Website Important?</h2>
<p>Your portfolio, much like a shop's facade or what you wear, communicates your personal style and standards, offering a reflection of your professional identity. </p>
<p>The structure of your website, the projects you spotlight, and the narrative you weave about your journey play a pivotal role in this representation.</p>
<p>While some have navigated their way to impressive careers without a digital footprint, these days it's become more and more important to have a solid personal portfolio. This is particularly true for tech professionals—ranging from software engineers to data scientists and AI experts.</p>
<p>This digital domain gives you a place to show what sets you apart, embodying the core of your unique value proposition. Without this platform, you leave your professional narrative to chance, permitting others to assemble a fragmented view of your persona.</p>
<p>Not having an online personal portfolio could well dictate whether you secure your dream position and successfully establish a strong personal brand, versus fading into the background. In the digital-first era we navigate, a personal portfolio transcends mere benefit—it emerges as an <a target="_blank" href="https://www.freecodecamp.org/news/personal-branding-for-devs-handbook/">essential instrument for personal branding</a>.</p>
<h3 id="heading-how-a-personal-portfolio-helps-you">How a Personal Portfolio Helps You</h3>
<p>For engineers seeking to carve out a distinctive identity in the tech landscape, the quest to showcase what makes you unique is paramount. While GitHub serves as a testament to your technical abilities, it often speaks a language too specialized for a broader audience.</p>
<p>Similarly, YouTube offers a platform for personality-driven exposure, but the prospect of speaking to a camera for hours may not align with your preferred mode of expression. Herein lies the unparalleled value of a personal portfolio page: it stands as one of the most effective mediums to represent yourself.</p>
<p>When you're creating your personal portfolio, it's crucial to demonstrate your expertise and establish your credibility. This demands clear and concise communication, directly engaging potential clients or employers, showcasing your skills, and fostering an online presence.</p>
<p>These are not mere suggestions but critical measures for contemporary professionals. By crafting a portfolio that not only displays your work but propels your career forward, you assert a distinctive presence in the competitive realm of technology, ensuring your unique contributions are both seen and valued.</p>
<h2 id="heading-how-to-choose-the-right-domain-name">How to Choose the Right Domain Name</h2>
<p>When setting up your personal portfolio website, selecting the perfect domain name is a crucial decision that can impact your online presence significantly. </p>
<p>Your domain name is essentially your online identity, so it's important to choose wisely to make a lasting impression on your visitors and potential clients. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-73.png" alt="Image" width="600" height="400" loading="lazy">
<em>Choosing my domain name - vaheaslanyan.com</em></p>
<p>For my personal portfolio page, I like to keep it simple: firstname+lastname.com or just firstname.com. So for me it is: <a target="_blank" href="http://vaheaslanyan.com">vaheaslanyan.com</a>. (My firstname + lastname.com) </p>
<h3 id="heading-other-examples-are">Other examples are</h3>
<ol>
<li><strong>JohnDoe.com</strong>: This is the most direct approach, using both your first and last name. It's professional and clear for anyone looking for your work.</li>
<li><strong>AlexJohnson.dev</strong>: For those in the tech industry, using a domain extension like .dev can highlight your area of expertise.</li>
<li><strong>MichaelRoberts.design</strong>: Similar to the .dev extension, using .design can be a great choice for professionals in fields like graphic design, UX/UI, or related areas.</li>
<li><strong>Elena.co</strong>: If your first name is unique enough, or if you prefer a shorter URL, you might use just your first name with a less common extension like .co.</li>
<li><strong>MaxCreates.com</strong>: If "Max" is your nickname and you're in a creative industry, this domain clearly indicates what you do and who you are.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-74.png" alt="Image" width="600" height="400" loading="lazy">
<em>My name coming up in Google search results</em></p>
<h3 id="heading-importance-of-a-domain-name">Importance of a Domain Name</h3>
<p>Your domain name is the first thing users see when they land on your website, so it should be memorable, easy to spell, and reflect your brand identity. </p>
<p>Here's why it's critical:</p>
<ul>
<li><strong>It Reflects Your Brand</strong>: A well-chosen domain name can effectively convey your unique voice and what you have to offer. If it is your project portfolio website, then something that reflects your name or nickname is a great way to accomplish this.</li>
<li><strong>It Improves Search Engine Ranking</strong>: Having relevant keywords in your domain name can positively impact your website's SEO.</li>
<li><strong>It Attracts Potential Clients</strong>: A professional and catchy domain name can pique the interest of potential clients and make your website stand out.</li>
</ul>
<h3 id="heading-tips-for-choosing-the-perfect-domain-name">Tips for Choosing the Perfect Domain Name</h3>
<ul>
<li><strong>Keep It Simple</strong>: Opt for a domain name that is easy to remember and type.</li>
<li><strong>Include Keywords</strong>: If possible, incorporate relevant keywords related to your industry or profession.</li>
<li><strong>Consider Your Niche</strong>: Ensure your domain name aligns with the type of work showcased on your portfolio.</li>
<li><strong>Avoid Numbers and Hyphens</strong>: Numbers and hyphens can be confusing and are often forgotten when typing a domain name.</li>
<li><strong>Check Availability</strong>: Verify that the domain name you want is not already taken and is available for purchase. You can use <a target="_blank" href="https://www.namecheap.com/">namecheap.com</a>, <a target="_blank" href="https://www.godaddy.com/nl-nl">godaddy.com</a> and many other online hosting websites.</li>
</ul>
<h2 id="heading-how-to-design-your-portfolio-site">How to Design Your Portfolio Site</h2>
<p>When selecting a design for your portfolio website, it’s important that the theme reflects your professional brand. For example, lawyers typically want a theme that suggests reliability and authority, while educators might choose a Learning Management System (LMS) to share courses. </p>
<p>To find the right design, websites like Envato, ThemeForest, and Figma templates are useful. It's helpful to plan what you want on each page before deciding on a design.</p>
<p>Here's how to approach this process:</p>
<p><strong>Understand Your Brand</strong>: First, define what your brand represents and the impression you want to make. Whether your style is modern and sleek or more traditional and professional, your website's design should align with this.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-75.png" alt="Image" width="600" height="400" loading="lazy">
<em>My personal portfolio homepage</em></p>
<p><strong>Research Design Options</strong>: Explore various design templates on platforms like Envato, ThemeForest, and Figma. These resources offer a wide array of designs suited to different professions, aiding in finding one that matches your vision.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-76.png" alt="Image" width="600" height="400" loading="lazy">
<em>Different site design options</em></p>
<p><strong>Functionality and Features Needs</strong>: Consider the features essential for showcasing your work effectively. For a digital artist, high-resolution galleries might be key, while a developer might need support for embedding code snippets.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-77.png" alt="Image" width="600" height="400" loading="lazy">
<em>Press releases on my site</em></p>
<p><strong>Sketch Your Layouts</strong>: Draft a basic layout for each page of your website before committing to a design. This helps visualize how the design will accommodate your content.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-78.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example wireframes for creating a personal portfolio</em></p>
<p><strong>Prioritize Simplicity and User Experience</strong>: A design that's easy to navigate and not overly complicated will keep the focus on your portfolio and provide a better experience for your visitors.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-79.png" alt="Image" width="600" height="400" loading="lazy">
<em>An example of a simple website design</em></p>
<p><strong>Ensure Compatibility and Responsiveness</strong>: The design should look good and function well across all devices and browsers. Responsiveness ensures your website adapts to any screen size.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-80.png" alt="Image" width="600" height="400" loading="lazy">
<em>An example of a responsive site that looks good on both desktop and mobile</em></p>
<p><strong>Customization Flexibility</strong>: Look for a design that offers customization. Being able to adjust elements like colors, fonts, and layout means you can tailor the design to fit your brand perfectly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-81.png" alt="Image" width="600" height="400" loading="lazy">
<em>An example of a flexible design</em></p>
<p><strong>Test and Gather Feedback</strong>: If possible, test the design with some of your content and ask for feedback from colleagues or mentors. Their insights can help you gauge the design's effectiveness in presenting your work.</p>
<p>By carefully choosing a design that represents your professional brand, you create a strong foundation for a portfolio website that effectively showcases your skills and communicates your unique value to visitors.</p>
<h2 id="heading-how-to-pick-the-right-tools">How to Pick the Right Tools</h2>
<p>Choosing the right tools to build your portfolio involves understanding your needs, evaluating available options, and selecting solutions that align with your skills and goals. </p>
<p>This chapter guides you through this process, from deciding between coding your own site vs using a no-code platform to selecting the perfect hosting provider.</p>
<h3 id="heading-understand-your-needs">Understand Your Needs</h3>
<p>First, it's important to identify your field: are you in the tech industry, or are you in some other line of work?</p>
<p>Your field of work significantly influences your choice of portfolio tools. Tech professionals might lean towards custom-coded sites to showcase their coding prowess, while those in non-tech fields may prefer no-code builders for their convenience and ease of use.</p>
<p>Next, you'll want to evaluate your skills to decide how to actually create the site. Do you want to code your own portfolio, or choose a no-code solution? </p>
<p>Assess your skills honestly. If you're comfortable with coding, a custom-built site might be the best way to showcase your technical abilities. For those without coding skills or a desire to learn, no-code platforms offer a practical and efficient alternative.</p>
<p>Then you'll want to set your website goals and objectives. Define what you want your portfolio to achieve. Are you looking to attract freelance clients, land a full-time job, or simply showcase your work? Your goals can influence the choice of platform, design, and content of your portfolio.</p>
<h3 id="heading-options-for-tech-professionals">Options for Tech Professionals</h3>
<p>Coding your own portfolio from scratch offers complete control over design and functionality, allowing you to create a unique and personalized online presence. It also showcases what you can do with code.</p>
<h4 id="heading-benefits-of-custom-coding">Benefits of Custom Coding</h4>
<ul>
<li>Customization: Tailor every aspect of your site to fit your personal brand.</li>
<li>Skill Demonstration: Showcase your coding ability to potential employers or clients.</li>
</ul>
<h4 id="heading-recommended-technologies-and-frameworks">Recommended Technologies and Frameworks</h4>
<ul>
<li><strong>HTML, CSS, and JavaScript Basics</strong>: The foundation of web development, essential for any custom-built portfolio.</li>
<li><strong>Advanced Frameworks</strong>: Utilize React, Angular, or Vue to create dynamic and responsive sites.</li>
</ul>
<h4 id="heading-hosting-solutions-for-coders">Hosting Solutions for Coders</h4>
<ul>
<li><strong>GitHub Pages</strong>: A free option for hosting simple, static sites.</li>
<li><strong>Netlify and Vercel</strong>: Provide more flexibility and support for dynamic sites built with advanced frameworks.</li>
</ul>
<h3 id="heading-no-code-solutions-for-non-tech-professionals">No-Code Solutions for Non-Tech Professionals</h3>
<p>No-code platforms have democratized web design, enabling anyone to create beautiful, functional websites without writing a single line of code.</p>
<h4 id="heading-comparing-popular-no-code-platforms">Comparing Popular No-Code Platforms</h4>
<ul>
<li><strong>WordPress</strong>: Versatile and widely used, with extensive themes and plugins.</li>
<li><strong>Webflow</strong>: Offers unparalleled customization and flexibility without requiring coding knowledge.</li>
<li><strong>Squarespace</strong>: Known for its ease of use and stunning, design-forward templates.</li>
</ul>
<h3 id="heading-how-to-choose-the-right-hosting-option">How to Choose the Right Hosting Option</h3>
<p>Your hosting provider impacts your site's loading speed, uptime, and overall user experience, making it a crucial factor in your portfolio's success.</p>
<h4 id="heading-some-common-hosting-providers">Some Common Hosting Providers</h4>
<ul>
<li><strong>Namecheap</strong>: Offers affordable, reliable hosting with excellent customer support.</li>
<li><strong>Alternative Options</strong>: Explore other reputable providers like Bluehost and SiteGround based on your specific needs.</li>
</ul>
<p>Choosing the right tools for your personal portfolio requires a thoughtful evaluation of your needs, skills, and goals. Whether through custom coding or no-code platforms, the perfect portfolio is within reach. </p>
<p>By carefully selecting the right tools and platforms, you can create a portfolio that not only showcases your work but also helps you achieve your career aspirations.</p>
<h2 id="heading-how-to-showcase-your-best-work">How to Showcase Your Best Work</h2>
<p>When setting up your portfolio page, it's essential to showcase projects that reflect your current focus in software engineering, data science, AI, or whatever area of tech you call home. </p>
<p>Select projects that are recent and demonstrate your skills and interests in these areas, ensuring your portfolio aligns with the professional direction you're aiming for.</p>
<h3 id="heading-categorize-your-projects">Categorize Your Projects</h3>
<p>Divide your projects into categories like software development, data analysis, or artificial intelligence to help visitors navigate your portfolio more easily. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-82.png" alt="Image" width="600" height="400" loading="lazy">
<em>The project section on my personal portfolio site with clear sections for different topics</em></p>
<p>This organization allows you to display a broad skill set, making it clear you're well-versed in multiple aspects of tech. It also helps visitors quickly find projects that align with their specific interests or needs.</p>
<h3 id="heading-provide-detailed-project-descriptions">Provide Detailed Project Descriptions</h3>
<p>For each project, write a brief description that outlines the project's goals, your role in its development, and the technology used. Highlight any unique challenges you encountered and how you overcame them, showcasing your problem-solving skills. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-86.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example of some project descriptions listing the challenge, goal, technologies used, etc.</em></p>
<p>These descriptions give context to your work and help visitors understand the depth of your technical expertise and creativity.</p>
<h3 id="heading-use-high-quality-visuals-and-code-snippets">Use High-Quality Visuals and Code Snippets</h3>
<p>Incorporate clear screenshots of your projects or UI designs, and consider including code snippets to illustrate the quality of your coding practices. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-85.png" alt="Image" width="600" height="400" loading="lazy">
<em>A high quality visual</em></p>
<p>Visuals help convey the complexity and functionality of your projects, while code snippets can give fellow tech professionals insight into your technical approach. Together, they provide a comprehensive view of your capabilities.</p>
<h3 id="heading-include-client-or-collaborator-testimonials">Include Client or Collaborator Testimonials</h3>
<p>Testimonials from clients, colleagues, or collaborators can add credibility to your portfolio. Positive feedback on your technical skills, work ethic, and ability to deliver solutions can significantly enhance your professional image. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-87.png" alt="Image" width="600" height="400" loading="lazy">
<em>Examples from my testimonials page</em></p>
<p>These testimonials act as a form of social proof, validating your expertise in your field.</p>
<h3 id="heading-highlight-achievements-and-contributions">Highlight Achievements and Contributions</h3>
<p>Showcase any recognitions, certifications, or contributions to open-source projects. </p>
<p>For example, if you've received a "Developer of the Month" award from a well-known tech community, obtained a certification in Advanced Machine Learning from a reputable online platform, or contributed to open-source projects on GitHub like TensorFlow or Apache Spark, make sure these are prominently featured.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-88.png" alt="Image" width="600" height="400" loading="lazy">
<em>Some of my own milestones throughout my career</em></p>
<p>Highlighting such achievements, like winning a hackathon or being part of a team that developed a widely used software tool, showcases your commitment and active engagement in the tech community. </p>
<p>It also positions you as a dedicated and accomplished professional, underlining your contributions and achievements within the fields of software engineering, data science, and AI.</p>
<h3 id="heading-feature-interactive-demos">Feature Interactive Demos</h3>
<p>Where possible, include interactive demos of your projects or links to live sites where visitors can see your work in action. This not only makes your portfolio more engaging but also lets visitors experience the usability and functionality of your creations firsthand. </p>
<p>Interactive demos can be particularly compelling for projects in AI and data science, where results and data visualizations play a crucial role.</p>
<p>To effectively include interactive demos or links to live sites in your portfolio, consider these approaches:</p>
<ol>
<li><strong>Embed Project Videos</strong>: Create short video demonstrations of your projects and embed them directly into your portfolio. These videos can guide viewers through the functionality and features of your creations, providing a dynamic way to showcase your work.</li>
<li><strong>Use GitHub Pages</strong>: For software engineering projects, deploy your web applications or tools on <a target="_blank" href="https://pages.github.com/">GitHub Pages</a>, providing a direct link in your portfolio. This free hosting service allows you to present live versions of your projects, making them accessible to anyone.</li>
<li><strong>Interactive Data Visualization Tools</strong>: For data science projects, utilize tools like <a target="_blank" href="https://public.tableau.com/app/discover">Tableau Public</a> or <a target="_blank" href="https://d3js.org/">D3.js</a> to create interactive visualizations of your data analyses. Embed these visualizations in your portfolio to let visitors interact with the data, understand your analytical skills, and appreciate the insights you've uncovered.</li>
<li><strong>Host on Cloud Platforms</strong>: Use cloud platforms like <a target="_blank" href="https://www.heroku.com/">Heroku</a> or <a target="_blank" href="https://www.netlify.com/">Netlify</a> to deploy your projects and include the live links in your portfolio. These platforms support a wide range of programming languages and frameworks, making them suitable for showcasing both frontend and backend projects.</li>
<li><strong>Demo Notebooks for AI Models</strong>: Share Jupyter notebooks via platforms like GitHub or NBViewer that demonstrate the workings of your AI models. These notebooks can include code, visualizations, and explanations, offering a comprehensive view of your project's development process and results.</li>
<li><strong>Create an Interactive Portfolio Section</strong>: Design a specific area of your portfolio dedicated to interactive demos. This section can include thumbnails or icons representing each project, which visitors can click on to explore the demo or live site.</li>
<li><strong>Use Code Sandboxes</strong>: For smaller projects or code snippets, use online code editors like <a target="_blank" href="https://codepen.io/">CodePen</a> or <a target="_blank" href="https://jsfiddle.net/">JSFiddle</a> to create live previews. These platforms allow you to write HTML, CSS, and JavaScript in the browser and share interactive versions of your work.</li>
</ol>
<p>By incorporating these methods, you can make your portfolio more engaging and provide potential employers or clients with a hands-on understanding of your projects' usability, functionality, and the innovative solutions you bring to the table in software engineering, data science, and AI.</p>
<h3 id="heading-regularly-update-your-portfolio">Regularly Update Your Portfolio</h3>
<p>Keep adding new projects and updating your portfolio to reflect your latest work and ongoing learning in the tech field. This demonstrates your commitment to staying current with technological advancements and your continuous professional development. An up-to-date portfolio shows you're actively involved in your field and passionate about your work.</p>
<p>By tailoring your portfolio with these considerations in mind, you ensure it effectively showcases your expertise in software engineering, data science, and AI. This approach not only highlights your technical skills but also your adaptability and ongoing growth in these rapidly evolving fields.</p>
<h2 id="heading-how-to-craft-your-brand-story-and-unique-voice"><strong>How to Craft Your Brand Story and Unique Voice</strong></h2>
<p>Crafting your brand's voice is a vital aspect of the brand's identity, serving as the embodiment of its personality across all forms of communication. A well-defined brand voice not only distinguishes a brand in a crowded market but also establishes a consistent and relatable presence that resonates with its target audience. </p>
<p>This chapter delves into the intricate process of sculpting your brand's unique voice and narrative, ensuring it aligns with its core values, mission, and the expectations of its audience.</p>
<h3 id="heading-understanding-your-brands-core"><strong>Understanding Your Brand's Core</strong></h3>
<p>The journey to crafting your brand's voice begins with a deep dive into its essence. This involves clarifying your brand's mission, values, and the unique proposition it offers to its audience. </p>
<p>A brand voice that is in harmony with these core aspects will not only be authentic but also compelling to your target audience.</p>
<h3 id="heading-identify-your-audience"><strong>Identify Your Audience</strong></h3>
<p>Understanding and identifying your target audience as a developer is pivotal to creating a personal portfolio website that resonates with the right people, whether they are potential employers, clients, students for your courses, or collaborators. Here's how to refine your approach based on who you're speaking to most often:</p>
<h4 id="heading-for-educators-and-course-creators">For Educators and Course Creators</h4>
<p>If analytics or feedback suggests that a significant portion of your visitors (like the 90% in the example) are potential students interested in your courses, your portfolio should position you as a subject matter expert and a capable educator. Here's how:</p>
<ul>
<li><strong>Showcase Educational Content</strong>: Highlight courses you've created, workshops you've conducted, and any educational materials you offer. Use clear, engaging descriptions and include student testimonials to underscore the value of your courses.</li>
<li><strong>Demonstrate Expertise</strong>: Include a detailed "About Me" section that outlines your credentials, experience, and any awards or recognitions you've received in your field. This helps build trust and establish your authority.</li>
<li><strong>Provide Free Resources</strong>: Offering free tutorials, blog posts, or downloadable resources can be a great way to showcase your knowledge and teaching style, encouraging visitors to engage with your paid courses.</li>
<li><strong>Engage with Interactive Demos</strong>: Use interactive elements or demos related to your courses to engage visitors and give them a taste of what you offer, making your site and courses more memorable.</li>
</ul>
<h4 id="heading-for-developers-seeking-employment">For Developers Seeking Employment</h4>
<p>If your primary goal is to attract potential employers, your portfolio needs to emphasize your technical skills, projects, and the professional value you bring. Here's how to tailor your site:</p>
<ul>
<li><strong>Highlight Relevant Projects</strong>: Showcase a selection of projects that are most relevant to the types of roles you're seeking. Include detailed case studies that walk through your problem-solving process, technologies used, and the outcomes achieved.</li>
<li><strong>Professional Narrative</strong>: Use the "About Me" section to narrate your professional journey, emphasizing experiences and skills that align with your career aspirations. Mention any collaborative projects to demonstrate teamwork and communication skills.</li>
<li><strong>Testimonials from Colleagues</strong>: Including endorsements from past colleagues, supervisors, or collaborators can add credibility and provide insight into your working style and contributions to a team.</li>
<li><strong>Technical Blog</strong>: If you maintain a blog, focus on posts that highlight your expertise, insights into technology trends, and solutions to common problems in your field. This not only showcases your knowledge but also your ability to communicate complex ideas clearly.</li>
</ul>
<h3 id="heading-general-tips-for-identifying-your-audience">General Tips for Identifying Your Audience</h3>
<ul>
<li><strong>Survey Your Visitors</strong>: Simple surveys or feedback forms on your website can provide direct insights into who your visitors are and what they're looking for.</li>
<li><strong>Analyze Website Analytics</strong>: Use tools like Google Analytics to understand the demographics, interests, and behavior of your site's visitors. Look for patterns in the pages visited, the content engaged with, and the referral sources.</li>
<li><strong>Social Media Insights</strong>: If you use social media to share your work or insights, platforms like Twitter, LinkedIn, and Instagram provide analytics that can help you understand your followers' profiles and preferences.</li>
<li><strong>Competitor Analysis</strong>: Look at the portfolios of peers in your field. Notice who their target audience seems to be and how they engage with them. This can offer clues about your own audience.</li>
</ul>
<p>Identifying and understanding your target audience allows you to craft a portfolio that speaks directly to their needs and interests. Whether you're teaching, seeking employment, or offering freelance services, a focused approach ensures that your portfolio resonates with the right people, making it a powerful tool in achieving your professional goals.</p>
<h3 id="heading-craft-your-brand-narrative"><strong>Craft Your Brand Narrative</strong></h3>
<p>Once you have a clear understanding of your brand's core and its audience, the next step is to weave your brand narrative. </p>
<p>This narrative should tell the story of your brand—where it comes from, what it stands for, and where it is headed. </p>
<p>The narrative becomes the foundation upon which your brand voice is built, ensuring that every message you convey is coherent and aligned with your brand's story.</p>
<h3 id="heading-be-aware-of-the-tone-of-your-voice"><strong>Be Aware of the Tone of Your Voice</strong></h3>
<p>The tone of your brand voice can vary depending on the context and the platform, but it should always reflect your brand's personality. </p>
<p>Whether it's confident and authoritative, friendly and conversational, or inspirational and aspirational, the tone should be consistent across all channels to maintain brand recognition and loyalty.</p>
<h3 id="heading-consistency-is-key"><strong>Consistency Is Key</strong></h3>
<p>Maintaining consistency in your brand voice across all platforms and touchpoints is crucial. This consistency helps in building trust and credibility with your audience. It ensures that no matter where or how your audience encounters your brand, they receive the same experience and message.</p>
<h3 id="heading-evolve-your-voice"><strong>Evolve Your Voice</strong></h3>
<p>While consistency is essential, it's also important to allow your brand voice to evolve with your audience and the market. Stay open to feedback and be willing to adjust your voice as needed to ensure it remains relevant and engaging to your audience.</p>
<p>Crafting your brand story and unique voice is not a one-time effort but an ongoing process of refinement and evolution. </p>
<p>By staying true to your brand's core, understanding your audience, and being consistent yet flexible in your approach, you can create a brand voice that not only defines your brand's identity but also builds lasting connections with your audience.</p>
<h2 id="heading-how-to-add-additional-content">How to Add Additional Content</h2>
<p>Enhancing your personal portfolio website with a variety of content not only enriches the visitor's experience but also showcases the breadth of your capabilities. Here are some key additions that can transform your site:</p>
<h3 id="heading-client-testimonials-and-peer-reviews">Client Testimonials and Peer Reviews</h3>
<p>Incorporate feedback from previous clients or colleagues to highlight your skills and professionalism. Testimonials lend authenticity and foster trust in your portfolio. </p>
<p><strong>Example:</strong> a section titled "What People Are Saying" can feature a carousel of quotes from past clients, such as: "Alex transformed our brand's vision into reality with exceptional design and attention to detail. Working with them was a game-changer!" – Jamie, Brand Manager </p>
<h3 id="heading-detailed-case-studies">Detailed Case Studies</h3>
<p>Showcase your standout projects with comprehensive case studies. These narratives offer a deep dive into your creative process, problem-solving abilities, and the impact of your work. </p>
<p><strong>Example:</strong> a page dedicated to a rebranding project for a local café, including the challenge ("Revitalizing a beloved local brand"), the process (from initial sketches to final design choices), and the impact (increased customer engagement and sales).</p>
<h3 id="heading-blog-insights">Blog Insights</h3>
<p>A blog section can serve as a platform for sharing your expertise, industry insights, or personal reflections. It reflects your dedication to your craft and ensures your site remains vibrant with new content. </p>
<p><strong>Example:</strong> a series of posts under "Design Insights," with entries like "The Future of Web Design: Trends to Watch" or "My Creative Process: From Concept to Completion," providing readers with valuable knowledge and a peek into your creative world.</p>
<h3 id="heading-behind-the-scenes-glimpses">Behind-the-Scenes Glimpses</h3>
<p>Offer a look into your creative journey through behind-the-scenes content like sketches, mood boards, or early designs. This transparency into your process can fascinate visitors and add depth to your work presentation. </p>
<p><strong>Example:</strong> a gallery called "The Making of a Logo," featuring early sketches, revisions, and commentary on the thought process behind each stage, culminating in the final design.</p>
<h3 id="heading-engaging-interactive-elements">Engaging Interactive Elements</h3>
<p>Incorporate dynamic features such as sliders, animations, or interactive galleries. These elements can make your portfolio more engaging and leave a lasting impression on visitors. </p>
<p><strong>Example:</strong> an interactive gallery for a photography portfolio, where visitors can filter images by theme or color. Hovering over an image reveals the project name and details, while clicking enlarges the photo with a detailed caption. </p>
<h3 id="heading-awards-and-honors">Awards and Honors</h3>
<p>Highlight any accolades or recognitions you've received. Showcasing your achievements adds prestige to your portfolio and distinguishes you from your peers. </p>
<p><strong>Example:</strong> a "Recognition" section displaying badges or trophies from design competitions, accompanied by a brief description of the award and the project that won it, such as "Winner of the 2023 Design Innovation Award for the Eco-Friendly Packaging Series."</p>
<h3 id="heading-video-demonstrations">Video Demonstrations</h3>
<p>Embedding video content, whether project walkthroughs or client feedback, can provide an immersive experience. Videos add a dynamic layer to your presentation, making your work more relatable and memorable. </p>
<p><strong>Example:</strong> a video walkthrough of a mobile app design process, starting from wireframes to the finished product, with voiceover explaining design choices, challenges overcome, and user feedback incorporated into the final version.</p>
<h3 id="heading-infographics-and-data-visualizations">Infographics and Data Visualizations</h3>
<p>For those whose work involves data, adding infographics or visualizations can compellingly present complex information. This not only showcases your ability to simplify intricate data but also your creative approach to visual communication. </p>
<p><strong>Example:</strong> an infographic titled "The Impact of Good Design on User Engagement," showcasing statistics on user retention, satisfaction, and conversion rates before and after a website redesign, highlighting your ability to drive results through design.</p>
<p>Diversifying the content on your personal portfolio website with additions like testimonials, in-depth case studies, and interactive elements enriches your online presence. Such enhancements not only amplify the appeal and credibility of your portfolio but also demonstrate your comprehensive skill set and creativity.</p>
<h2 id="heading-what-pages-you-need-in-your-portfolio">What Pages You Need in Your Portfolio</h2>
<p>To make your personal portfolio website truly unforgettable, paying close attention to its structure and content is paramount. </p>
<p>The goal is to showcase not just your work and abilities, but to establish a holistic online platform that mirrors your professional identity. Integrating specific essential pages is critical for a detailed presentation of your competencies to prospective employers or clients. </p>
<p>Here’s a detailed guide on the essential pages that are fundamental to a standout personal portfolio:</p>
<h3 id="heading-home-page-the-gateway-to-your-professional-universe">Home Page: The Gateway to Your Professional Universe</h3>
<p>The home page serves as the initial welcome to your professional domain, providing a brief yet impactful overview of your identity and offerings. This first impression is vital, setting the tone for the visitor's experience on your site. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-101.png" alt="Image" width="600" height="400" loading="lazy">
<em>My home page</em></p>
<p>Your home page should emphasize:</p>
<ul>
<li><strong>Engaging Introduction</strong>: Introduce yourself with a quick mention of your professional designation and expertise.</li>
<li><strong>Visual Elements</strong>: Incorporate captivating visuals or a portfolio reel to immediately draw attention to your work.</li>
<li><strong>Navigation Tips</strong>: Direct visitors smoothly to different site sections with clear navigation aids.</li>
</ul>
<h3 id="heading-portfolio-section-a-display-of-your-expertise">Portfolio Section: A Display of Your Expertise</h3>
<p>This core section is where you showcase your finest work. Organizing this area thoughtfully ensures that visitors can easily navigate through your projects. You should include:</p>
<ul>
<li><strong>Organization by Category</strong>: Group projects by theme, type, or sector for straightforward browsing.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-93.png" alt="Image" width="600" height="400" loading="lazy">
<em>Projects organized by category on my personal portfolio page - Python, Statistics, Machine Learning, and NLP</em></p>
<ul>
<li><strong>Project Summaries</strong>: Provide a concise description of each project, outlining your role, the methodology, and the results.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-94.png" alt="Image" width="600" height="400" loading="lazy">
<em>Example project summary on my portfolio</em></p>
<ul>
<li><strong>High-Resolution Media</strong>: Present your work with high-quality images or videos to fully convey its quality.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-95.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-about-me-page-your-professional-narrative">About Me Page: Your Professional Narrative</h3>
<p>This page allows you to forge a deeper connection with your audience by sharing your story, ambitions, and what makes you unique. It should cover:</p>
<ul>
<li><strong>Professional Background</strong>: Share your career path, educational achievements, and significant milestones.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-97.png" alt="Image" width="600" height="400" loading="lazy">
<em>Some of my personal milestones</em></p>
<ul>
<li><strong>Skills and Expertise</strong>: Spotlight the abilities that distinguish you in your field.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-96.png" alt="Image" width="600" height="400" loading="lazy">
<em>A page on my portfolio showing my "digital DNA" - the skills I'd like to showcase</em></p>
<ul>
<li><strong>Personal Insights</strong>: Offering a peek into your personal hobbies or interests can make you more approachable and relatable.</li>
</ul>
<h3 id="heading-services-offered-how-you-can-assist-clients">Services Offered: How You Can Assist Clients</h3>
<p>If your portfolio is meant to attract freelance or consulting work, clearly detail the services you provide. This section should convey:</p>
<ul>
<li><strong>Services List</strong>: Enumerate the services you offer, such as design, consulting, or writing.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-98.png" alt="Image" width="600" height="400" loading="lazy">
<em>A section on my site describing the courses I offer/topics I teach about</em></p>
<ul>
<li><strong>Unique Advantages</strong>: Describe what sets your services apart from the competition.</li>
<li><strong>Workflow Description</strong>: Outline your process from the initial contact to project completion, offering potential clients a glimpse into your working style.</li>
</ul>
<h3 id="heading-contact-information-simplifying-communication">Contact Information: Simplifying Communication</h3>
<p>For potential collaborations, it’s essential to make reaching out as simple as possible. Your contact page should include:</p>
<ul>
<li><strong>Various Contact Methods</strong>: Provide a contact form, email, and possibly a phone number for easy communication.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-99.png" alt="Image" width="600" height="400" loading="lazy">
<em>My contact information page on my personal portfolio site</em></p>
<ul>
<li><strong>Social Media Profiles</strong>: Guide visitors to your professional social media pages.</li>
<li><strong>Geographic Details</strong>: If applicable, mention your location or time zone.</li>
</ul>
<h3 id="heading-client-testimonials-evidence-of-your-expertise">Client Testimonials: Evidence of Your Expertise</h3>
<p>Testimonials from previous clients or colleagues act as a strong endorsement of your skills and dependability. Consider featuring:</p>
<ul>
<li><strong>Client Feedback</strong>: Include concise, meaningful quotes from clients about their satisfaction with your work.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-100.png" alt="Image" width="600" height="400" loading="lazy">
<em>Some of my client's testimonials</em></p>
<ul>
<li><strong>Project Links</strong>: Optionally, connect these testimonials to specific projects they refer to.</li>
<li><strong>Client Images or Logos</strong>: Adding photos or logos of the client's company can add credibility to each testimonial.</li>
</ul>
<p>Incorporating these key pages into your personal portfolio website not only effectively showcases your skills but also offers a complete picture of your professional sphere. </p>
<p>By meticulously designing each section, you can cultivate a digital presence that draws in potential clients or employers, showcases your proficiency, and distinguishes you in your industry.</p>
<h2 id="heading-how-to-organize-the-home-page">How to Organize the Home Page</h2>
<p>Now let's talk about how to strategically organize the homepage of your personal portfolio website. The essence of the homepage is to succinctly represent you and ignite the curiosity of visitors to delve into your portfolio page. Essential sections include:</p>
<h3 id="heading-1-navigation-bar">1. Navigation Bar</h3>
<p>A well-designed navigation bar is your visitor's roadmap to your website. It should be intuitive and straightforward, guiding them through your site with ease. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-102.png" alt="Image" width="600" height="400" loading="lazy">
<em>Navigation section of my site</em></p>
<p>Include links to your portfolio, about page, services, and contact information. Ensure it's accessible from every page, providing a seamless browsing experience.</p>
<h3 id="heading-2-header-hero">2. Header Hero</h3>
<p>Your header hero acts as the first impression and the hook that draws visitors in. This section should feature a striking, high-quality image or a dynamic slider that represents your work or persona. </p>
<p>Accompany this visual with a bold, concise statement about who you are and what you do. This is your chance to make an impact, so choose words and images that resonate with your professional identity and goals.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-103.png" alt="Image" width="600" height="400" loading="lazy">
<em>Homepage displaying the header hero image on my site</em></p>
<p>My hero image has my name in large print, and informs viewers that I specialize in compter science, data science, and AI.</p>
<h3 id="heading-3-your-skills">3. Your Skills</h3>
<p>Highlighting your skills is crucial in showcasing your competencies to potential clients or employers. </p>
<p>Create a visually engaging section where you list your primary skills, perhaps using icons or progress bars for a more dynamic presentation. Focus on the skills that set you apart and are most relevant to the work you want to attract.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-109.png" alt="Image" width="600" height="400" loading="lazy">
<em>Some of my skills listed on my site</em></p>
<p>On my site, I emphasize that I've had experience managing teams, optimizing performance, and building AI-driven products, for example.</p>
<h3 id="heading-4-your-case-studies">4. Your Case Studies</h3>
<p>Case studies are powerful testimonials of your work history and success stories. Dedicate a section to showcase selected projects that highlight your problem-solving skills, creativity, and the value you’ve brought to previous engagements. </p>
<p>For each case study, provide context, your role, challenges, solutions, and results. Including impactful visuals or links to the projects can greatly enhance this section.</p>
<h3 id="heading-5-your-milestones">5. Your Milestones</h3>
<p>This section is a timeline or a list of significant achievements, awards, recognitions, or other milestones in your career. It's a narrative of your professional journey and successes. </p>
<p>Presenting this information in a chronological timeline or a visually engaging infographic can help tell your story compellingly and succinctly. </p>
<p>You can list achievements such as starting a company, graduating from a degree program, reaching a certain number of followers on social media, and so on.</p>
<h3 id="heading-6-testimonials-what-others-say-about-you">6. Testimonials (What others say about you)</h3>
<p>Testimonials from past clients, colleagues, or employers lend credibility and trust to your professional image. Select quotes that reflect your work ethic, impact, and personality. </p>
<p>Displaying these testimonials with the name, title, and, if possible, a photo of the person providing them adds authenticity and a personal touch.</p>
<h3 id="heading-7-technical-skills">7. Technical Skills</h3>
<p>In addition to your general skills, it's important to specifically highlight your technical skills. This section should detail the tools, technologies, software, and programming languages you are proficient in. </p>
<p>Using logos or icons of these technologies can make this section more visually appealing and easier to scan for visitors who are quickly trying to gauge your technical capabilities.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/image-104.png" alt="Image" width="600" height="400" loading="lazy">
<em>My "digital DNA"</em></p>
<p>For example, my "digital DNA" is made up of C++, Java, JavaScript, Python, React, and other popular technologies.</p>
<p>Beyond these, your homepage doesn't require additional content. It's vital to capture the visitor's attention quickly with compelling visuals and effective copywriting, given the short window to make an impact.</p>
<h3 id="heading-dos-for-a-captivating-homepage">Do's for a Captivating Homepage</h3>
<ul>
<li><strong>Be Concise</strong>: Ensure your message is clear and to the point.</li>
<li><strong>Use Endorsement Logos</strong>: Incorporate logos of companies you've appeared on to build trust.</li>
<li><strong>Display Technical Stack Logos</strong>: Show the technologies you're proficient in using their logos.</li>
<li><strong>Showcase a Case Study Gallery</strong>: Highlight your best work in an easily navigable gallery.</li>
<li><strong>Include a Dynamic Header</strong>: Use an engaging hero section to make a strong first impression.</li>
<li><strong>Highlight Recent Work</strong>: Give visitors a glimpse of your latest projects to show you're active and relevant.</li>
<li><strong>Use Testimonials Strategically</strong>: Place client testimonials where they'll have the most impact, showcasing your reliability and skill.</li>
</ul>
<h3 id="heading-donts-for-your-homepage">Don'ts for Your Homepage</h3>
<ul>
<li><strong>Avoid Random Placement</strong>: Structure your content logically – don't place information haphazardly.</li>
<li><strong>Steer Clear of Long Paragraphs</strong>: Keep your homepage breezy and easy to read with short, impactful statements.</li>
<li><strong>Don't Overwhelm with Content</strong>: Your homepage isn't the place to squeeze in every detail of your portfolio.</li>
<li><strong>Avoid Image Neglect</strong>: Images are crucial for visual engagement, so don't omit them.</li>
<li><strong>Resist Clutter</strong>: Keep the design clean and focused to improve user experience.</li>
<li><strong>Avoid Technical Jargon</strong>: Use language that's accessible to all visitors, not just industry insiders.</li>
<li><strong>Skip the Hard Sell</strong>: Present your skills and achievements without being overly aggressive.</li>
</ul>
<p>Effective organization of your homepage is key to creating a memorable and professional personal portfolio website. </p>
<p>By following these guidelines, you ensure that your homepage not only accurately represents your professional persona but also encourages further exploration of your portfolio, opening up new opportunities for engagement and collaboration.</p>
<h2 id="heading-how-to-keep-your-portfolio-fresh">How to Keep Your Portfolio Fresh</h2>
<p>Keeping your portfolio updated is crucial for reflecting your evolving skills, expertise, and experiences. An engaging and current portfolio not only captivates potential clients and visitors but also illustrates your dedication to your profession. Here are strategies to ensure your portfolio remains fresh and appealing:</p>
<h3 id="heading-continuously-update-with-new-work">Continuously Update with New Work</h3>
<p>Regularly add your latest and greatest projects to your portfolio. This demonstrates your active involvement in your field and your commitment to quality and innovation. It's a way to showcase your growth and adaptability over time.</p>
<h3 id="heading-revamp-your-portfolios-design">Revamp Your Portfolio's Design</h3>
<p>Periodically rejuvenating the design of your portfolio website can keep it looking modern and fresh. An updated design can better engage visitors and reflect your ability to stay in tune with the latest trends and technologies in design.</p>
<h3 id="heading-showcase-testimonials-and-recognitions">Showcase Testimonials and Recognitions</h3>
<p>Incorporate new testimonials from clients or colleagues, as well as any recent awards or honors you've received. This external validation of your work adds credibility and can significantly influence potential clients' perceptions of your expertise.</p>
<h3 id="heading-maintain-an-active-blog">Maintain an Active Blog</h3>
<p>A blog can be a dynamic component of your portfolio, offering insights into your work process, industry observations, or detailed project breakdowns. Regular posts keep your site content fresh and can help establish you as a thought leader in your field.</p>
<h3 id="heading-leverage-social-media">Leverage Social Media</h3>
<p>Use social media platforms to highlight recent projects, achievements, and updates from your portfolio. A robust social media presence can extend your reach, attract a broader audience, and keep your work in the spotlight.</p>
<h3 id="heading-incorporate-interactive-elements">Incorporate Interactive Elements</h3>
<p>Adding interactive elements, such as animations or interactive galleries, can refresh the user experience on your site. These elements can make your portfolio more memorable and engaging.</p>
<h3 id="heading-feedback-loop">Feedback Loop</h3>
<p>Invite feedback on your work and website from peers and mentors. Implementing constructive feedback can improve your portfolio's effectiveness and demonstrate your commitment to excellence and continuous improvement.</p>
<h3 id="heading-diversify-your-content">Diversify Your Content</h3>
<p>Show a range of projects and skills, including collaborative works, to display the breadth of your capabilities. This diversity can attract a wider array of clients interested in different aspects of your expertise.</p>
<h3 id="heading-stay-relevant-with-trends">Stay Relevant with Trends</h3>
<p>Keep abreast of the latest trends in your industry and incorporate them into your work and portfolio presentation. This shows that you are forward-thinking and capable of adapting to change.</p>
<p>By employing these strategies, you can ensure that your portfolio remains an accurate, engaging representation of your professional journey and capabilities. Regular updates and a proactive approach to showcasing your achievements can significantly enhance your portfolio's appeal, making it a powerful tool for attracting new opportunities and clients.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When it comes to crafting a standout personal portfolio page, remember that simplicity and showcasing your best work are key. By following the step-by-step guide outlined in this blog, you can create a compelling portfolio that effectively highlights your skills and talents.</p>
<p>Start by organizing your projects in a clean and visually appealing layout. Use high-quality images and concise descriptions to provide visitors with a clear understanding of your work. Remember, your portfolio is a reflection of you and your brand.</p>
<p>Don't forget to include a call to action that prompts visitors to take the next step, whether it's contacting you for collaborations or viewing more of your work. Engage your audience with a clear message and encourage them to explore further. Ready to elevate your online presence? Take the first step and build that portfolio.</p>
<h3 id="heading-resources"><strong>Resources</strong></h3>
<p>Kickstart your journey in technology with our specialized program that dives into Artificial Intelligence (AI) and machine learning. This initiative is crafted to build your programming expertise, supplemented with dedicated mentorship and career guidance to pave your way in the tech industry.</p>
<h3 id="heading-propel-your-career-forward-with-this-curated-list-of-resources-focused-on-tangible-outcomes">Propel your career forward with this curated list of resources, focused on tangible outcomes:</h3>
<ul>
<li><a target="_blank" href="https://downloads.tatevaslanyan.com/six-figure-data-science-ebook">How to Enter Gen AI in 2024:</a> This guide breaks down the essentials of emerging AI technologies and prepares you for future trends.</li>
<li><a target="_blank" href="https://join.lunartech.ai/software-engineering-internship">Land Your Software Engineering Internship:</a> This resource provides step-by-step instructions for finding and landing a valuable internship in software engineering, giving you a competitive edge.</li>
<li><a target="_blank" href="https://join.lunartech.ai/machine-learning-fundamentals--3f64f">Machine Learning Fundamentals eBook:</a> Begin your exploration of machine learning with this eBook, which provides a concise overview of its core principles and techniques.</li>
</ul>
<p>For access to these resources and detailed information about our program, visit <a target="_blank" href="https://lunartech.ai/">LunarTech's</a> website. Embark on your tech career path with the right tools and support from <a target="_blank" href="https://lunartech.ai/">LunarTech</a>.</p>
<h3 id="heading-connect-with-me"><strong>Connect with Me:</strong></h3>
<ul>
<li><a target="_blank" href="https://ca.linkedin.com/in/vahe-aslanyan">Follow me on LinkedIn for a ton of Free Resources in CS, ML and AI</a></li>
<li><a target="_blank" href="https://vaheaslanyan.com/">Visit my Personal Website</a></li>
<li>Subscribe to my <a target="_blank" href="https://tatevaslanyan.substack.com/">The Data Science and AI Newsletter</a></li>
</ul>
<h3 id="heading-about-the-author"><strong>About the Author</strong></h3>
<p>I'm Vahe Aslanyan, specializing in the world of computer science, data science, and artificial intelligence. Explore my work at <a target="_blank" href="https://www.vaheaslanyan.com/">vaheaslanyan.com</a>. My expertise encompasses robust full-stack development and the strategic enhancement of AI products, with a focus on inventive problem-solving.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.vaheaslanyan.com/">https://www.vaheaslanyan.com/</a></div>
<p>I've consistently aimed to revolutionize technical education, striving to set a new, universal standard. As we wrap up this handbook, I want to say a big thank you for spending time with it. Sharing what I've learned has made me think more about my work. I hope what we've gone through together helps you move forward in tech.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Portfolio Site with Sanity and Next.js ]]>
                </title>
                <description>
                    <![CDATA[ By Victor Eke Knowing how to handle content is important when creating a personal website for yourself or a client.  This is because maintaining and updating a site can result in substantial expenses if you don't do it correctly. This is even more th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-portfolio-site-with-sanity-and-nextjs/</link>
                <guid isPermaLink="false">66d4617955db48792eed3fc3</guid>
                
                    <category>
                        <![CDATA[ headless cms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 28 Jul 2023 16:30:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/07/Sanity-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Victor Eke</p>
<p>Knowing how to handle content is important when creating a personal website for yourself or a client. </p>
<p>This is because maintaining and updating a site can result in substantial expenses if you don't do it correctly. This is even more the case if you're building for someone with a non-technical background.</p>
<p>To address this, you can integrate your website with a <a target="_blank" href="https://www.sanity.io/headless-cms">headless CMS</a> service that offers an API for content management and updates. In this case, we will utilize <a target="_blank" href="https://sanity.io">Sanity</a> for this purpose.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-sanity">What is Sanity?</a></li>
<li><a class="post-section-overview" href="#heading-step-1-install-nextjs">Step 1: Install Next.js</a></li>
<li><a class="post-section-overview" href="#heading-step-2-setup-sanity-studio">Step 2: Setup Sanity Studio</a></li>
<li><a class="post-section-overview" href="#heading-step-3-mount-sanity-studio-into-nextjs">Step 3: Mount Sanity Studio into Next.js</a></li>
<li><a class="post-section-overview" href="#heading-step-4-create-content-schemas">Step 4: Create Content Schemas</a></li>
<li><a class="post-section-overview" href="#heading-step-5-query-data-using-groq">Step 5: Query Data using GROQ</a></li>
<li><a class="post-section-overview" href="#heading-step-6-display-content-in-your-nextjs-app">Step 6: Display Content in your Next.js App</a></li>
<li><a class="post-section-overview" href="#heading-fix-studio-layout">Fix Studio Layout</a></li>
<li><a class="post-section-overview" href="#heading-step-7-deployment">Step 7: Deployment</a></li>
<li><a class="post-section-overview" href="#heading-setup-sanity-webhooks-for-studio-update">Setup Sanity Webhooks for Studio Update</a></li>
<li><a class="post-section-overview" href="#heading-what-next">What Next?</a></li>
</ol>
<h2 id="heading-what-is-sanity">What is Sanity?</h2>
<p>Sanity is a headless CMS framework for managing content. It provides tools to leverage APIs to connect to your web app providing instantaneous, rich and automated infrastructure for managing content on the cloud.</p>
<p>With Sanity, you can hook up pages or content that require regular updating to the studio and manage them from the content lake without having to touch code frequently. This makes the content creation and management process accessible to more people regardless of their technical background.</p>
<p>In this post, you'll learn how to use Sanity as a data source to build a portfolio site with Next.js 13 and Tailwind CSS. You'll also learn how to host it on <a target="_blank" href="https://vercel.com">Vercel</a> and set-up webhooks to trigger deployments.</p>
<p>Here is a screenshot of what the portfolio site will look like. Some of the designs for this site were inspired by <a target="_blank" href="https://tailwindui.com/templates/spotlight">Tailwind's Spotlight Portfolio Template</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/final-result-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Finished personal project</em></p>
<p>Want to play around with it? Check out the <a target="_blank" href="https://sanity-nextjs-site.vercel.app">live demo</a>. Also, you can find the source code for the project on <a target="_blank" href="https://github.com/Evavic44/sanity-nextjs-site">GitHub</a>.</p>
<h2 id="heading-step-1-install-nextjs">Step 1: Install Next.js</h2>
<p>Open a terminal and run this command to install the latest version of Next.js:</p>
<pre><code class="lang-bash">npx create-next-app@latest
</code></pre>
<p>Select all your preferred install options. Except for the project name, I'll go with the default options.</p>
<pre><code class="lang-bash">√ What is your project named? ... sanity-nextjs-site
√ Would you like to use TypeScript with this project? ... Yes
√ Would you like to use ESLint with this project? ... Yes
√ Would you like to use Tailwind CSS with this project? ... Yes
√ Would you like to use `src/` directory with this project? ... No
√ Would you like to use App Router? (recommended) ... Yes
√ Would you like to customize the default import <span class="hljs-built_in">alias</span>? ... No
</code></pre>
<p>This should install all the required dependencies, including Tailwind CSS into the project folder. To see it live run the command below:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> sanity-nextjs-site

npm run dev
</code></pre>
<p>Visit <a target="_blank" href="https://localhost:3000">http://localhost:3000</a> to see the site.</p>
<h2 id="heading-step-2-setup-sanity-studio">Step 2: Setup Sanity Studio</h2>
<p>Sanity studio is Sanity's open source single-page app for managing your data and operations. This is the interface from which you can create, delete, and update your data within Sanity.</p>
<h3 id="heading-install-sanity-studio">Install Sanity Studio</h3>
<p>Open up a new terminal outside of your Next.js application and type the commands below:</p>
<pre><code class="lang-bash">mkdir sanity-studio

<span class="hljs-built_in">cd</span> sanity-studio

npm create sanity@latest
</code></pre>
<p>Once your run the command in your terminal, you'll be prompted to select a login provider from the list of options. If you already have an account, it will authenticate your account and automatically log you in or else you can create a new account on Sanity.</p>
<p>Once your account has been successfully authenticated, more prompts will be provided in the terminal to configure your project. Here are the options set for the studio:</p>
<pre><code class="lang-bash">$ Project name: Sanity Next.js Site
$ Use the default dataset configuration?: Yes
$ Project output path: C:\Users\USER\Desktop\sanity-studio
$ Select project template: Clean project with no predefined schemas
$ Do you want to use TypeScript? Yes
$ Package manager to use <span class="hljs-keyword">for</span> installing dependencies?: npm
</code></pre>
<p>Once completed, this should install Sanity studio locally. To see the studio, run <code>npm run dev</code> and visit <a target="_blank" href="http://localhost:3333">localhost:3333</a>, log into your account using the same method used in creating your account, and you should see the studio running locally.</p>
<h2 id="heading-step-3-mount-sanity-studio-into-nextjs">Step 3: Mount Sanity Studio into Next.js</h2>
<p>You can choose to host your studio separately, but in this tutorial you'll be mounting it together with your Next.js application using the <a target="_blank" href="https://github.com/sanity-io/next-sanity">next-sanity</a> toolkit. </p>
<p>End the server running your Next app and run this command:</p>
<pre><code class="lang-bash">npm install sanity next-sanity
</code></pre>
<p>And then on the <code>sanity-studio</code> directory running the studio locally, copy the <code>schema</code> folder and <code>sanity.config.ts</code> file and paste into the root of your Next.js app.</p>
<p>The folder structure should look like this:</p>
<pre><code class="lang-bash">├── .next
├── app/
├── node_modules/
├── public/
├── schemas/
│   └── index.ts
├── .eslintrc.json
├── .gitignore
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── README.md
├── sanity.config.ts
├── tailwind.config.js
└── tsconfig.json
</code></pre>
<p>Next, inside the <code>sanity.config.ts</code> file, add a <code>basePath</code> key and give it a value of <code>/studio</code> or any valid URL path where you would like your studio to live.</p>
<pre><code class="lang-js"><span class="hljs-comment">// sanity.config.ts</span>

<span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"sanity"</span>;
<span class="hljs-keyword">import</span> { deskTool } <span class="hljs-keyword">from</span> <span class="hljs-string">"sanity/desk"</span>;
<span class="hljs-keyword">import</span> { schemaTypes } <span class="hljs-keyword">from</span> <span class="hljs-string">"./schemas"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">name</span>: <span class="hljs-string">"sanity-nextjs-site"</span>,
  <span class="hljs-attr">title</span>: <span class="hljs-string">"Sanity Next.js Site"</span>,
  <span class="hljs-attr">projectId</span>: <span class="hljs-string">"ga8lllhf"</span>,
  <span class="hljs-attr">dataset</span>: <span class="hljs-string">"production"</span>,
  <span class="hljs-attr">basePath</span>: <span class="hljs-string">"/studio"</span>,
  <span class="hljs-attr">plugins</span>: [deskTool()],
  <span class="hljs-attr">schema</span>: { <span class="hljs-attr">types</span>: schemaTypes },
});
</code></pre>
<p>Here's a breakdown of each property:</p>
<ul>
<li><code>name</code>: Used to differentiate workspaces. Not compulsory for single workspace setup.</li>
<li><code>title</code>: Title of your project. This will show up on the Studio.</li>
<li><code>projectId</code>: This is a unique ID that points to the Sanity project you're working with.</li>
<li><code>dataset</code>: The name of the dataset to use for your studio. Common names are <em>production</em> and <em>development</em>.</li>
<li><code>basePath</code>: This is the URL path where your studio will be mounted.</li>
<li><code>schema</code>: The object where your schema files will be defined.</li>
</ul>
<h3 id="heading-create-the-studio-component">Create the Studio Component</h3>
<p>This is where the studio page will be mounted within your Next app. You can name this file whatever you prefer, but it must match with the <code>basePath</code> key specified inside the <code>sanity.config.ts</code> file. In my case, the file name will be <code>studio</code>.</p>
<p>To create the studio route, we'll utilize Next.js dynamic segments. Inside the app directory, create a <code>studio/[[...index]]/page.tsx</code> file. </p>
<pre><code class="lang-bash">app/
└── studio/
    └── [[...index]]/
         └── page.tsx
</code></pre>
<p>With this, when you visit any route that matches with <code>/studio</code>, the studio component <code>page.tsx</code> will be rendered.</p>
<p>To complete this setup, paste this code inside the component:</p>
<pre><code class="lang-js"><span class="hljs-comment">// app/studio/[[...index]]/page.tsx</span>

<span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> { NextStudio } <span class="hljs-keyword">from</span> <span class="hljs-string">"next-sanity/studio"</span>;
<span class="hljs-keyword">import</span> config <span class="hljs-keyword">from</span> <span class="hljs-string">"@/sanity.config"</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">Studio</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NextStudio</span> <span class="hljs-attr">config</span>=<span class="hljs-string">{config}</span> /&gt;</span></span>;
}
</code></pre>
<p>First, <code>NextStudio</code> is imported from the <code>next-sanity</code> library and the configuration file is imported from the <code>sanity.config.ts</code> file you created earlier.</p>
<p>Now run <code>npm run dev</code> and visit <code>localhost:3000/studio</code>. You will get a prompt to add <code>localhost:3000</code> as a CORS origin to your Sanity project. Just click continue to add the URL. </p>
<p>Once added, log into your Sanity account using the same method you used in creating your account and you should see the Studio mounted into your Next.js application as shown in the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/sanity-studio-admin-page-3.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With the studio now running in your Next.js app, you don't need the separate <code>sanity-studio</code> directory anymore. You can delete or close it.</p>
<p>By default, the studio will be blank because you haven't created any schemas files. Let's do that in the next section.</p>
<h2 id="heading-step-4-create-content-schemas">Step 4: Create Content Schemas</h2>
<p>Schemas are essentially a way of organizing datasets in a database depending on what type of content you need. </p>
<p>Since we're building a portfolio site, we'll create schemas to handle projects, profile, and so on. To be more specific, you'll create three schemas files for this portfolio project:</p>
<ul>
<li><code>profile:</code> Schema file for defining your personal information like name, about, skills, and so on.</li>
<li><code>project:</code> Schema file for your projects.</li>
<li><code>work:</code> Schema file for defining your work experience.</li>
</ul>
<p>Let's start with the profile schema.</p>
<h3 id="heading-profile-schema">Profile Schema</h3>
<p>Inside the schemas directory, create a <code>profile.ts</code> file. </p>
<pre><code class="lang-bash">touch schemas/profile.ts
</code></pre>
<p>Let's start by defining the basic properties of a schema file.</p>
<pre><code class="lang-js"><span class="hljs-comment">// schemas/profile.ts</span>

<span class="hljs-keyword">import</span> { defineField } <span class="hljs-keyword">from</span> <span class="hljs-string">"sanity"</span>;
<span class="hljs-keyword">import</span> { BiUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/bi"</span>;

<span class="hljs-keyword">const</span> profile = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">"profile"</span>,
  <span class="hljs-attr">title</span>: <span class="hljs-string">"Profile"</span>,
  <span class="hljs-attr">type</span>: <span class="hljs-string">"document"</span>,
  <span class="hljs-attr">icon</span>: BiUser,
  <span class="hljs-attr">fields</span>: [],
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> profile;
</code></pre>
<p>Each schema file must contain a <code>name</code>, <code>title</code>, and <code>type</code> property. Here's a brief breakdown of the function of each property:</p>
<ul>
<li>The <code>name</code> key is the property that is used to reference a schema in the query language. The value must be a <a target="_blank" href="https://www.sanity.io/help/schema-object-fields-invalid">unique value</a> to avoid conflating schemas.</li>
<li><code>title</code> defines what the schema type is called in the Studio UI.</li>
<li><code>type</code> defines what schema type you're working with. The <code>document</code> value will tell the studio that it should make it possible to make new documents.</li>
<li>The <code>icon</code> is an optional property you can add alongside the <code>title</code>. To use the icon, install the <a target="_blank" href="https://react-icons.github.io/react-icons">react-icons</a> library with the command <code>npm install -D react-icons</code></li>
<li>The <code>fields</code> array, is where the individual input fields will be defined. Here are the fields for the profile schema:</li>
</ul>
<pre><code class="lang-js">fields: [
    defineField({
      <span class="hljs-attr">name</span>: <span class="hljs-string">"fullName"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Full Name"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
      <span class="hljs-attr">validation</span>: <span class="hljs-function">(<span class="hljs-params">rule</span>) =&gt;</span> rule.required(),
    }),
    defineField({
      <span class="hljs-attr">name</span>: <span class="hljs-string">"headline"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Headline"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"In one short sentence, what do you do?"</span>,
      <span class="hljs-attr">validation</span>: <span class="hljs-function">(<span class="hljs-params">Rule</span>) =&gt;</span> Rule.required().min(<span class="hljs-number">40</span>).max(<span class="hljs-number">50</span>),
    }),
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"profileImage"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Profile Image"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"image"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Upload a profile picture"</span>,
      <span class="hljs-attr">options</span>: { <span class="hljs-attr">hotspot</span>: <span class="hljs-literal">true</span> },
      <span class="hljs-attr">fields</span>: [
        {
          <span class="hljs-attr">name</span>: <span class="hljs-string">"alt"</span>,
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Alt"</span>,
          <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
        },
      ],
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"shortBio"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Short Bio"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>,
      <span class="hljs-attr">rows</span>: <span class="hljs-number">4</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"email"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Email Address"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"location"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Location"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"fullBio"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Full Bio"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"array"</span>,
      <span class="hljs-attr">of</span>: [{ <span class="hljs-attr">type</span>: <span class="hljs-string">"block"</span> }],
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"resumeURL"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Upload Resume"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"file"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"socialLinks"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Social Links"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"object"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Add your social media links:"</span>,
      <span class="hljs-attr">fields</span>: [
        {
          <span class="hljs-attr">name</span>: <span class="hljs-string">"github"</span>,
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Github URL"</span>,
          <span class="hljs-attr">type</span>: <span class="hljs-string">"url"</span>,
          <span class="hljs-attr">initialValue</span>: <span class="hljs-string">"https://github.com/"</span>,
        },
        {
          <span class="hljs-attr">name</span>: <span class="hljs-string">"linkedin"</span>,
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Linkedin URL"</span>,
          <span class="hljs-attr">type</span>: <span class="hljs-string">"url"</span>,
          <span class="hljs-attr">initialValue</span>: <span class="hljs-string">"https://linkedin.com/in/"</span>,
        },
        {
          <span class="hljs-attr">name</span>: <span class="hljs-string">"twitter"</span>,
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Twitter URL"</span>,
          <span class="hljs-attr">type</span>: <span class="hljs-string">"url"</span>,
          <span class="hljs-attr">initialValue</span>: <span class="hljs-string">"https://twitter.com/"</span>,
        },
        {
          <span class="hljs-attr">name</span>: <span class="hljs-string">"twitch"</span>,
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Twitch URL"</span>,
          <span class="hljs-attr">type</span>: <span class="hljs-string">"url"</span>,
          <span class="hljs-attr">initialValue</span>: <span class="hljs-string">"https://twitch.com/"</span>,
        },
      ],
      <span class="hljs-attr">options</span>: {
        <span class="hljs-attr">collapsed</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">collapsible</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">columns</span>: <span class="hljs-number">2</span>,
      },
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"skills"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Skills"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"array"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Add a list of skills"</span>,
      <span class="hljs-attr">of</span>: [{ <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span> }],
    },
 ],
</code></pre>
<p>To understand how fields work, visualize each field object as a HTML <code>&lt;input&gt;</code> that will be available in the studio. The value in each input will be exported to a JSON object you can use to inject your data. You can add as many fields, but each must contain a <code>name</code>, <code>title</code>, and <code>type</code> property.</p>
<p>The <code>defineField()</code> helper function helps enable auto-completion of field types in your schema file.</p>
<p>Sanity comes with its own built-in schema types: <code>number</code>, <code>datetime</code>, <code>image</code>, <code>array</code>, <code>object</code>, <code>string</code>, <code>url</code>, and more. You can check out the full list of <a target="_blank" href="https://www.sanity.io/docs/schema-types">schema types here</a>.</p>
<p>To expose this newly created schema file to the Studio, you need to import it into the schemas array inside the <code>schemas/index.ts</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// schemas/index.ts</span>

<span class="hljs-keyword">import</span> profile <span class="hljs-keyword">from</span> <span class="hljs-string">"./profile"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> schemaTypes = [profile];
</code></pre>
<p>Now you can start working with it from within the studio. Visit your studio at <code>localhost:3000/studio</code> or whatever path you used to mount it. Then click on the Profile tab and select the edit button on the top corner to start editing the fields.</p>
<p>This is what that looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/profile-schema-filled.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Fill in all the fields and click publish once completed. This will append the data into a parsed JSON document. To view this JSON output, click the menu button on the top right corner and hit "Inspect" or simply hold down <code>Ctrl Alt I</code> on your keyboard.</p>
<p>Here's what the structure for the profile schema looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/inspect-schema-types-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Inspect schema document</em></p>
<p>With this, you can easily query the data to fetch the exact content you need in your front-end. Let's do that in the next section.</p>
<h2 id="heading-step-5-query-data-using-groq">Step 5: Query Data using GROQ</h2>
<p><a target="_blank" href="https://www.sanity.io/docs/groq">GROQ (Graph-Relational Object Queries)</a> is Sanity's query language designed to query collections of largely schema-less JSON documents. The idea behind the query language is to be able to describe exactly what information you need from your schema, or filter certain data, and return only specific elements from your data</p>
<p>To start using GROQ, first create a <code>sanity/sanity.client.ts</code> file in your project root directory.</p>
<pre><code class="lang-bash">mkdir sanity &amp;&amp; touch sanity/sanity.client.ts
</code></pre>
<p>Paste the code into this file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// sanity/sanity.client.ts</span>

<span class="hljs-keyword">import</span> { createClient, type ClientConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"@sanity/client"</span>;

<span class="hljs-keyword">const</span> config: ClientConfig = {
  <span class="hljs-attr">projectId</span>: <span class="hljs-string">"ga8lllhf"</span>,
  <span class="hljs-attr">dataset</span>: <span class="hljs-string">"production"</span>,
  <span class="hljs-attr">apiVersion</span>: <span class="hljs-string">"2023-07-16"</span>,
  <span class="hljs-attr">useCdn</span>: <span class="hljs-literal">false</span>,
};

<span class="hljs-keyword">const</span> client = createClient(config);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> client;
</code></pre>
<ul>
<li><code>apiVersion</code>:  The version of the Sanity API you're using. For the latest API version, use your current date in this format <code>YYYY-MM-DD</code>.</li>
<li><code>useCdn</code> is used to disable edge cases</li>
</ul>
<p>What this file does is provide a few configurations that will be defined in each query so this is just to avoid repeating it every time. Now for the main query, create a <code>sanity/sanity.query.ts</code> file.</p>
<pre><code class="lang-bash">touch sanity/sanity.query.ts
</code></pre>
<p>Note: There is not clear-cut way to arrange or name these files so feel free to change it up as needed.</p>
<p>Here's the basic query for the profile schema:</p>
<pre><code class="lang-js"><span class="hljs-comment">// sanity/sanity.query.ts</span>

<span class="hljs-keyword">import</span> { groq } <span class="hljs-keyword">from</span> <span class="hljs-string">"next-sanity"</span>;
<span class="hljs-keyword">import</span> client <span class="hljs-keyword">from</span> <span class="hljs-string">"./sanity.client"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getProfile</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> client.fetch(
    groq<span class="hljs-string">`*[_type == "profile"]{
      _id,
      fullName,
      headline,
      profileImage {alt, "image": asset-&gt;url},
      shortBio,
      location,
      fullBio,
      email,
      "resumeURL": resumeURL.asset-&gt;url,
      socialLinks,
      skills
    }`</span>
  );
}
</code></pre>
<p>Here we created an exported async function called <code>getProfile()</code> that returns a groq fetch query wrapped with the client config created in the first step.</p>
<p>The groq query starts with an asterisk (<code>*</code>) which represents every document in your dataset followed by a filter in brackets. The filter above returns the schema that has a <code>_type</code> of "profile".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/profile-type-1.png" alt="Image" width="600" height="400" loading="lazy">
_Schema JSON showing profile schema <em>type</em></p>
<p>The filter is followed by curly braces which contains specific content from the dataset needed like: <code>fullName</code>, <code>headline</code>, <code>profileImage</code> and so on. This is called <a target="_blank" href="https://www.sanity.io/docs/how-queries-work#727ecb6f5e15">projections</a> in the Sanity docs and it returns the entire data as an array.</p>
<p>If you want to learn more about querying using GROQ, I suggest you go through the <a target="_blank" href="https://www.sanity.io/docs/how-queries-work">how queries work</a> section in the documentation. For syntax highlighting of your GROQ query, install the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=sanity-io.vscode-sanity">sanity.io extension</a> available on the Visual Studio Code marketplace.</p>
<p>We're done with the configuration you need to start using your content. Let's look at how to display this content in your Next application.</p>
<h2 id="heading-step-6-display-content-in-your-nextjs-app">Step 6: Display Content in your Next.js App</h2>
<p>This section is broken down into two separate parts: Displaying the hero section, and about page content.</p>
<h3 id="heading-add-types-to-data-content">Add Types to Data Content</h3>
<p>Since you're using TypeScript for this project, it is important to first provide the types for the data coming from the studio.</p>
<p>Create a <code>types/index.ts</code> file in the root directory and add the profile type below:</p>
<pre><code class="lang-js"><span class="hljs-comment">// types/index.ts</span>

<span class="hljs-keyword">import</span> { PortableTextBlock } <span class="hljs-keyword">from</span> <span class="hljs-string">"sanity"</span>;

<span class="hljs-keyword">export</span> type ProfileType = {
  <span class="hljs-attr">_id</span>: string,
  <span class="hljs-attr">fullName</span>: string,
  <span class="hljs-attr">headline</span>: string,
  <span class="hljs-attr">profileImage</span>: {
    <span class="hljs-attr">alt</span>: string,
    <span class="hljs-attr">image</span>: string
  },
  <span class="hljs-attr">shortBio</span>: string,
  <span class="hljs-attr">email</span>: string,
  <span class="hljs-attr">fullBio</span>: PortableTextBlock[],
  <span class="hljs-attr">location</span>: string,
  <span class="hljs-attr">resumeURL</span>: string,
  <span class="hljs-attr">socialLinks</span>: string[],
  <span class="hljs-attr">skills</span>: string[],
};
</code></pre>
<p><code>PortableTextBlock</code> is a unique type coming from Sanity that properly defines the data type for the rich text editor.</p>
<p>Now you've defined the types for your content, it's easier to visualize the data you're expecting in your studio.</p>
<h3 id="heading-display-hero-section">Display Hero Section</h3>
<p>First, remove all the styling inside the <code>global.css</code> file, except for the necessary Tailwind imports at the top. Then clear everything inside the root <code>page.tsx</code> file of your Next.js app and paste the following code inside:</p>
<pre><code class="lang-js"><span class="hljs-comment">// app/page.tsx</span>

<span class="hljs-keyword">import</span> { getProfile } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/sanity/sanity.query"</span>;
<span class="hljs-keyword">import</span> type { ProfileType } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;
<span class="hljs-keyword">import</span> HeroSvg <span class="hljs-keyword">from</span> <span class="hljs-string">"./icons/HeroSvg"</span>;;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> profile: ProfileType[] = <span class="hljs-keyword">await</span> getProfile();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-7xl mx-auto lg:px-16 px-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex xl:flex-row flex-col xl:items-center items-start xl:justify-center justify-between gap-x-12 lg:mt-32 mt-20 mb-16"</span>&gt;</span>
        {profile &amp;&amp;
          profile.map((data) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{data._id}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:max-w-2xl max-w-2xl"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold tracking-tight sm:text-5xl mb-6 lg:leading-[3.7rem] leading-tight lg:min-w-[700px] min-w-full"</span>&gt;</span>
                {data.headline}
              <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base text-zinc-400 leading-relaxed"</span>&gt;</span>
                {data.shortBio}
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-x-6 my-10"</span>&gt;</span>
                {Object.entries(data.socialLinks)
                  .sort()
                  .map(([key, value], id) =&gt; (
                    <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>
                      <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                        <span class="hljs-attr">href</span>=<span class="hljs-string">{value}</span>
                        <span class="hljs-attr">rel</span>=<span class="hljs-string">"noreferer noopener"</span>
                        <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-x-3 mb-5 hover:text-purple-400 duration-300"</span>
                      &gt;</span>
                        {key[0].toUpperCase() + key.toLowerCase().slice(1)}
                      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  ))}
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          ))}
        <span class="hljs-tag">&lt;<span class="hljs-name">HeroSvg</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<ul>
<li>First the <code>getProfile</code> query is imported from the <code>sanity.query.ts</code> file which is a filtered-down version of our data coming from the schema.</li>
<li><code>ProfileType</code> is imported to add types to the data.</li>
<li>The <code>profile</code> array is mapped inside the component to return the <code>headline</code>, <code>shortBio</code>, and <code>socialLinks</code>.</li>
<li><code>&lt;HeroSvg /&gt;</code> is essentially an <code>svg</code> element imported as a react component added just for UI aesthetics. You can download the <a target="_blank" href="https://github.com/Evavic44/sanity-nextjs-site/blob/main/app/(site)/icons/HeroSvg.tsx">HeroSVG icon component</a>.</li>
</ul>
<p>Here's the resulting output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/hero-section-content-result-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>hero section output</em></p>
<p>To speed things up, I've created the navbar and footer navigation components. Simply <a target="_blank" href="https://github.com/Evavic44/sanity-nextjs-site/tree/main/app/(site)/components/global">download the directory</a> and import them into the <code>layout.tsx</code> file like so:</p>
<pre><code class="lang-js"><span class="hljs-comment">// app/layout.tsx</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./globals.css"</span>;
<span class="hljs-keyword">import</span> type { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;
<span class="hljs-keyword">import</span> { Inter } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/font/google"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/global/Navbar"</span>;
<span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/global/Footer"</span>;

<span class="hljs-keyword">const</span> inter = Inter({ <span class="hljs-attr">subsets</span>: [<span class="hljs-string">"latin"</span>] });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata: Metadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">"Sanity Next.js Portfolio Site"</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">"A personal portfolio site built with Sanity and Next.js"</span>,
  <span class="hljs-attr">openGraph</span>: {
    <span class="hljs-attr">images</span>: <span class="hljs-string">"add-your-open-graph-image-url-here"</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">RootLayout</span>(<span class="hljs-params">{children}: {children: React.ReactNode}</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">inter.className</span>} <span class="hljs-attr">bg-zinc-900</span> <span class="hljs-attr">text-white</span>`}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
        {children}
        <span class="hljs-tag">&lt;<span class="hljs-name">Footer</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></span>
  );
}
</code></pre>
<p>With these components, the home page should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/hero-section-with-component-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>home page with navbar and footer components</em></p>
<h3 id="heading-display-about-page">Display About Page</h3>
<p>Let's build the about page using content from the <code>getProfile</code> query as well. In this section you'll need to install a React library called <code>PortableTextBlock</code> by Sanity. This library will allow you easily de-structure the block content of the rich text editor.</p>
<p>To install this package run <code>npm install -D @portabletext/react</code> and I'll explain how to use it later on. </p>
<p>Create an <code>about</code> folder inside the <code>app</code> directory and add a <code>page.tsx</code> file inside this new folder. You can also do this quickly using the following command:</p>
<pre><code class="lang-js">mkdir app/about &amp;&amp; touch app/about/page.tsx
</code></pre>
<p>Here's the code snippet for the about page:</p>
<pre><code class="lang-js"><span class="hljs-comment">// app/about/page.tsx</span>

<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;
<span class="hljs-keyword">import</span> { getProfile } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/sanity/sanity.query"</span>;
<span class="hljs-keyword">import</span> type { ProfileType } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;
<span class="hljs-keyword">import</span> { PortableText } <span class="hljs-keyword">from</span> <span class="hljs-string">"@portabletext/react"</span>;
<span class="hljs-keyword">import</span> { BiEnvelope, BiFile } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/bi"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> profile: ProfileType[] = <span class="hljs-keyword">await</span> getProfile();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:max-w-7xl mx-auto max-w-3xl md:px-16 px-6"</span>&gt;</span>
      {profile &amp;&amp;
        profile.map((data) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{data._id}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid lg:grid-cols-2 grid-cols-1 gap-x-6 justify-items-center"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"order-2 lg:order-none"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:text-5xl text-4xl lg:leading-tight basis-1/2 font-bold mb-8"</span>&gt;</span>
                  I<span class="hljs-symbol">&amp;apos;</span>m {data.fullName}. I live in {data.location}, where I
                  design the future.
                <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">className</span>=<span class="hljs-string">"flex flex-col gap-y-3 text-zinc-400 leading-relaxed"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">PortableText</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{data.fullBio}</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">className</span>=<span class="hljs-string">"flex flex-col lg:justify-self-center justify-self-start gap-y-8 lg:order-1 order-none mb-12"</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">Image</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"rounded-2xl mb-4 object-cover max-h-96 min-h-96 bg-top bg-[#1d1d20]"</span>
                    <span class="hljs-attr">src</span>=<span class="hljs-string">{data.profileImage.image}</span>
                    <span class="hljs-attr">width</span>=<span class="hljs-string">{400}</span>
                    <span class="hljs-attr">height</span>=<span class="hljs-string">{400}</span>
                    <span class="hljs-attr">quality</span>=<span class="hljs-string">{100}</span>
                    <span class="hljs-attr">alt</span>=<span class="hljs-string">{data.profileImage.alt}</span>
                  /&gt;</span>

                  <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                    <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.resumeURL</span>}?<span class="hljs-attr">dl</span>=<span class="hljs-string">${data.fullName}_resume</span>`}
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center justify-center gap-x-2 bg-[#1d1d20] border border-transparent hover:border-zinc-700 rounded-md duration-200 py-2 text-center cursor-cell font-medium"</span>
                  &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">BiFile</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base"</span> /&gt;</span> Download Resumé
                  <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">ul</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                      <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">mailto:</span>${<span class="hljs-attr">data.email</span>}`}
                      <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-x-2 hover:text-purple-400 duration-300"</span>
                    &gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">BiEnvelope</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-lg"</span> /&gt;</span>
                      {data.email}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-24 max-w-2xl"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold text-4xl mb-4"</span>&gt;</span>Expertise<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">className</span>=<span class="hljs-string">"text-zinc-400 max-w-lg"</span>&gt;</span>
                I<span class="hljs-symbol">&amp;apos;</span>ve spent few years working on my skills. In no particular
                order, here are a few of them.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center gap-3 mt-8"</span>&gt;</span>
                {data.skills.map((skill, id) =&gt; (
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>
                    <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-[#1d1d20] border border-transparent hover:border-zinc-700 rounded-md px-2 py-1"</span>
                  &gt;</span>
                    {skill}
                  <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 class="hljs-tag">&lt;/<span class="hljs-name">section</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">main</span>&gt;</span></span>
  );
}
</code></pre>
<ul>
<li>Similar to the home page, we're also fetching the data from the <code>getProfile</code> query and assigning the <code>ProfileType</code> for type safety.</li>
<li>The profile data is also mapped to get the individual properties: <code>fullName</code>, <code>location</code>, <code>fullBio</code>, <code>profileImage</code>, <code>resumeURL</code>, <code>email</code>, and <code>skills</code> array.</li>
<li>The portable text editor was de-structured using the <code>&lt;PortableText /&gt;</code> component which takes in a value prop that receives the content of the rich text editor.</li>
</ul>
<p>Adding the image from Sanity's CDN should throw an error in Next.js since you haven't added Sanity's image source hostname in your <code>next.config.ts</code> file. Here's how to do it in Next.js 13:</p>
<pre><code class="lang-js"><span class="hljs-comment">// next.config.ts</span>

<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('next').NextConfig}</span> </span>*/</span>
<span class="hljs-keyword">const</span> nextConfig = {};

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">images</span>: {
    <span class="hljs-attr">remotePatterns</span>: [
      {
        <span class="hljs-attr">protocol</span>: <span class="hljs-string">"https"</span>,
        <span class="hljs-attr">hostname</span>: <span class="hljs-string">"cdn.sanity.io"</span>,
        <span class="hljs-attr">port</span>: <span class="hljs-string">""</span>,
      },
    ],
  },
};
</code></pre>
<p>Here's the resulting output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/about-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>About page</em></p>
<h3 id="heading-work-experience">Work Experience</h3>
<p>In a typical portfolio site, you may need to create a list of past work experience. This is what the schema would look like:</p>
<p>Create a <code>schemas/job.ts</code> file and paste the following code:</p>
<pre><code class="lang-js"><span class="hljs-comment">// schemas/job.ts</span>

<span class="hljs-keyword">import</span> { BiBriefcase } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/bi"</span>;

<span class="hljs-keyword">const</span> job = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">"job"</span>,
  <span class="hljs-attr">title</span>: <span class="hljs-string">"Job"</span>,
  <span class="hljs-attr">type</span>: <span class="hljs-string">"document"</span>,
  <span class="hljs-attr">icon</span>: BiBriefcase,
  <span class="hljs-attr">fields</span>: [
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"name"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Company Name"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"What is the name of the company?"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"jobTitle"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Job Title"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Enter the job title. E.g: Software Developer"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"logo"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Company Logo"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"image"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"url"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Company Website"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"url"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"description"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Job Description"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>,
      <span class="hljs-attr">rows</span>: <span class="hljs-number">3</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Write a brief description about this role"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"startDate"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Start Date"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"date"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"endDate"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"End Date"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"date"</span>,
    },
  ],
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> job;
</code></pre>
<p>To expose this new schema file to the studio, add it to the <code>schemaTypes</code> array inside the <code>schemas/index.ts</code> and you should see it in your studio. </p>
<p>Here's the resulting output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/job-schema-7.png" alt="Image" width="600" height="400" loading="lazy">
<em>job schema fields in sanity studio</em></p>
<p>Click the create button and add as many records as you want. Now you can move on to querying the data. </p>
<p>Similar to how the <code>profile</code> schema was queried inside the <code>sanity.query.ts</code> file, you will do that for the job schema too: </p>
<pre><code class="lang-js"><span class="hljs-comment">// sanity/sanity.query.ts</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getJob</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> client.fetch(
    groq<span class="hljs-string">`*[_type == "job"]{
      _id,
      name,
      jobTitle,
      "logo": logo.asset-&gt;url,
      url,
      description,
      startDate,
      endDate,
    }`</span>
  );
}
</code></pre>
<p>Next add the types for the returned dataset:</p>
<pre><code class="lang-js"><span class="hljs-comment">// types/index.ts</span>

<span class="hljs-keyword">export</span> type JobType = {
  <span class="hljs-attr">_id</span>: string;
  name: string;
  jobTitle: string;
  logo: string;
  url: string;
  description: string;
  startDate: <span class="hljs-built_in">Date</span>;
  endDate: <span class="hljs-built_in">Date</span>;
};
</code></pre>
<p>And then to display it in your front-end, create a <code>Job.tsx</code> file inside the <code>components</code> directory and add the following code:</p>
<pre><code class="lang-js"><span class="hljs-comment">// app/components/Job.tsx</span>

<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;
<span class="hljs-keyword">import</span> { getJob } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/sanity/sanity.query"</span>;
<span class="hljs-keyword">import</span> type { JobType } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Job</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> job: JobType[] = <span class="hljs-keyword">await</span> getJob();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-32"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-16"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold text-4xl mb-4"</span>&gt;</span>Work Experience<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">className</span>=<span class="hljs-string">"flex flex-col gap-y-12"</span>&gt;</span>
        {job.map((data) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
            <span class="hljs-attr">key</span>=<span class="hljs-string">{data._id}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-start lg:gap-x-6 gap-x-4 max-w-2xl relative before:absolute before:bottom-0 before:top-[4.5rem] before:left-7 before:w-[1px] before:h-[calc(100%-50px)] before:bg-zinc-800"</span>
          &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
              <span class="hljs-attr">href</span>=<span class="hljs-string">{data.url}</span>
              <span class="hljs-attr">rel</span>=<span class="hljs-string">"noreferrer noopener"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"min-h-[60px] min-w-[60px] rounded-md overflow-clip relative"</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Image</span>
                <span class="hljs-attr">src</span>=<span class="hljs-string">{data.logo}</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"object-cover"</span>
                <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">data.name</span>} <span class="hljs-attr">logo</span>`}
                <span class="hljs-attr">fill</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> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-start"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-bold"</span>&gt;</span>{data.name}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{data.jobTitle}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">small</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-zinc-500 mt-2 tracking-widest uppercase"</span>&gt;</span>
                {data.startDate.toString()} - {data.endDate.toString()}
              <span class="hljs-tag">&lt;/<span class="hljs-name">small</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base text-zinc-400 my-4"</span>&gt;</span>{data.description}<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">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">section</span>&gt;</span></span>
  );
}
</code></pre>
<p>To view the component, you can import it into the home page:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Note: This is a truncated version of the home page (app/page.tsx) file to illustrate how the Job component is declared.</span>

<span class="hljs-keyword">import</span> { getProfile } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/sanity/sanity.query"</span>;
<span class="hljs-keyword">import</span> type { ProfileType } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;
<span class="hljs-keyword">import</span> HeroSvg <span class="hljs-keyword">from</span> <span class="hljs-string">"./icons/HeroSvg"</span>;
<span class="hljs-keyword">import</span> Job <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Job"</span>; <span class="hljs-comment">// import job component</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> profile: ProfileType[] = <span class="hljs-keyword">await</span> getProfile();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-7xl mx-auto lg:px-16 px-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span>&gt;</span> // code truncated for brevity
        <span class="hljs-tag">&lt;<span class="hljs-name">HeroSvg</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Job</span> /&gt;</span> // declare job component
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>Here's the resulting output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/job-description-result-output-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>work experience section</em></p>
<p>By now, you should have a clear understanding of the necessary steps to showcase content with Sanity: <strong>Create schema file, &gt; Query the dataset &gt; Display the content in your application</strong>. </p>
<p>Let's now focus on configuring data for dynamic routes in your application and leveraging it to construct the projects page.</p>
<h3 id="heading-project-schema">Project Schema</h3>
<p>As always, you'll start by creating the schema file:</p>
<pre><code class="lang-bash">touch schemas/project.ts
</code></pre>
<p>Here's the code for the schema fields:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { BiPackage } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/bi"</span>;
<span class="hljs-keyword">import</span> { defineField } <span class="hljs-keyword">from</span> <span class="hljs-string">"sanity"</span>;

<span class="hljs-keyword">const</span> project = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">"project"</span>,
  <span class="hljs-attr">title</span>: <span class="hljs-string">"Project"</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">"Project Schema"</span>,
  <span class="hljs-attr">type</span>: <span class="hljs-string">"document"</span>,
  <span class="hljs-attr">icon</span>: BiPackage,
  <span class="hljs-attr">fields</span>: [
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"name"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Name"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Enter the name of the project"</span>,
    },
    defineField({
      <span class="hljs-attr">name</span>: <span class="hljs-string">"tagline"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Tagline"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
      <span class="hljs-attr">validation</span>: <span class="hljs-function">(<span class="hljs-params">rule</span>) =&gt;</span> rule.max(<span class="hljs-number">60</span>).required(),
    }),
    defineField({
      <span class="hljs-attr">name</span>: <span class="hljs-string">"slug"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Slug"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"slug"</span>,
      <span class="hljs-attr">description</span>:
        <span class="hljs-string">"Add a custom slug for the URL or generate one from the name"</span>,
      <span class="hljs-attr">options</span>: { <span class="hljs-attr">source</span>: <span class="hljs-string">"name"</span> },
      <span class="hljs-attr">validation</span>: <span class="hljs-function">(<span class="hljs-params">rule</span>) =&gt;</span> rule.required(),
    }),
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"logo"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Project Logo"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"image"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"projectUrl"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Project URL"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"url"</span>,
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"coverImage"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Cover Image"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"image"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Upload a cover image for this project"</span>,
      <span class="hljs-attr">options</span>: { <span class="hljs-attr">hotspot</span>: <span class="hljs-literal">true</span> },
      <span class="hljs-attr">fields</span>: [
        {
          <span class="hljs-attr">name</span>: <span class="hljs-string">"alt"</span>,
          <span class="hljs-attr">title</span>: <span class="hljs-string">"Alt"</span>,
          <span class="hljs-attr">type</span>: <span class="hljs-string">"string"</span>,
        },
      ],
    },
    {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"description"</span>,
      <span class="hljs-attr">title</span>: <span class="hljs-string">"Description"</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">"array"</span>,
      <span class="hljs-attr">description</span>: <span class="hljs-string">"Write a full description about this project"</span>,
      <span class="hljs-attr">of</span>: [{ <span class="hljs-attr">type</span>: <span class="hljs-string">"block"</span> }],
    },
  ],
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> project;
</code></pre>
<p>Next, expose the schema to the <code>schemaTypes</code> array:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> job <span class="hljs-keyword">from</span> <span class="hljs-string">"./job"</span>;
<span class="hljs-keyword">import</span> profile <span class="hljs-keyword">from</span> <span class="hljs-string">"./profile"</span>;
<span class="hljs-keyword">import</span> project <span class="hljs-keyword">from</span> <span class="hljs-string">"./project"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> schemaTypes = [profile, job, project];
</code></pre>
<p>Visit your studio, click the project schema, and add as many projects as you want. You can download the <a target="_blank" href="https://github.com/Evavic44/sanity-nextjs-site/tree/main/public">asset files</a> used for each project from the repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/project-schema-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sanity studio showing project schema fields</em></p>
<p>Here's the query to get all the projects:</p>
<pre><code class="lang-js"><span class="hljs-comment">// sanity/sanity.query.ts</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getProjects</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> client.fetch(
    groq<span class="hljs-string">`*[_type == "project"]{
      _id, 
      name,
      "slug": slug.current,
      tagline,
      "logo": logo.asset-&gt;url,
    }`</span>
  );
}
</code></pre>
<p>Next, add the types.</p>
<pre><code class="lang-js"><span class="hljs-comment">// types/index.ts</span>

<span class="hljs-keyword">export</span> type ProjectType = {
  <span class="hljs-attr">_id</span>: string;
  name: string;
  slug: string;
  tagline: string;
  projectUrl: string;
  logo: string;
  coverImage: {
    <span class="hljs-attr">alt</span>: string | <span class="hljs-literal">null</span>;
    image: string;
  };
  description: PortableTextBlock[];
};
</code></pre>
<p>And then display the content in your front-end.</p>
<pre><code class="lang-bash">mkdir app/projects &amp;&amp; touch app/projects/page.tsx
</code></pre>
<p>This will create a <code>page.tsx</code> file inside a directory called project. Here's the code for the projects:</p>
<pre><code class="lang-js"><span class="hljs-comment">// app/projects/page.tsx</span>

<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>;
<span class="hljs-keyword">import</span> { getProjects } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/sanity/sanity.query"</span>;
<span class="hljs-keyword">import</span> type { ProjectType } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Project</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> projects: ProjectType[] = <span class="hljs-keyword">await</span> getProjects();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-7xl mx-auto md:px-16 px-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-2xl mb-16"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold tracking-tight sm:text-5xl mb-6 lg:leading-[3.7rem] leading-tight"</span>&gt;</span>
          Featured projects I<span class="hljs-symbol">&amp;apos;</span>ve built over the years
        <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base text-zinc-400 leading-relaxed"</span>&gt;</span>
          I<span class="hljs-symbol">&amp;apos;</span>ve worked on tons of little projects over the years but these
          are the ones that I<span class="hljs-symbol">&amp;apos;</span>m most proud of. Many of them are
          open-source, so if you see something that piques your interest, check
          out the code and contribute if you have ideas for how it can be
          improved.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid xl:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-5 mb-12"</span>&gt;</span>
        {projects.map((project) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
            <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`/<span class="hljs-attr">projects</span>/${<span class="hljs-attr">project.slug</span>}`}
            <span class="hljs-attr">key</span>=<span class="hljs-string">{project._id}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center gap-x-4 bg-[#1d1d20] border border-transparent hover:border-zinc-700 p-4 rounded-lg ease-in-out"</span>
          &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Image</span>
              <span class="hljs-attr">src</span>=<span class="hljs-string">{project.logo}</span>
              <span class="hljs-attr">width</span>=<span class="hljs-string">{60}</span>
              <span class="hljs-attr">height</span>=<span class="hljs-string">{60}</span>
              <span class="hljs-attr">alt</span>=<span class="hljs-string">{project.name}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-zinc-800 rounded-md p-2"</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">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-1"</span>&gt;</span>{project.name}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-zinc-400"</span>&gt;</span>{project.tagline}<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">Link</span>&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>Here's the resulting output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/project-page-4.png" alt="Image" width="600" height="400" loading="lazy">
<em>project page</em></p>
<h3 id="heading-display-dynamic-routes">Display Dynamic Routes</h3>
<p>Each project card is wrapped in a link that points to their respective page based on the slug: <code>/projects/${project.slug}</code>. With this, the dynamic component can be easily created in next.js</p>
<p>Create a folder called <code>[project]</code> (wrapped in square brackets) inside the projects directory, and add a <code>page.tsx</code> file.</p>
<p>You can also do this via the terminal:</p>
<pre><code class="lang-bash">mkdir app/projects/[project] &amp;&amp; touch app/projects/[project]/page.tsx
</code></pre>
<p>This folder enclosed in square brackets is known as a <a target="_blank" href="https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#convention">dynamic segment</a> in Next.js, and it allows the component to be mounted based on the params property. </p>
<p>Since you've already created the project schema type, all that's left is to query the dataset to fetch single projects.</p>
<p>Here's the query to get single projects:</p>
<pre><code class="lang-js"><span class="hljs-comment">// sanity/sanity.query.ts</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSingleProject</span>(<span class="hljs-params">slug: string</span>) </span>{
  <span class="hljs-keyword">return</span> client.fetch(
    groq<span class="hljs-string">`*[_type == "project" &amp;&amp; slug.current == $slug][0]{
      _id,
      name,
      projectUrl,
      coverImage { alt, "image": asset-&gt;url },
      tagline,
      description
    }`</span>,
    { slug }
  );
}
</code></pre>
<p>To fetch the slug from the route, we've added a parameter called <code>slug</code> into the function, which will allow the <code>getSingleProject</code> function to be called with the respective slug using the Next.js <a target="_blank" href="https://nextjs.org/docs/pages/api-reference/functions/get-static-props#context-parameter">params property</a>.</p>
<pre><code class="lang-js"><span class="hljs-comment">// app/projects/[project]/page.tsx</span>

<span class="hljs-keyword">import</span> Image <span class="hljs-keyword">from</span> <span class="hljs-string">"next/image"</span>;
<span class="hljs-keyword">import</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;
<span class="hljs-keyword">import</span> { getSingleProject } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/sanity/sanity.query"</span>;
<span class="hljs-keyword">import</span> type { ProjectType } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/types"</span>;
<span class="hljs-keyword">import</span> { PortableText } <span class="hljs-keyword">from</span> <span class="hljs-string">"@portabletext/react"</span>;
<span class="hljs-keyword">import</span> fallBackImage <span class="hljs-keyword">from</span> <span class="hljs-string">"@/public/project.png"</span>;

type Props = {
  <span class="hljs-attr">params</span>: {
    <span class="hljs-attr">project</span>: string;
  };
};

<span class="hljs-comment">// Dynamic metadata for SEO</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateMetadata</span>(<span class="hljs-params">{ params }: Props</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">Metadata</span>&gt; </span>{
  <span class="hljs-keyword">const</span> slug = params.project;
  <span class="hljs-keyword">const</span> project: ProjectType = <span class="hljs-keyword">await</span> getSingleProject(slug);

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">title</span>: <span class="hljs-string">`<span class="hljs-subst">${project.name}</span> | Project`</span>,
    <span class="hljs-attr">description</span>: project.tagline,
    <span class="hljs-attr">openGraph</span>: {
      <span class="hljs-attr">images</span>: project.coverImage?.image || <span class="hljs-string">"add-a-fallback-project-image-here"</span>,
      <span class="hljs-attr">title</span>: project.name,
      <span class="hljs-attr">description</span>: project.tagline,
    },
  };
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Project</span>(<span class="hljs-params">{ params }: Props</span>) </span>{
  <span class="hljs-keyword">const</span> slug = params.project;
  <span class="hljs-keyword">const</span> project: ProjectType = <span class="hljs-keyword">await</span> getSingleProject(slug);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-6xl mx-auto lg:px-16 px-8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-3xl mx-auto"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-start justify-between mb-4"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-bold lg:text-5xl text-3xl lg:leading-tight mb-4"</span>&gt;</span>
            {project.name}
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
            <span class="hljs-attr">href</span>=<span class="hljs-string">{project.projectUrl}</span>
            <span class="hljs-attr">rel</span>=<span class="hljs-string">"noreferrer noopener"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-[#1d1d20] text-white hover:border-zinc-700 border border-transparent rounded-md px-4 py-2"</span>
          &gt;</span>
            Explore
          <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">Image</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"rounded-xl border border-zinc-800"</span>
          <span class="hljs-attr">width</span>=<span class="hljs-string">{900}</span>
          <span class="hljs-attr">height</span>=<span class="hljs-string">{460}</span>
          <span class="hljs-attr">src</span>=<span class="hljs-string">{project.coverImage?.image</span> || <span class="hljs-attr">fallBackImage</span>}
          <span class="hljs-attr">alt</span>=<span class="hljs-string">{project.coverImage?.alt</span> || <span class="hljs-attr">project.name</span>}
        /&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-y-6 mt-8 leading-7 text-zinc-400"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">PortableText</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{project.description}</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">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>Since the data coming from the dataset is a single project and not an array, no de-structuring is required.</p>
<p>Here's the resulting output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/dynamic-project-page-2.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Dynamic project page</em></p>
<h3 id="heading-add-loading-states">Add Loading States</h3>
<p>Next.js 13 introduced a special file <code>loading.js</code> that helps you create an instant loading state from the server while the content of a route segment loads. This helps users understand the app is responding and provides a better user experience.</p>
<p>With this special file, you can create a loading state that mimics the UI of the single project page easily.</p>
<p>Create a <code>loading.tsx</code> file inside the <code>[project]</code> directory and add the code snippet:</p>
<pre><code class="lang-js"><span class="hljs-comment">// projects/[project]/loading.tsx</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">Loading</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-3xl mx-auto lg:px-0 px-8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center justify-between mb-6"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-52 h-11 bg-[#1d1d20] rounded-sm animate-pulse"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-20 h-11 bg-[#1d1d20] rounded-sm animate-pulse"</span>&gt;</span><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">className</span>=<span class="hljs-string">"w-full h-96 mb-8 bg-[#1d1d20] rounded-sm animate-pulse"</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">className</span>=<span class="hljs-string">"flex flex-col gap-y-2"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full h-5 bg-[#1d1d20] rounded-sm animate-pulse"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full h-5 bg-[#1d1d20] rounded-sm animate-pulse"</span>&gt;</span><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>&gt;</span></span>
  );
}
</code></pre>
<p>Here's the resulting output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/loading-state-2.gif" alt="Image" width="600" height="400" loading="lazy">
<em>dynamic project page showing next.js instant loading state</em></p>
<h2 id="heading-fix-studio-layout">Fix Studio Layout</h2>
<p>You may have noticed the <code>navbar</code> and <code>footer</code> components are showing up in the studio route. This is because these components we're defined in the root layout —which applies to all routes in the application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/studio-component-ui-error-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>navbar and footer components in studio page</em></p>
<p>To fix this, you'll have to create a separate <code>layout.tsx</code> file for the studio component:</p>
<p>Create two folders wrapped in parenthesis inside the <code>app</code> directory. Name one folder <code>(site)</code>, and the other <code>(studio)</code>. These folders are wrapped in parenthesis to prevent Next.js from mounting them as routes.</p>
<p>Move all the files in the app directory that relates to the next app except the <code>studio</code> folder, <code>global.css</code> and <code>favicon.ico</code> into the <code>(site)</code> directory, and then move the studio folder inside the <code>(studio)</code> directory.</p>
<p>The only files that will live in the app root is <code>global.css</code> and <code>favicon.ico</code>.</p>
<p>Here's what your new folder structure should look like:</p>
<pre><code class="lang-bash">app/
├── (site)/
│   ├── about/
│   ├── components/
│   ├── icons/
│   ├── projects/
│   ├── layout.tsx
│   └── page.tsx
├── (studio)/
│   └── studio/
├── favicon.ico
└── global.css
</code></pre>
<p>Once completed, create a <code>layout.tsx</code> file inside the <code>(studio)</code> directory and paste the following code snippet inside:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">"../globals.css"</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">StudioLayout</span>(<span class="hljs-params">{children}: {children: React.ReactNode}</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>{children}<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></span>
  );
}
</code></pre>
<p>Update all the imports that may have changed, run your server again and you should see your studio up and running, without the components.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/sanity-studio-without-navbar-components-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Studio page without navbar and footer components</em></p>
<h2 id="heading-step-7-deployment">Step 7: Deployment</h2>
<p>Deploying a Sanity powered Next.js application is a pretty straightforward process. Follow this guide to <a target="_blank" href="https://vercel.com/docs/getting-started-with-vercel/import">set-up your account and deploy with Vercel</a>.</p>
<p>After successfully deploying your site, visit the studio route; <code>your-site-name/studio</code>, and you should get a prompt to add the URL to the CORS setting in Sanity:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/add-hosted-site-URL-to-sanity-cors-settings.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sanity CORS settings prompt</em></p>
<p>Simply click "continue" and follow the on-screen instructions to do so. If successful, you should be able to see your studio.</p>
<h2 id="heading-setup-sanity-webhooks-for-studio-update">Setup Sanity Webhooks for Studio Update</h2>
<p>Updates made to your site would be triggered only on build time. What this means is that if you update a field in your studio using the hosted link, you would have to manually trigger a deployment on Vercel to see the changes.</p>
<p>Having to trigger the deployment server each time can be a cumbersome task, especially when building for a client. </p>
<p>In this section, I'll guide you through the steps to manually deploy your site whenever a change is made to your studio using Sanity <a target="_blank" href="https://www.sanity.io/docs/webhooks">GROQ-powered Web Hooks</a>.</p>
<h3 id="heading-create-a-deploy-hook-on-vercel">Create a Deploy Hook on Vercel</h3>
<p>First, you will need the URL endpoint from your hosting service to trigger the deployment.</p>
<p>Navigate to your project settings on Vercel and click the <strong>Git</strong> tab. Under the <strong>Deploy Hooks</strong> section, choose a name for your hook and the select the branch that will be deployed when the generated URL is requested.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/create-hook-endpoint-on-vercel-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>creating the hook</em></p>
<p>Submit the form and copy the URL endpoint generated by Vercel.</p>
<h3 id="heading-trigger-hook-using-sanity-groq-powered-webhooks">Trigger Hook Using Sanity GROQ-powered Webhooks</h3>
<p>Visit <a target="_blank" href="https://www.sanity.io/manage">sanity.io/manage</a>, pick your project, navigate to the <strong>API</strong> section and click on the "Create webhook" button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/sanity-api-tab-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sanity GROQ-powered Webhooks</em></p>
<p>Fill in the form with information about the hook you want to create. </p>
<ul>
<li><code>Name</code>:  Portfolio Deployment.</li>
<li><code>Description</code>: Trigger rebuild when portfolio content is created, updated, and deleted.</li>
<li><code>URL</code>: [Paste the URL endpoint generated by Vercel here].</li>
<li><code>Dataset</code>: The dataset to apply the hook to.</li>
<li><code>Trigger on</code>: Check the <strong>"create"</strong>, <strong>"update"</strong>, and <strong>"delete"</strong> boxes.</li>
</ul>
<p>Leave <code>filter</code> and <code>projection</code> inputs blank so the hook will be applied to all documents, and for the rest of the fields, leave it as is and hit save.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/sanity-groq-powered-hook-created-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>sanity groq powered hook created</em></p>
<p>Now visit your hosted studio and update any document. Once you click publish, this should trigger the deploy hook and update your site when completed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/sanity-hook-trigger-build-on-vercel-2.png" alt="Image" width="600" height="400" loading="lazy">
<em>deploy hook triggering deployment on Vercel</em></p>
<p>Another good alternative to setting up live updates in your Sanity/Next.js app is using <a target="_blank" href="https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration">Incremental Static Regeneration (ISR)</a>, which is a better option if you're building a large scale application.</p>
<p>And that's it! You can see the <a target="_blank" href="https://sanity-nextjs-site.vercel.app">Live Preview here</a> and find the <a target="_blank" href="https://github.com/Evavic44/sanity-nextjs-site">GitHub URL here</a>.</p>
<h2 id="heading-what-next">What Next?</h2>
<p>Although this tutorial covered a lot of useful information, there are still many more possibilities with Sanity that you can explore. </p>
<p>You can customize your studio, integrate third-party APIs, build a storefront with Shopify, and much more.</p>
<p>If you found this article enjoyable and want to dive deeper into the world of Sanity, I recommend checking out the following resources:</p>
<ul>
<li><a target="_blank" href="https://www.sanity.io/docs/customizing-the-portable-text-editor">Customizing the Portable Text Editor</a></li>
<li><a target="_blank" href="https://www.sanity.io/guides/singleton-document">How to Create a Singleton Document</a></li>
<li><a target="_blank" href="https://www.sanity.io/plugins/code-input">Syntax Highlight Code Block</a></li>
</ul>
<p>Thanks for reading. Share, and subscribe to my blog for future updates.</p>
<p><a target="_blank" href="https://github.com/Evavic44">GitHub</a> | <a target="_blank" href="https://twitter.com/victorekea">Twitter</a> | <a target="_blank" href="https://eke.hashnode.dev">Blog</a> | <a target="_blank" href="https://www.linkedin.com/in/victorekeawa/">LinkedIn</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Level Up Your Developer Portfolio ]]>
                </title>
                <description>
                    <![CDATA[ By Peter Lynch It's not a big secret that building a developer portfolio can greatly increase your chances of getting hired. Everyone tells you that. But, they don't always tell you what makes a good one. You see, many portfolios are generic and rath... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/level-up-developer-portfolio/</link>
                <guid isPermaLink="false">66d460898812486a37369d32</guid>
                
                    <category>
                        <![CDATA[ career advice ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Job Hunting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Job Interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Junior developer  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ self-improvement  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 07 Feb 2022 18:38:59 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/02/level-up-portfolio-post.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Peter Lynch</p>
<p>It's not a big secret that building a developer portfolio can greatly increase your chances of getting hired.</p>
<p>Everyone tells you that. But, they don't always tell you what makes a good one.</p>
<p>You see, many portfolios are generic and rather uninspiring. If your portfolio looks just like everyone else's, then how can you expect it to stand out?</p>
<p>Well, it won't stand out. </p>
<p>But don't worry – I've been there. <a target="_blank" href="https://peter-lynch.netlify.app/index.html">My first portfolio</a> was bland, uninspiring, and had many of the not-to-do's I talk about in this post. </p>
<p>However, I'm an ex-marketer, turned developer, I know how to make something stand out. I've made ad campaigns that have skyrocketed sales and had people talking. I realized I needed to think like a marketer to make my portfolio stand out. </p>
<p>The good news is that the same principles can be applied to your portfolio to level it up. If you read this guide and implement the tips, you'll raise your portfolio above the sea of other junior developers trying to stand out.</p>
<p>Before we launch into the five tips to level up your portfolio, let's just make sure we are on the same page.</p>
<h2 id="heading-what-is-a-portfolio">What is a portfolio?</h2>
<p>A portfolio is a way for you to showcase who you are, what skills you have, and most importantly the work you've done.</p>
<p>As a developer, your portfolio is going to be in the form of a website. The main goal of this website is to showcase yourself and your work in the best way possible. </p>
<p>It needs to make you stand out and it needs to make employers think "Whoa! I'd like to work with this person". Ultimately it will need to show them that you could be awesome to work with so they are compelled find out more about you.</p>
<p>A portfolio should therefore include some standard elements. Below are the bare minimum elements your portfolio must include.</p>
<h2 id="heading-the-bare-minimum-of-a-portfolio">The bare minimum of a portfolio</h2>
<p>These are the non-negotiables of any developer portfolio:</p>
<ol>
<li>Responsive design (learn about how to do that <a target="_blank" href="https://www.freecodecamp.org/learn/responsive-web-design/">here</a>)</li>
<li>Projects showcasing your work</li>
<li>An "about me" section or somewhere people reading your site can learn about you</li>
<li>Your portfolio needs to be accessible</li>
<li>Your portfolio should have a custom domain. Buy a domain name – you can get them for less than $10. Don't send people to 39238834.netfily.com</li>
<li>A way for people to contact you, preferably your email</li>
</ol>
<p>If you have these six non-negotiables on your portfolio, you are off to a good start.</p>
<p>Now let's look at five ways you can level up your portfolio, starting with a non-obvious one.</p>
<h1 id="heading-five-things-you-can-do-to-level-up-your-developer-portfolio">Five things you can do to level up your developer portfolio</h1>
<h2 id="heading-1-know-your-target-audience">1. Know your target audience</h2>
<p>One area that can help level up your portfolio is knowing exactly what the purpose of your portfolio is. That is, who is this portfolio for?</p>
<p>If I asked you what the purpose of your portfolio was, does the output match that? Let's say, for example, the purpose of your portfolio is to land your first junior developer role. Then your portfolio is going to look very different from someone who is looking to be a freelance web developer.</p>
<p>Therefore the first thing you need to do is ask what is the purpose of your portfolio. Start broad and then try to refine it. Keeping with the junior developer role example, you could refine it down to a junior frontend developer specializing in React.</p>
<p>If you are a junior frontend developer, then your portfolio won't need things like the generic skill cards below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/skills-card.png" alt="Three generic skills cards with headings cross-industry experience, full stack development and design focus." width="600" height="400" loading="lazy"></p>
<p>These cards are better suited for someone who is looking for freelance work. </p>
<p>When you know the purpose of your portfolio, you can better tailor the information to communicate why someone should help you achieve your purpose.</p>
<h2 id="heading-2-level-up-your-project-details">2. Level up your project details</h2>
<p>This is the most important section of your portfolio, and it is the one section I often see that is not well-executed.</p>
<p>If you browse something like <a target="_blank" href="https://www.reddit.com/r/webdev">r/webdev</a> and search "portfolio" let me tell you what you're going to see if you click 90% of those portfolios and look at projects. You're going to see a calculator app, a weather app, tic tac toe, a movie finder app, "insert big website" clone, and so on.</p>
<p>Now, don't get me wrong – these are useful projects for learning the skills they require. And people have spent time making them. The problem is that everyone builds the same stuff. You can often just follow along with a tutorial and create them without much thought.</p>
<p>There are two problems here.</p>
<p>The first is that you are not differentiating yourself. If I'm HR or the hiring manager why should I pick your calculator app over the other 30 in the inbox? Also, how can I trust that you built this thing yourself and that you didn't just copy/paste code from a tutorial?</p>
<p>The second is that most people just put the project in, link to the GitHub repo, and maybe a live demo. This doesn't tell the person who is potentially going to hire you a lot, and it doesn't give them much reason to employ you over everyone else who has done the same.</p>
<p>So then, how do you make your projects section stand out? Let's look at a few ways you could level up the projects section of your portfolio.</p>
<h3 id="heading-add-original-projects">Add original projects</h3>
<p>You don't have to reinvent the wheel or make the next great social media application. But it would be great if you tried to use a different API instead of the standard weather API.</p>
<p>You could for instance create a surf report app that is the same as a weather app but instead you are calling different endpoints. This shows me that you know how to call an API outside of a tutorial.</p>
<p>If you're not sure how to make your own original project then check out this guide on <a target="_blank" href="https://www.peterlunch.com/blog/how-to-plan-and-build-a-programming-project">how to plan and build a programming project for beginners</a>.</p>
<h3 id="heading-give-some-details-about-the-project">Give some details about the project</h3>
<p>The first problem is that many projects are unoriginal. The second is that most people don't give more than the basic details about the project.</p>
<p>What will make you stand out is giving some details about your project. When I say details, what I mean is give me a story about the project. The best portfolios I see tell me about the project don't just show it to me.</p>
<p>Try answering some of these questions.</p>
<ul>
<li>Why did you choose to build this project?</li>
<li>What challenged you when making this project?</li>
<li>What did you learn from making this project?</li>
<li>What learnings have you taken with you into other projects?</li>
<li>What would you do differently next time?</li>
<li>Did you get stuck at any point? How did you get unstuck?</li>
<li>What was your process for completing this project? Did you do wireframes, make a Trello board, or did you just get stuck into it?</li>
</ul>
<p>If you answer these questions and craft them into a few succinct paragraphs, I will now know a lot more about your abilities as a developer. I can see what interests you, how you overcome challenges, how you learn from those challenges, and what skill level you are.</p>
<p>You're already well on your way to standing out if you follow this tip.</p>
<h2 id="heading-3-remove-unnecessary-fluff-from-your-portfolio">3. Remove Unnecessary Fluff from Your Portfolio</h2>
<p>Sometimes the best thing you can do to improve your portfolio is to remove things.</p>
<p>You should remove things that add no value to your target audience and things that reduce your credibility.</p>
<p>Here is a list of things you to remove and why. </p>
<ol>
<li><strong>Step-by-step tutorial projects.</strong> These projects hurt your credibility. They are unoriginal, they don't show me what you can do. Rather, they show me that you can copy what someone else does.  </li>
<li><strong>Projects not related to development or the job you are trying to land.</strong> These just aren't relevant to your target audience. </li>
<li><strong>If one of your Projects is the portfolio itself.</strong> Why get rid of this? The reason is that they are on your portfolio now. They are interacting with the project. So save space for another project.</li>
<li><strong>Skill progress bars.</strong> While they look cool, they are bad. If you're a junior you probably know 10% JavaScript, would you want to represent that? What the heck does 80% CSS mean? <strong>If you remove one thing, please make it these.</strong></li>
<li><strong>If your portfolio is a carbon copy of someone else's portfolio – start over</strong>. It's not cool for starters. Plus, I want to see what you're capable of doing, not what you're capable of copying. It can come back to hurt you in the long run. </li>
</ol>
<h2 id="heading-4-give-your-about-me-some-personality">4. Give your about me some personality</h2>
<p>There is this song by Paul Kelly called "Every F$*#%) City" and the general idea of the song is that every city is the same. Well, I've seen a lot of portfolio's and a lot of them look the same.</p>
<p>The section that often comes off as the same is the about me section. Let's look at an example below.</p>
<p><em>"Hi, my name's Jonny Lucky, a Full-Stack Web Developer from Example Town. I love building websites that solve life's unique problems.</em></p>
<p><em>I work primarily with MongoDB, Express, React, Node, and GraphQL and have set up countless WordPress and Shopify websites. My previous career was as an IT Consultant, E-commerce Business Owner, and Digital Marketer. I also really enjoy tinkering with CSS.</em></p>
<p><em>In my spare time, I play video games and also work out."</em></p>
<p>Hello Jonny Lucky, or was it Lucy Lovely? I forgot because I'm the HR person and I've read 50 of these portfolios and they are all very similar.</p>
<p>If I were to change your name, town, and tech stack, many of these candidates would just become one.</p>
<p>To be fair, there are some personal touches like the that they "play video games and also workout". But, guess what? A lot of other applicants also enjoy these things.</p>
<p>Can you see the problem, if you have an about me section like the above? You won't stand out and you'll come across as a little stiff.</p>
<p>Your about me needs to be a little bit spicy, it needs to show your personality and experience. Because your personality is different from other people, so your about me section should be too.</p>
<p>Let's have a look at mine to give you an idea of what you should be shooting for. I've gotten a few compliments on it.</p>
<p><em>"I'm an ex-marketer who loves building things with code.</em></p>
<p><em>I first became enthralled with programming in 2015. I was in San Francisco with my brother in law and he was telling me about this thing called Python and how it could automate things. As a person who looks for efficiencies, I was hooked.</em></p>
<p><em>I jumped right into Python on my flight back to Australia. Soon, I was automating my own things. I made a bot to book classes at the gym, one to send weekly reports to my boss and I even made one to like my girlfriend's content on Instagram. Unfortunately, that is as far as it went. Shortly after, I got promoted. So, I had less time to learn and I let programming fall by the wayside.</em></p>
<p><em>But, then Covid hit and suddenly I had more time without my commute. So, I started learning again. Specifically, I learned web development, as I had things I wanted to build for myself.</em></p>
<p><em>After a few months of self-teaching, I knew this was what I wanted to do for my career. So, I signed up for the coder academy Bootcamp and put my heart and soul into it. I was coding every day, late into the night, and on weekends. Until I was ready to be unleashed on production code.</em></p>
<p><em>Since the boot camp, I have grown as a developer, working alongside senior developers who have helped me raise my standards for what's expected of any web application.</em></p>
<p><em>I'm now seeking a full-time role where I can contribute my skills both in coding and business to help a company achieve their goals."</em></p>
<p>As you can see my about me section is unique to me. I'd say there isn't another soul on the planet who could write the same about me section without telling some lies.</p>
<p>Now, your about me section doesn't have to be zany, it can be professional. But, and this is a key point... </p>
<blockquote>
<p><em>You need to make it interesting so the person reading it wants to continue reading it.</em></p>
</blockquote>
<p>To make your about me section interesting, you can try things like stories and anecdotes. Try and weave them in with the key points you want to get across.</p>
<p>Another way to think of it is, if you are invited in for an interview would your about me be interesting enough that the person interviewing you would say "hey, I loved that story about X on your portfolio"? If it can do that you're winning because you've opened up more dialogue.</p>
<p>In the end, you're trying to spark interest in the person looking at your portfolio.</p>
<p>Think about it like this. If HR has 50 applications to get through, they are only going to interview 5 of those people. </p>
<p>If your about me section is a bland generic version like the first one, they are likely to just skim through it and onto your next section. If nothing else on your portfolio stands out, then they'll move on to the next application until something does jump out at them. You want to grab their attention and make them shift you to the interview pile.</p>
<p>Since most portfolios are generic, it doesn't take much to stand out. If you have less experience than someone else but their portfolio is generic and yours isn't, I'd argue you are more likely to get picked for an interview. </p>
<p>Why? Because we like to work with people we like, and if you show that you're an interesting unique person, I'd probably like to at least find out more about you.</p>
<h2 id="heading-5-throw-some-sprinkles-on-it">5. Throw some Sprinkles on it</h2>
<p>One of the most important lessons I was taught early on in my coding Bootcamp was that you need the doughnut before the sprinkles.</p>
<p>Looking at this analogy in regards to your portfolio, the doughnut is all the other things I have listed above. You need the baseline requirements of a portfolio like solid projects, an interesting about me section, and responsive design.</p>
<p>The sprinkles should come after you have all the basics. So, if you followed the tips above you have the doughnut. Now, you can start to make your portfolio shine.</p>
<p>Add things that make a prospective employer go wow! You can add amazing animations like <a target="_blank" href="https://cherupil.com/">Christopher Cherupil</a>, cool transition effects like <a target="_blank" href="https://philippeneveu.com/">Philippe Neveu</a>, or dark mode like <a target="_blank" href="https://jhey.dev/">Jhey Tompkins</a>.</p>
<p>I added a <a target="_blank" href="https://www.peterlunch.com/blog/javascript-exit-intent-modal">Zelda-themed modal</a> to make sure a prospective employer grabbed my résumé before they left my old portfolio site. I just made sure I had the basics first.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/02/Screen-Shot-2022-02-06-at-11.51.25-am.png" alt="Contents of the Zelda exit intent modal I built for my resume." width="600" height="400" loading="lazy"></p>
<p>You can also go wild with your ideas, just make sure you execute them. You don't want unpolished sprinkles that could take away from your doughnut's main elements.</p>
<h2 id="heading-summary">Summary</h2>
<p>If you read this post and thought, oh man my portfolio is quite generic – don't fret. You now have 5 things you can do to level up your portfolio so that it moves from the "forgotten" pile to the "give this person an interview" pile.</p>
<p>I can frequently be found in portfolio advice threads on Reddit and Twitter trying to offer advice to people trying to break into the industry. </p>
<p>When you're done leveling up your portfolio, and you're looking for some feedback on it, make sure to Tweet it to me with a link to your site. </p>
<p>If you have a portfolio you're proud of or you're looking for feedback on it, <strong><a target="_blank" href="https://twitter.com/thelynchpinau">follow me on twitter</a></strong> and tweet the link to me so I can look over it and give you some feedback.   </p>
<p>If you have an awesome one I might even add it to my <a target="_blank" href="https://github.com/pin0S/portfolios-that-pop">portfolios that pop list</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Portfolio Website Using HTML, CSS, JavaScript, and Bootstrap 5 ]]>
                </title>
                <description>
                    <![CDATA[ By Sampurna Chapagain If you are a web developer or a web designer, it is essential for you to have a portfolio website. It lets you provide information about yourself and showcase your best work with your relevant skills and experience. In this blog... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-portfolio-website-using-html-css-javascript-and-bootstrap/</link>
                <guid isPermaLink="false">66d460f78812486a37369d58</guid>
                
                    <category>
                        <![CDATA[ Bootstrap 5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 25 Jan 2022 17:10:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/Untitled-design-6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Sampurna Chapagain</p>
<p>If you are a web developer or a web designer, it is essential for you to have a portfolio website. It lets you provide information about yourself and showcase your best work with your relevant skills and experience.</p>
<p>In this blog post, I will discuss some of the benefits of creating a portfolio website. Then I'll show you how to create a beautiful responsive portfolio website for yourself using HTML, CSS, JavaScript and Bootstrap version 5.</p>
<h3 id="heading-heres-an-interactive-scrim-about-how-to-create-a-portfolio-website-using-html-css-js-and-bootstrap-5">Here's an interactive scrim about how to create a portfolio website using HTML, CSS, JS, and Bootstrap 5:</h3>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/co27f4302abab219aea652cf7?embed=freecodecamp,mini-header" width="100%" height="480" title="Embedded content" loading="lazy"></iframe></div>

<h2 id="heading-table-of-contents">Table Of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-benefits-of-having-a-portfolio-website">Benefits of having a portfolio website</a></li>
<li><a class="post-section-overview" href="#heading-what-is-bootstrap">What is Bootstrap</a>?</li>
<li><a class="post-section-overview" href="#heading-folder-structure">Folder Structure</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-a-navigation-menu-to-your-portfolio">How to Add a Navigation Menu to Your Portfolio</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-a-hero-header-to-the-portfolio">How to Add a Hero Header to the Portfolio</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-about-section">How to Make the About Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-services-section">How to Make the Services Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-dark-background-color-to-navbar-on-page-scroll">How to Add Dark Background Color to Navbar on Page Scroll</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-portfolio-section">How to Build the Portfolio Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-contact-section">How to Build the Contact Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-footer-section">How to Build the Footer Section</a></li>
<li><a class="post-section-overview" href="#heading-final-touches">Adding Final Touches</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-benefits-of-having-a-portfolio-website">Benefits of having a Portfolio Website</h2>
<p>Having a portfolio website has several benefits, including:</p>
<ul>
<li>it provides a platform to showcase your relevant skills and experience </li>
<li>it shows your personality</li>
<li>it lets hiring managers find you instead of you reaching out to them </li>
<li>you are easily searchable on search engines like Google </li>
</ul>
<h2 id="heading-what-is-bootstrap">What is Bootstrap?</h2>
<p>Bootstrap is a popular front-end CSS framework which is used to develop responsive and mobile friendly websites. The latest release of Bootstrap is version 5. You can find the official documentation of Bootstrap 5 <a target="_blank" href="https://getbootstrap.com/docs/5.0/getting-started/introduction/">here</a>. </p>
<h2 id="heading-folder-structure">Folder Structure</h2>
<p>We will now start working on creating the portfolio website.</p>
<p>First, let's create the folder structure. You can get the project starter files on <a target="_blank" href="https://github.com/SampurnaC/portfolio_website_fcc/tree/portfolio-starter-files">GitHub</a>. Also, you can visit <a target="_blank" href="https://brad-portfolio.netlify.app/">here</a> to see the live demo of this project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Screenshot-from-2022-01-22-19-10-25.png" alt="Image" width="600" height="400" loading="lazy">
<em>Project Folder Structure</em></p>
<p>The folder structure consists of index.html, style.css, and script.js files and an images folder. We'll write all CSS in the style.css file and the JavaScript in the script.js file . </p>
<p>In the index.html file, you can see the HTML boilerplate code with the Bootstrap CDN, <a target="_blank" href="https://fontawesome.com/">font awesome kit</a>, and a link to the external style sheet and JavaScript.</p>
<p>Here, the script.js file is loaded after loading all the HTML code. </p>
<h2 id="heading-how-to-add-a-navigation-menu-to-your-portfolio">How to Add a Navigation Menu to Your Portfolio</h2>
<p>Now, let's work on adding a navigation menu in our project. It will help visitors find the relevant info they're looking for.</p>
<p>We will use Bootstrap's <code>fixed-top</code> class in nav element to keep the navbar at the top of the page. The navbar also has a <code>navbar-brand</code> class where we keep the name of the person as a brand. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar navbar-expand-lg fixed-top navbarScroll"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-brand"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>&gt;</span>Brad<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">data-bs-toggle</span>=<span class="hljs-string">"collapse"</span> <span class="hljs-attr">data-bs-target</span>=<span class="hljs-string">"#navbarSupportedContent"</span> <span class="hljs-attr">aria-controls</span>=<span class="hljs-string">"navbarSupportedContent"</span> <span class="hljs-attr">aria-expanded</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Toggle navigation"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler-icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"collapse navbar-collapse"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"navbarSupportedContent"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-nav ms-auto"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-item active"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#home"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</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">li</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#services"</span>&gt;</span>Services<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#portfolio"</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">li</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</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">li</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>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</code></pre>
<p>The navbar has the following features:</p>
<ul>
<li>It has six links: home, about, services, portfolio, contact, and footer </li>
<li>It has a transparent background. We will add a dark background on page scrolling later.</li>
<li>It toggles on smaller devices</li>
</ul>
<p>You can find more details regarding Bootstrap 5 navbar features <a target="_blank" href="https://getbootstrap.com/docs/5.0/components/navbar/">here</a>.</p>
<p>However, the navbar has a problem while scrolling. It's fully transparent throughout the page which causes readability issues.  We will fix this issue after we complete the <a class="post-section-overview" href="#heading-how-to-make-the-services-section">Services section</a> to make you understand the issue properly. </p>
<h2 id="heading-how-to-add-a-hero-header-to-the-portfolio">How to Add a Hero Header to the Portfolio</h2>
<p>Now, we will be adding a hero image with some text in the center. A hero image is a web design term which refers to a high quality full width image that displays the company or individual's main goals, a representative image, photo, or other eye-catching elements. It helps attract users to your site.</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- main banner --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bgimage"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"home"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container-fluid"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-12 col-md-12 col-sm-12 col-xs-12 hero-text"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hero_title"</span>&gt;</span>Hi, it's me Brad<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">"hero_desc"</span>&gt;</span>I am a professional freelancer in New York City<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">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">section</span>&gt;</span>
</code></pre>
<p>  Also, let's add the CSS for the above code in the style.css file: </p>
<pre><code class="lang-css"><span class="hljs-comment">/* hero background image */</span>
<span class="hljs-selector-class">.bgimage</span> {
    <span class="hljs-attribute">height</span>:<span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">url</span>(<span class="hljs-string">'images/heroImage.jpeg'</span>);
    <span class="hljs-attribute">background-size</span>:cover;
    <span class="hljs-attribute">position</span>:relative;
}
<span class="hljs-comment">/* text css above hero image*/</span>
<span class="hljs-selector-class">.hero_title</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">4.5rem</span>;
}
<span class="hljs-selector-class">.hero_desc</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-selector-class">.hero-text</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">top</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translate</span>(-<span class="hljs-number">50%</span>, -<span class="hljs-number">50%</span>);
    <span class="hljs-attribute">color</span>: white;
}
</code></pre>
<p>Here we can see that the section has an id named <code>bgimage</code> which is responsible for displaying the background hero image with full width. It also displays some text in the center above the background image with the help of the above CSS. </p>
<p>This is how the site looks so far with the navbar and the hero section:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Screenshot-from-2022-01-25-10-13-25.png" alt="Image" width="600" height="400" loading="lazy">
<em>Hero Image with Navbar</em></p>
<h2 id="heading-how-to-make-the-about-section">How to Make the About Section</h2>
<p>The About page contains important information about you and your background. Visitors to your portfolio site can get to know you through the information you provide in this page. </p>
<p>We will be adding an image to the left side of the row, and on the right side we will add our quick introduction in this section. Let's demonstrate it using the code below:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- about section--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"about"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mt-4 pt-4"</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-center"</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">"row mt-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/about.jpeg"</span> <span class="hljs-attr">class</span>= <span class="hljs-string">"imageAboutPage"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</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">"col-lg-8"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged

                    <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">"row mt-3"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</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">li</span>&gt;</span>Name: David Parker<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Age: 28<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Occupation: Web Developer<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 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">"col-md-6"</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">li</span>&gt;</span>Name: David Parker<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Age: 28<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Occupation: Web Developer<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 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">"row mt-3"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span> Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
                            Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
                        <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">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">section</span>&gt;</span>
</code></pre>
<p> Let's add some CSS for the left side image:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* about section image css */</span>
<span class="hljs-selector-class">.imageAboutPage</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
}
</code></pre>
<p>This will create an about section. You can modify the content based on your use cases. We have added classes named <code>mt-4</code> and <code>pt-4</code> with container class which will set the margin top and padding top to 1.5 rem.</p>
<p>The row has two columns. One has the <code>col-lg-4</code> class for displaying the image which will occupy the left column with a 4-part grid for large screens. </p>
<p>The next column is assigned a class of <code>col-lg-8</code> which will occupy the right column with an 8-part grid for larger screens. For medium and small screens they will overlap with each other which we can see in the below GIF file:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/about.gif" alt="Image" width="600" height="400" loading="lazy">
<em>About Section</em></p>
<h2 id="heading-how-to-make-the-services-section">How to Make the Services Section</h2>
<p>This section helps convert website visitors into potential clients. This is where you explain what specific services you offer, and where you <a target="_blank" href="https://www.freecodecamp.org/news/web-design-niche/">niche down</a> your offered services. </p>
<p>Let's add the code for this section and describe it below:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- services section--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"services"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</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-center"</span>&gt;</span>Services<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">"row"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-4 mt-4"</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 servicesText"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas servicesIcon fa-clock"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title mt-3"</span>&gt;</span>Website Development<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text mt-3"</span>&gt;</span>Some quick example text to build on the card title and make up the bulk of the card's content.
                                Some quick example text to build on the card title and make up the bulk of the card's content.
                            <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">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">"col-lg-4 mt-4"</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 servicesText"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'fas servicesIcon fa-layer-group'</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title mt-3"</span>&gt;</span>Website Design<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text mt-3"</span>&gt;</span>Some quick example text to build on the card title and make up the bulk of the card's content.
                                Some quick example text to build on the card title and make up the bulk of the card's content.
                            <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">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">"col-lg-4 mt-4"</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 servicesText"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'far servicesIcon fa-check-circle'</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title mt-3"</span>&gt;</span>Website Deployment<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text mt-3"</span>&gt;</span>Some quick example text to build on the card title and make up the bulk of the card's content.
                                Some quick example text to build on the card title and make up the bulk of the card's content.
                            <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">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> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-4 mt-4"</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 servicesText"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'fas servicesIcon fa-search'</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title mt-3"</span>&gt;</span>SEO<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text mt-3"</span>&gt;</span>Some quick example text to build on the card title and make up the bulk of the card's content.
                                Some quick example text to build on the card title and make up the bulk of the card's content.
                            <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">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">"col-lg-4 mt-4"</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 servicesText"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'fas servicesIcon fa-shield-alt'</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title mt-3"</span>&gt;</span>DevOps<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text mt-3"</span>&gt;</span>Some quick example text to build on the card title and make up the bulk of the card's content.
                                Some quick example text to build on the card title and make up the bulk of the card's content.
                            <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">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">"col-lg-4 mt-4"</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 servicesText"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'fas servicesIcon fa-wrench'</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title mt-3"</span>&gt;</span>QA<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text mt-3"</span>&gt;</span>Some quick example text to build on the card title and make up the bulk of the card's content.
                                Some quick example text to build on the card title and make up the bulk of the card's content.
                            <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">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 class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>Since this website is targeted towards web developers and designers, I've included some of the services which a web developer or designer might offer.</p>
<p>We have used bootstrap cards to display services. Our services section has 2 rows and 3 columns each. For large screens with a width greater than or equal to 992px, three cards are displayed in a row. For screens less than 992px wide, only a single card is displayed in a row. </p>
<p>You can find more about bootstrap breakpoints <a target="_blank" href="https://getbootstrap.com/docs/5.0/layout/breakpoints/">here</a>. </p>
<p>Also, there are fonts added in each card to make them look better. </p>
<p>Without CSS, the services section would look like this :</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Screenshot-from-2022-01-23-14-01-00.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>So, let's add some CSS to increase the font icon font size and card height and add some extra color when a user hovers over a card.</p>
<pre><code class="lang-css"><span class="hljs-comment">/* services section css */</span>
<span class="hljs-selector-class">.servicesText</span><span class="hljs-selector-class">.card</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">280px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
  }
<span class="hljs-selector-class">.servicesIcon</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
}
<span class="hljs-selector-class">.card-title</span> {
    <span class="hljs-attribute">text-align</span>: center;
}
<span class="hljs-selector-class">.card</span><span class="hljs-selector-pseudo">:hover</span> <span class="hljs-selector-class">.servicesIcon</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#008000</span>;
}
<span class="hljs-selector-class">.servicesText</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#008000</span>;
}
</code></pre>
<p>This is how our services section looks now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/services.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Services</em></p>
<h2 id="heading-how-to-add-dark-background-color-to-navbar-on-page-scroll">How to Add Dark Background Color to Navbar on Page Scroll</h2>
<p>If you look into the above gif properly you will see that the navbar is transparent throughout the page which causes readability issues. So let's work on fixing this issue.</p>
<p>We will write some JavaScript and CSS in order to resolve this problem. We will add a <code>navbarDark</code> class in order to show a dark background color for the navbar on page scroll. </p>
<p>For that we need to go to the script.js file and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// add class navbarDark on navbar scroll</span>
<span class="hljs-keyword">const</span> header = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'.navbar'</span>);

<span class="hljs-built_in">window</span>.onscroll = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> top = <span class="hljs-built_in">window</span>.scrollY;
    <span class="hljs-keyword">if</span>(top &gt;=<span class="hljs-number">100</span>) {
        header.classList.add(<span class="hljs-string">'navbarDark'</span>);
    }
    <span class="hljs-keyword">else</span> {
        header.classList.remove(<span class="hljs-string">'navbarDark'</span>);
    }
}
</code></pre>
<p> Now, let's break down the above code:</p>
<ul>
<li>The header holds the value of the nav element since the querySelector method returns the first element that matches the CSS selector (which is <code>.navbar</code> in this case).</li>
<li><code>window.onscroll</code> fires up when the scroll event happens.</li>
<li><code>window.scrollY</code> returns the number of pixels that the document is scrolled vertically and its value is assigned to a variable named <code>top</code>.</li>
<li>If the value of <code>top</code> is greater than or equal to 100, it adds a class of <code>navbarDark</code> to the header. </li>
</ul>
<p>Let's quickly add CSS for the <code>navbarDark</code> class. For that, go to your style.css file and add the following code:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* display background color black on navbar scroll */</span>
<span class="hljs-selector-class">.navbarScroll</span><span class="hljs-selector-class">.navbarDark</span> {
    <span class="hljs-attribute">background-color</span>: black;
}
</code></pre>
<p>This is how the navbar will look now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/navbar.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Dark background color on Navbar on page scroll</em></p>
<h2 id="heading-how-to-build-the-portfolio-section">How to Build the Portfolio Section</h2>
<p>This section includes your best work. People can see what you are capable of doing, and showcasing strong past work will definitely attract more potential clients or recruiters. So only add your best work in this section.</p>
<p>We will use Bootstrap cards to display the portfolio projects. There will be 2 rows and each row will have 3 columns of cards.</p>
<p>This will be the code for portfolio section: </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- portfolio section--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mt-3"</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-center"</span>&gt;</span>Portfolio<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">"row"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-4 mt-4"</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"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/portfolioImage1.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Card image"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100%"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>YouTube Clone<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>Lorem Ipsum is simply dummy text of the printing and typesetting industry.<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">"text-center"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-success"</span>&gt;</span>Link<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">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">"col-lg-4 mt-4"</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 portfolioContent"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/portfolioImage4.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Card image"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100%"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Quiz App<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>Lorem Ipsum is simply dummy text of the printing and typesetting industry.<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">"text-center"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-success"</span>&gt;</span>Link<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">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">"col-lg-4 mt-4"</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 portfolioContent"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/portfolioImage3.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Card image"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100%"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Product Landing Page<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>Lorem Ipsum is simply dummy text of the printing and typesetting industry.<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">"text-center"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-success"</span>&gt;</span>Link<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">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">br</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-4 mt-4"</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 portfolioContent"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/portfolioImage4.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Card image"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100%"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Messaging Service<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>Lorem Ipsum is simply dummy text of the printing and typesetting industry.<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">"text-center"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-success"</span>&gt;</span>Link<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">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">"col-lg-4 mt-4"</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 portfolioContent"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/portfolioImage1.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Card image"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100%"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Twitter Clone<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>Lorem Ipsum is simply dummy text of the printing and typesetting industry.<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">"text-center"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-success"</span>&gt;</span>Link<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">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">"col-lg-4 mt-4"</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 portfolioContent"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-img-top"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"images/portfolioImage4.jpg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Card image"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:100%"</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"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-title"</span>&gt;</span>Blog App<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card-text"</span>&gt;</span>Lorem Ipsum is simply dummy text of the printing and typesetting industry.<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">"text-center"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-success"</span>&gt;</span>Link<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">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">section</span>&gt;</span>
</code></pre>
<p>Each card has an image, title, description, and link to the projects. Three cards are displayed in a row for large screens which have breakpoints of ≥ 992px wide, but for screens &lt; 992px wide only a single card is displayed in a row.</p>
<p>The GIF below shows how the portfolio section looks now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/portfolio.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Portfolio</em></p>
<h2 id="heading-how-to-build-the-contact-section">How to Build the Contact Section</h2>
<p>You should include your contact information in this section so that visitors can contact you if they want to hire you. </p>
<p>Our contact section will include 2 columns in a single row: Google maps for location and a contact form. </p>
<p>In order to embed the Google map, you need to follow these steps:</p>
<ul>
<li>go to <a target="_blank" href="https://www.embed-map.com/">https://www.embed-map.com</a> </li>
<li>enter your location</li>
<li>click on the <strong>Generate HTML Code</strong> button which will provide your Google Map HTML Code</li>
</ul>
<p>Our code will look like this with the contact form included: </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- contact section--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container mt-3 contactContent"</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-center"</span>&gt;</span>Contact 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">"row mt-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-lg-6"</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- to edit google map goto https://www.embed-map.com type your location, generate html code and copy the html  --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"max-width:100%;overflow:hidden;color:red;width:500px;height:500px;"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"embedmap-canvas"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"height:100%; width:100%;max-width:100%;"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">iframe</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"height:100%;width:100%;border:0;"</span> <span class="hljs-attr">frameborder</span>=<span class="hljs-string">"0"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://www.google.com/maps/embed/v1/place?q=new+york&amp;key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8"</span>&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">iframe</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> <span class="hljs-attr">class</span>=<span class="hljs-string">"googlemaps-html"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://www.embed-map.com"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"get-data-forembedmap"</span>&gt;</span>https://www.embed-map.com<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css"><span class="hljs-selector-id">#embedmap-canvas</span> <span class="hljs-selector-tag">img</span>{<span class="hljs-attribute">max-width</span>:none<span class="hljs-meta">!important</span>;<span class="hljs-attribute">background</span>:none<span class="hljs-meta">!important</span>;<span class="hljs-attribute">font-size</span>: inherit;<span class="hljs-attribute">font-weight</span>:inherit;}
                        </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</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">"col-lg-6"</span>&gt;</span>
                    <span class="hljs-comment">&lt;!-- form fields --&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control form-control-lg"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Name"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control mt-3"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Email"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control mt-3"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Subject"</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-3 mt-3"</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"comment"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Project Details"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">form</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-success mt-3"</span>&gt;</span>Contact Me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

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

            <span class="hljs-tag">&lt;/<span class="hljs-name">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">section</span>&gt;</span>
</code></pre>
<p>The first column will display the Google map and the next one will display the contact form. </p>
<p>The form has four different form fields: name, email, subject and project details. The form doesn't submit the request itself. You will need to connect it with any back-end language. Or, you can simply use <a target="_blank" href="https://www.netlify.com/products/forms/">Netlify Form</a> or <a target="_blank" href="https://formspree.io/">Formspree form</a> for this.</p>
<p>This is how the contact section will appear:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Screenshot-from-2022-01-25-11-31-56.png" alt="Image" width="600" height="400" loading="lazy">
<em>Contact Section</em></p>
<h2 id="heading-how-to-build-the-footer-section">How to Build the Footer Section</h2>
<p>Now we have come to the last section of this post, which is the footer section. We have already added a link to the font awesome CDN in the index.html file.  </p>
<p>In the Footer, we will add links to our social media through font awesome icons. </p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- footer section--&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"footer"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container-fluid"</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- social media icons --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"social-icons mt-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://www.facebook.com/"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-facebook"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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://www.instagram.com/"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-instagram"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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://www.twitter.com/"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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://www.linkedin.com/"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-linkedin"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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://www.twitch.tv/"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitch"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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">footer</span>&gt;</span>
</code></pre>
<p>Without the CSS, our footer will look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Screenshot-from-2022-01-23-17-56-37.png" alt="Image" width="600" height="400" loading="lazy">
<em>footer without styling</em></p>
<p>So let's add some styling to the footer with this code:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* social media icons styling */</span>
<span class="hljs-selector-class">.social-icons</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
}
<span class="hljs-selector-class">.fa-facebook</span><span class="hljs-selector-pseudo">:hover</span>,<span class="hljs-selector-class">.fa-instagram</span><span class="hljs-selector-pseudo">:hover</span>,<span class="hljs-selector-class">.fa-twitter</span><span class="hljs-selector-pseudo">:hover</span>,<span class="hljs-selector-class">.fa-linkedin</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.fa-twitch</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#008000</span>;
}
<span class="hljs-selector-class">.fab</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#000000</span>;
}
<span class="hljs-comment">/* footer styling */</span>
<span class="hljs-selector-id">#footer</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#808080</span>;
    <span class="hljs-attribute">text-align</span>: center;
}
</code></pre>
<p>The icons are now displayed in the center with a hover effect which we can see in the below GIF file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/footer.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Footer</em></p>
<h2 id="heading-final-touches">Final Touches</h2>
<p>In order to add some spacing between all the sections, let's add some more styling: </p>
<pre><code class="lang-css"><span class="hljs-comment">/* spacing on all sections */</span>
<span class="hljs-selector-id">#about</span>, <span class="hljs-selector-id">#services</span>, <span class="hljs-selector-id">#portfolio</span>, <span class="hljs-selector-id">#contact</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
    <span class="hljs-attribute">padding-top</span>: <span class="hljs-number">4rem</span>;
}
<span class="hljs-selector-id">#contact</span> {
    <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">4rem</span>;
}
</code></pre>
<p>Now we're done making our complete portfolio website. </p>
<p>You can find the full source code of this project <a target="_blank" href="https://github.com/SampurnaC/portfolio_website_fcc/tree/master">here</a>. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This is how you can create a complete responsive portfolio website using HTML, CSS, JavaScript, and Bootstrap 5 .</p>
<p>In this blog post we saw some of the benefits of creating a portfolio website for web developers and designers. We divided the whole website into different sections and discussed each one individually as we built it.</p>
<p> You can customize this website based on your own use cases.</p>
<p>I hope you found this post useful. </p>
<p>Happy Coding!</p>
<p>You can find me on <a target="_blank" href="https://twitter.com/saam_codes">Twitter</a> for daily content regarding Web development.</p>
 ]]>
                </content:encoded>
            </item>
        
            <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 Build Your Own Developer Portfolio Website with HTML, CSS, and JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Everyone needs websites and web applications these days. So there are many opportunities for you if you work as a web developer.  But if you want to get a web developer job, you'll need a good portfolio website to showcase your skills and experience.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-developer-portfolio-website/</link>
                <guid isPermaLink="false">66adf0fbf452caf50fb1fdf0</guid>
                
                    <category>
                        <![CDATA[ Job Hunting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ self-improvement  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kolade Chris ]]>
                </dc:creator>
                <pubDate>Mon, 04 Oct 2021 20:59:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/09/responsive-web-design.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Everyone needs websites and web applications these days. So there are many opportunities for you if you work as a web developer. </p>
<p>But if you want to get a web developer job, you'll need a good portfolio website to showcase your skills and experience.</p>
<p>In this tutorial, I'll discuss some of the main reasons why you should make a portfolio website for yourself. Then, I'll walk you through how to build your own fully responsive portfolio website with HTML, CSS, and JavaScript.</p>
<h2 id="heading-table-of-content">Table of Content</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-a-developer-portfolio-website">What is a Developer Portfolio Website?</a></li>
<li><a class="post-section-overview" href="#heading-why-you-should-have-a-portfolio-website">Why you should have a Portfolio Website</a></li>
<li><a class="post-section-overview" href="#heading-portfolio-project-how-to-build-your-own-online-developer-portfolio">Portfolio Project – How to Build Your Own Online Developer Portfolio</a></li>
<li><a class="post-section-overview" href="#heading-the-project-folder-structure">The Project Folder Structure</a></li>
<li><a class="post-section-overview" href="#heading-the-basic-html-boilerplate">The Basic HTML Boilerplate</a></li>
<li><a class="post-section-overview" href="#heading-the-navbar-section">The Navbar Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-navbar">How to Style the Navbar</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-hero-section">How to Build the Hero Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-hero-section">How to Style the Hero Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-more-about-me-section">How to Build the More About Me Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-skills-section">How to Build the Skills Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-skills-section">How to Style the Skills Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-projects-section">How to Build the Projects Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-project-section">How to Style the Project Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-contact-section">How to Build the Contact Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-contact-section">How to Style the Contact Section</a></li>
<li><a class="post-section-overview" href="#heading-how-to-style-the-social-icons">How to Style the Social Icons</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-the-scroll-to-top-button">How to Add the Scroll to Top Button</a></li>
<li><a class="post-section-overview" href="#heading-the-html-for-the-scroll-to-top-button">The HTML for the Scroll to Top Button</a></li>
<li>H<a class="post-section-overview" href="#heading-how-to-style-the-scroll-to-top-icon">ow to Style the Scroll to Top Icon</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-your-portfolio-website-responsive">How to Make Your Portfolio Website Responsive</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-media-query-for-tablets-and-mobile-phones-max-width-720px">How to Create the Media Query for Tablets and Mobile Phones (max-width 720px)</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-hamburger-menu">How to Build the Hamburger Menu</a></li>
<li><a class="post-section-overview" href="#heading-the-javascript-for-the-hamburger-menu">The JavaScript for the Hamburger Menu</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-hero-section-responsive">How to Make the Hero Section Responsive</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-more-about-me-section-responsive">How to Make the More About Me Section Responsive</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-skills-section-responsive">How to Make the Skills Section Responsive</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-projects-section-responsive">How to Make the Projects Section Responsive</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-contact-form-responsive">How to Make the Contact Form Responsive</a></li>
<li><a class="post-section-overview" href="#heading-how-to-make-the-website-responsive-on-small-phones">How to Make the Website Responsive on Small Phones</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-is-a-developer-portfolio-website">What is a Developer Portfolio Website?</h2>
<p>A developer portfolio website provides relevant information to potential employers about your skills, experience, and projects you've worked on. </p>
<p>You can consider your portfolio website to be your online résumé.</p>
<h2 id="heading-why-you-should-have-a-portfolio-website">Why you should have a Portfolio Website</h2>
<h3 id="heading-1-a-portfolio-website-increases-your-online-presence">1. A Portfolio Website Increases Your Online Presence</h3>
<p>As a developer, you need an online presence. You can cultivate this online presence on social media platforms such as Twitter, Facebook, and Instagram.  But those are not entirely your own, as the moderators of those platforms have almost full control over your account.</p>
<p>With your own portfolio website, it's live on your own domain online. And people can easily find you when they search for your name on a search engine like Google, provided you put the right things in place when it comes to SEO.</p>
<h3 id="heading-2-a-portfolio-website-is-your-online-resume">2. A portfolio website is your online résumé</h3>
<p>Your portfolio website is like your online résumé. Potential clients and hiring managers can easily find you online and check out your previous projects and skills. </p>
<p>This also means that when anyone wants to give you an opportunity to work for them, and they ask for your previous projects, you just give them one link to your website (your portfolio). It not only has your projects but your skillset and information about your past experience as well.</p>
<h3 id="heading-3-a-portfolio-website-shows-evidence-of-expertise-in-your-field">3. A Portfolio Website Shows Evidence of Expertise in your Field</h3>
<p>Having (let alone building you own) portfolio website as a developer sends out a clear message that you're putting your skills into practice and that you know what you are doing. </p>
<p>A portfolio can also help build trust with clients because they have direct evidence of the quality of your work. </p>
<h2 id="heading-portfolio-project-how-to-build-your-own-online-developer-portfolio">Portfolio Project – How to Build Your Own Online Developer Portfolio</h2>
<p>You can make a cool portfolio website for yourself with HTML, CSS, and JavaScript. And that’s what we are going to do here. </p>
<p>I already did this some months ago and made it available to everyone as a free product on Gumroad, so I decided to create a tutorial on how I got it done.</p>
<p>This is the <a target="_blank" href="https://eager-williams-af0d00.netlify.app/?">live demo</a> of what we will be building.</p>
<p>To follow along with me, you can grab the starter files from <a target="_blank" href="https://github.com/Ksound22/developer-portfolio/tree/starter">Github</a>.</p>
<h3 id="heading-the-project-folder-structure">The Project Folder Structure</h3>
<p>To avoid confusion, I will be arranging the HTML, CSS, JavaScript, icons, and images of the project in their respective folders. </p>
<p>The HTML file goes in the root folder, and the image, icon, CSS, and JavaScript files will be in their separate subfolders in an asset folder. This is a common practice.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/ss1.png" alt="ss1" width="600" height="400" loading="lazy"></p>
<p>There is also a readme file containing all the tools I used in the project, with their respective links. It's available in the starter files.</p>
<h3 id="heading-the-basic-html-boilerplate">The Basic HTML Boilerplate</h3>
<p>Everyone has their preferences when coding out a whole project with HTML, CSS, and JavaScript. Some like to define the whole HTML boilerplate first and then the CSS later, but I like to do everything section by section. </p>
<p>So, I will be starting with the navbar section. But it’s good to show what the basic HTML boilerplate looks like first:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</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.0"</span> /&gt;</span>

    <span class="hljs-comment">&lt;!--CSS Styles --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"assets/css/styles.css"</span> /&gt;</span>

    <span class="hljs-comment">&lt;!-- Favicons --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span>
      <span class="hljs-attr">rel</span>=<span class="hljs-string">"apple-touch-icon"</span>
      <span class="hljs-attr">sizes</span>=<span class="hljs-string">"180x180"</span>
      <span class="hljs-attr">href</span>=<span class="hljs-string">"assets/icons/apple-touch-icon.png"</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">type</span>=<span class="hljs-string">"image/png"</span>
      <span class="hljs-attr">sizes</span>=<span class="hljs-string">"32x32"</span>
      <span class="hljs-attr">href</span>=<span class="hljs-string">"assets/icons/favicon-32x32.png"</span>
    /&gt;</span>

    <span class="hljs-comment">&lt;!-- Animate CSS CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span>
      <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
      <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Jane Doe | Web Developer<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

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

    <span class="hljs-comment">&lt;!-- Hero Section --&gt;</span>

    <span class="hljs-comment">&lt;!-- More about --&gt;</span>

    <span class="hljs-comment">&lt;!-- Skills section --&gt;</span>

    <span class="hljs-comment">&lt;!-- Projects section --&gt;</span>

    <span class="hljs-comment">&lt;!-- Contact section --&gt;</span>

    <span class="hljs-comment">&lt;!-- Social accounts - Fixed to the right --&gt;</span>

    <span class="hljs-comment">&lt;!-- Scroll to top --&gt;</span>

    <span class="hljs-comment">&lt;!-- Footer section --&gt;</span>

    <span class="hljs-comment">&lt;!-- Website scripts --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/js/app.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Ion icons scripts --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>
      <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"</span>
    &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>
      <span class="hljs-attr">nomodule</span>
      <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"</span>
    &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>I have all the sections in the HTML commented out so you can follow along better. In the boilerplate there are also the CDNs for animate CSS (A CSS animation library), and Ionic icons, the icon library I chose for the project.</p>
<p>I have a favicon made through Favicon IO and linked it in the head section. Favicon is the little image that shows on a browser tab. </p>
<h3 id="heading-the-navbar-section">The Navbar Section</h3>
<p>The Navbar section contains the simple logo of <code>h1</code> text, and the nav menu:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>JANE DOE<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navigation"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#about"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</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">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#skills"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span>&gt;</span>Skills<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#projects"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</span>&gt;</span>Projects<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"nav-link"</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">li</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">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"burger-menu"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"burger-menu"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ion-icon</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bars"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"menu-outline"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ion-icon</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</code></pre>
<p>If you are wondering what the button element represents, it’s the bars for toggling the nav menu on mobile (a hamburger menu). This will be hidden on desktop but shown on mobile.</p>
<p>I will also be linking the individual sections of the website to these nav items, so when the user clicks on any of the nav items, they are taken to the section that corresponds to the nav item they click. </p>
<p>That’s why I have the hyperlink reference (<code>href</code>) attributes set to <code>#about</code>, <code>#skills</code>, <code>#projects</code>, and <code>#contacts</code>, respectively. The individual section of the website will have these attributes as ids. </p>
<p>The navbar now looks like this: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss2.png" alt="ss2" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-style-the-navbar">How to Style the Navbar</h3>
<p>The navbar definitely needs some styling to make it look a bit nicer. </p>
<p>Before styling the navbar properly, I will be declaring some CSS variables to make things easier later. This is because, with CSS variables, it is easier to avoid redundancy and repetition in your CSS file. </p>
<p>The syntax for declaring CSS variables looks like this:</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--variable-name</span>: value;
}
</code></pre>
<p>To use the variable, you do this:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">selector</span> {
  <span class="hljs-attribute">property</span>: <span class="hljs-built_in">var</span>(--variable-name);
}
</code></pre>
<p>I will also import the Roboto font from Google, and declare some CSS resets to remove some default features such as margin and padding for elements, <code>text-decoration</code> for anchor tags, and <code>list-style-type</code> for lists.</p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> url(<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,900;1,700&amp;display=swap"</span>);

<span class="hljs-comment">/* Variables */</span>
<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">--font-family</span>: <span class="hljs-string">"Roboto"</span>, sans-serf;
  <span class="hljs-attribute">--normal-font</span>: <span class="hljs-number">400</span>;
  <span class="hljs-attribute">--bold-font</span>: <span class="hljs-number">700</span>;
  <span class="hljs-attribute">--bolder-font</span>: <span class="hljs-number">900</span>;
  <span class="hljs-attribute">--bg-color</span>: <span class="hljs-number">#fcfcfc</span>;
  <span class="hljs-attribute">--primary-color</span>: <span class="hljs-number">#4756df</span>;
  <span class="hljs-attribute">--secondary-color</span>: <span class="hljs-number">#ff7235</span>;
  <span class="hljs-attribute">--primary-shadow</span>: <span class="hljs-number">#8b8eaf</span>;
  <span class="hljs-attribute">--secondary-shadow</span>: <span class="hljs-number">#a17a69</span>;
  <span class="hljs-attribute">--bottom-margin</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">--bottom-margin-2</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">--line-height</span>: <span class="hljs-number">1.7rem</span>;
  <span class="hljs-attribute">--transition</span>: <span class="hljs-number">0.3s</span>;
}
<span class="hljs-comment">/* Variables end */</span>

<span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">scroll-behavior</span>: smooth;
}

<span class="hljs-comment">/* CSS Resets */</span>
* {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">ul</span> {
  <span class="hljs-attribute">list-style-type</span>: none;
}

<span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--primary-color);
}

<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary-color);
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-built_in">var</span>(--font-family);
}
</code></pre>
<p>If you notice, I set a hover state for all links on the website from line 39 to 41. When the user hovers on any link, it changes to the secondary color I set in the CSS variables.</p>
<p>Here's a good rule of thumb for declaring CSS variables: if you find yourself using the same property and value often in the same CSS file, you should declare a variable for it to avoid repetition. </p>
<p>You should also make your variable names are as descriptive as possible, like I did, in order to help others who might work with your code.</p>
<p>With the resets, there are some changes to the navbar in the browser:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss3.png" alt="ss3" width="600" height="400" loading="lazy"></p>
<p>To style the navbar and align the content in it, I will be using CSS Flexbox:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">nav</span> {
  <span class="hljs-attribute">position</span>: sticky;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: space-between;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5rem</span> <span class="hljs-number">3.5rem</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--bg-color);
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">3px</span> <span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
}
</code></pre>
<p><strong>What's the CSS above doing?</strong></p>
<p>I made the navbar sticky with the position property, so it remains at the top no matter what. </p>
<p>The <code>z-index</code> property with the value of 1 makes sure the navbar displays over any other element on the web page. That's how you make a sticky navbar.</p>
<p>In addition, I also applied a shadow to the bottom of the navbar with the <code>box-shadow</code> property.</p>
<p>The navbar has a new look: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss4.png" alt="ss4" width="600" height="400" loading="lazy"></p>
<p>But we're not finished yet. The nav menu items need to be side by side, not on top of each other. I will be doing that with Flexbox too. </p>
<p>I will also finish up the rest of the navbar styling by making the h1, nav items, and the hamburger menu button look nicer. I'll do this with some CSS variables initially declared.</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--primary-color);
}

<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--primary-color);
  <span class="hljs-attribute">transition</span>: <span class="hljs-built_in">var</span>(--transition);
}
<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--secondary-color);
  <span class="hljs-attribute">border-bottom</span>: <span class="hljs-number">2px</span> solid <span class="hljs-built_in">var</span>(--secondary-color);
}

<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">ul</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">1.9rem</span>;
}

<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">ul</span> <span class="hljs-selector-tag">li</span> {
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-built_in">var</span>(--bold-font);
}
</code></pre>
<p>The hamburger menu bar also needs to be hidden. It has a class of <code>.burger-menu</code>, so we can set a display of none with it and also make the button look better.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.burger-menu</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--primary-color);
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background-color</span>: transparent;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">display</span>: none;
}
</code></pre>
<p>Our navbar looks way nicer now: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss5.png" alt="ss5" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-the-hero-section">How to Build the Hero Section</h3>
<p>The next section we'll work on is the hero section. This won’t take quite as much work as the navbar.</p>
<p>The HTML boilerplate for the hero section is in the code snippet below:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hero"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"about"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
        <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/images/wfh_1.svg"</span>
        <span class="hljs-attr">alt</span>=<span class="hljs-string">"jane-doe"</span>
        <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"hero-img"</span>
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio animate__animated animate__shakeX"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio-title"</span>&gt;</span>About Me<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">"bio-text"</span>&gt;</span>
          Lorem ipsum dolor sit amet, consectetur adipisicing elit. Mollitia sed
          dolorem fugit sapiente porro veniam pariatur dolore nostrum delectus
          inventore tempore minus nemo, iste ullam illo laboriosam maiores
          repudiandae quos!
        <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">section</span>&gt;</span>
</code></pre>
<p>The only thing that's a bit strange are the classes of <code>animate__animated animate__shakeX</code> attached to the div containing the <code>About Me</code> text. The class names are from animate CSS and they serve to animate the About Me text  container.</p>
<p>With this, the website gets a new look:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss6.png" alt="ss6" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-style-the-hero-section">How to Style the Hero Section</h3>
<p>Flexbox will come to the rescue once again! This section has two major sets of content – an image and text in a div. So we can use flexbox to display them side by side. You can see how it works in the CSS code snippet below:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hero</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">2.5rem</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">68.75rem</span>;
  <span class="hljs-attribute">margin</span>: auto;
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/ss7.png" alt="ss7" width="600" height="400" loading="lazy"></p>
<p>Our Jane Doe image is too big, so we need to reduce its width and height. 
We also need to style the bio text (About Me text) for readability too. The CSS variables initially declared will be very instrumental here.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hero</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">37.5rem</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">37.5rem</span>;
}

<span class="hljs-selector-class">.bio</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">25rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.625rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">6px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">2px</span> <span class="hljs-number">15px</span> <span class="hljs-number">2px</span> <span class="hljs-built_in">var</span>(--primary-shadow);
}

<span class="hljs-selector-class">.bio</span> <span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-built_in">var</span>(--bottom-margin);
}

<span class="hljs-selector-class">.bio</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">line-height</span>: <span class="hljs-built_in">var</span>(--line-height);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.3rem</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>The hero section now looks beautiful: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss8.png" alt="ss8" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-the-more-about-me-section">How to Build the More About Me Section</h3>
<p>I included this section to include some more information about Jane Doe with some placeholder text. </p>
<p>You can take advantage of this to include information you were unable to put in the About Me section.</p>
<p>The HTML boilerplate for this section is quite short and simple:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"more-about"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>More About Me<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>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Reiciendis
        nesciunt excepturi quos obcaecati incidunt voluptatem ipsam sunt ipsum,
        autem deleniti cupiditate molestias quis unde quae totam porro dicta
        iure animi inventore, veniam hic! Omnis nulla, delectus a voluptatibus
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        Lorem ipsum dolor sit amet consectetur, adipisicing elit. Consequuntur
        nostrum dolor minus, libero delectus praesentium perferendis
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Vero,
        consequuntur labore? Ea totam voluptas amet!
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>The CSS is straightforward as well. All we'll do is set a <code>background-color</code> with the <code>--bg-color</code> CSS variable, make the section readable by setting the padding, margin, line-height, and aligning the h2 text to the center:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.more-about</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--bg-color);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span> <span class="hljs-number">6rem</span>;
}

<span class="hljs-selector-class">.more-about</span> <span class="hljs-selector-tag">h2</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-built_in">var</span>(--bottom-margin);
  <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-class">.more-about</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">line-height</span>: <span class="hljs-built_in">var</span>(--line-height);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.4rem</span>;
}
</code></pre>
<p>In the browser, the More About section looks like this:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss9.png" alt="ss9" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-the-skills-section">How to Build the Skills Section</h3>
<p>From the live demo, you'll see that the skills section contains relevant skills such as HTML, CSS, JavaScript, and so on. I was able to get the icons of those languages as SVGs from Icons8.</p>
<p>The HTML boilerplate for this section is in the code snippet below:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skill-header"</span>&gt;</span>My Top Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills-wrapper"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"first-set animate__animated animate__pulse"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-html-5.svg"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"icon icon-card"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-css3.svg"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"icon icon-card"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-javascript.svg"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"icon icon-card"</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">"second-set animate__animated animate__pulse"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-bootstrap.svg"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"icon icon-card"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-react-native.svg"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"icon icon-card"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-git.svg"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"icon icon-card"</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">section</span>&gt;</span>
</code></pre>
<p>There are six icons in total. And instead of having to align them with Flexbox, I grouped them in two places (3 teach), with the classes of first-set and second-set, so they stay on top of each other. This means that the stylings we'll apply will be more readable. Easy!</p>
<p>Notice that I’ve been attaching the loading attribute to the individual icons and images and setting it to lazy. This will make sure that the images are loaded only when the user scrolls to the sections containing them. This will subsequently speed up load time, because only what is needed will be loaded.</p>
<h3 id="heading-how-to-style-the-skills-section">How to Style the Skills Section</h3>
<p>Without styling, the skills section looks like this:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss10edited.jpg" alt="ss10edited" width="600" height="400" loading="lazy"></p>
<p>We should style the section a little bit because it doesn’t look good enough yet: </p>
<pre><code class="lang-css"><span class="hljs-selector-class">.skills</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">68.75rem</span>;
  <span class="hljs-attribute">margin</span>: auto;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2.5rem</span>;
}

<span class="hljs-selector-class">.skill-header</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-class">.skills-wrapper</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.25rem</span>;
}

<span class="hljs-selector-class">.icon</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">11.875rem</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">11.25rem</span>;
}
</code></pre>
<p>In the CSS above, I defined a maximum width for the whole section to push things to the center for a better user experience. </p>
<p>Other stylings we applied relate to clarity and readability. For example, I increased the size of the icons to make them more visible with the width and height properties. I also applied a padding of 1rem (16 pixels) to all the icons to push them apart from each other a little bit.</p>
<p>The skills section now looks cool:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss11.png" alt="ss11" width="600" height="400" loading="lazy"></p>
<p>Still, I think the section can be better, so I have decided to make some more tweaks with the box-shadow property. </p>
<p>Remember from the HTML that there is a class attribute called <code>.icon-card</code> attached to all the icons. I will be using the class name to put all the icons in a card:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.icon-card</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">11px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">3px</span> <span class="hljs-number">10px</span> <span class="hljs-built_in">var</span>(--secondary-shadow);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span>;
}
</code></pre>
<p>The skills section looks a lot better:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss12.png" alt="ss12" width="600" height="400" loading="lazy">
Look at that!</p>
<h3 id="heading-how-to-build-the-projects-section">How to Build the Projects Section</h3>
<p>One of the major purposes of a portfolio website is to show off your projects. So we'll need to build a section to showcase projects you've worked on in the past.</p>
<p>This section is probably the most tedious to style, but Flexbox won’t stop being our friend.</p>
<p>The HTML for this section is in the code snippet below:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"projects"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects-title"</span>&gt;</span>Some of my Recent Projects<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects-container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-container project-card"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/images/expenseTracker.png"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">"expense-tracker"</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"project-pic"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-title"</span>&gt;</span>Expense Tracker<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-details"</span>&gt;</span>
            Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quas
            ratione vel inventore labore commodi modi quos culpa aut saepe!
            Alias!
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-link"</span>&gt;</span>Check it Out<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">"project-container project-card"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/images/netflixClone.png"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">"netflic-clone"</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"project-pic"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-title"</span>&gt;</span>Netflix Clone<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-details"</span>&gt;</span>
            Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quas
            ratione vel inventore labore commodi modi quos culpa aut saepe!
            Alias!
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-link"</span>&gt;</span>Check it Out<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">"project-container project-card"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/images/greenyEarth.png"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">"greeny-earth"</span>
            <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"project-pic"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-title"</span>&gt;</span>Greeny Earth<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-details"</span>&gt;</span>
            Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quas
            ratione vel inventore labore commodi modi quos culpa aut saepe!
            Alias!
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"project-link"</span>&gt;</span>Check it Out<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">section</span>&gt;</span>
</code></pre>
<p>Looking at the HTML, there are three projects in total, all in their individual divs with the class name of project-container and project-card. These class names will be instrumental in styling the projects consistently. </p>
<p>The containing section element itself has a class of projects, and an id attribute of projects as well. The class name is for styling, and the id is for linking it to the Projects link on the navbar.  </p>
<p>The projects have their individual images with the class name of <code>project-pic</code>, their titles with a class of <code>project-title</code>, more details with the class name of <code>project-details</code>, and links with the class name of <code>project-link</code>. </p>
<p>The sole purpose of giving all of them unique class names is to style them.</p>
<p>These are a few of the projects I worked on myself when I was starting out as a developer.</p>
<p>The projects section looks like this in the browser:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/projects-unstyled.gif" alt="projects-unstyled" width="600" height="400" loading="lazy"></p>
<p>The section doesn’t look good yet, though – there is even an annoying horizontal scrollbar caused by the images. So we have a lot to do with CSS.</p>
<h3 id="heading-how-to-style-the-project-section">How to Style the Project Section</h3>
<p>First of all, I will give the whole section a background color by setting the greyish color (--bg-color) we declared in the CSS variables as the value. </p>
<p>I will also reduce the width and height of the project images by usung the <code>project-pic</code> class. Then I'll use Flexbox to put the projects side by side.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.projects</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--bg-color);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">32px</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2rem</span>;
}

<span class="hljs-selector-class">.project-pic</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">65%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">60%</span>;
}

<span class="hljs-selector-class">.projects-container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
}
</code></pre>
<p>The section looks a lot better:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss13Edited.jpg" alt="ss13Edited" width="600" height="400" loading="lazy"></p>
<p>The images now look better, but the project title, project details, and project links need to be aligned nicely within their individual containers. </p>
<p>The whole project section also needs to be pushed to the center. You don’t need Flexbox to do this, though – it can be done by setting the text align property to the value of center:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.projects-title</span> {
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
}

<span class="hljs-selector-class">.project-container</span> {
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">21.875rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
}
</code></pre>
<p>Notice that I also set a width of <code>21.875rem (350 pixels)</code> for the individual project containers. This will push them apart from the sides for a better user experience. In this case, the user would not need to look all the way across before they see everything. </p>
<p>The section now looks better:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss14.png" alt="ss14" width="600" height="400" loading="lazy"></p>
<p>We can still make this section better. The project titles, project details and project links look chunked together, so we should add some padding and margins. </p>
<p>The individual project containers also need to look more distinct. The <code>box-shadow</code> property will be instrumental here again, so I’m putting them in their individual cards.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.project-container</span> <span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.4rem</span>;
}

<span class="hljs-selector-class">.project-title</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-built_in">var</span>(--bottom-margin);
}

<span class="hljs-selector-class">.project-details</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-built_in">var</span>(--bottom-margin);
}

<span class="hljs-selector-class">.project-card</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">11px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">3px</span> <span class="hljs-number">10px</span> <span class="hljs-built_in">var</span>(--primary-shadow);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">20px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span>;
}
</code></pre>
<p>The project section looks way better now: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss15.png" alt="ss15" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-build-the-contact-section">How to Build the Contact Section</h3>
<p>If a potential employer or client finds your portfolio website attractive, they might want to contact you. So you'll want to have a contact form in this section, alongside links to your social media profiles.</p>
<p>The HTML for this section looks like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Get In Touch With Me<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"contact-form-container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"contact-form"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"https://formspree.io/f/xyylngw"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"sender-name"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter Your Name"</span>
                <span class="hljs-attr">class</span>=<span class="hljs-string">"input-field"</span>
                <span class="hljs-attr">required</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">"form-control"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"sender-email"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter Your Email"</span>
                <span class="hljs-attr">class</span>=<span class="hljs-string">"input-field"</span>
                <span class="hljs-attr">required</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">"form-control"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"message"</span>&gt;</span>Message<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span>
                <span class="hljs-attr">cols</span>=<span class="hljs-string">"60"</span>
                <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter Your Message"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
                <span class="hljs-attr">class</span>=<span class="hljs-string">"input-field"</span>
                <span class="hljs-attr">required</span>
              &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">input</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
              <span class="hljs-attr">value</span>=<span class="hljs-string">"Submit"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"submit-btn"</span>
              <span class="hljs-attr">class</span>=<span class="hljs-string">"submit-btn"</span>
            /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">section</span>&gt;</span>
</code></pre>
<p>Here we've built a contact form with input fields for name and email, a <code>textarea</code> so people can enter the message to be sent, and a submit button for submitting the message so you can see it. </p>
<p>If you take a good look at the form element, you’ll see I have an action attribute set to a URL from Formspree. This is what I chose for the form submission. With Formspree, you can get the message directly in your email inbox without having to set up a server with complex PHP or JavaScript.</p>
<p>Note that you can't use my URL – it won't work for you. You can easily setup your own on the Formspree website for free. I also attached a resource on how to set up Formspree to the readme file of the project. </p>
<p>I have set some <code>id</code> and <code>class</code> attributes for the individual inputs to style them. There is also a <code>name</code> attribute for all the input fields. This is required by the Formspree form submission service.</p>
<p>To get a basic validation, I attached a <code>required</code> attribute, so the form refuses to submit if the user leaves any of the input fields unfilled.</p>
<h3 id="heading-how-to-style-the-contact-section">How to Style the Contact Section</h3>
<p>Without styling, the contact section doesn’t look good at all:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss16Edited.jpg" alt="ss16Edited" width="600" height="400" loading="lazy"></p>
<p>All I will do in the CSS is align the whole content to the center and make the input fields look better.</p>
<p>With the text align and margin properties, you can align the h2 and the container for the contact form to the center. </p>
<p>I will also put the whole form in a card with the <code>box-shadow</code> property.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.contact</span> {
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2rem</span>;
}

<span class="hljs-selector-class">.contact</span> <span class="hljs-selector-tag">h2</span> {
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-built_in">var</span>(--bottom-margin-<span class="hljs-number">2</span>);
}

<span class="hljs-selector-class">.contact-form-container</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">40.75rem</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.938rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">3px</span> <span class="hljs-number">10px</span> <span class="hljs-built_in">var</span>(--secondary-shadow);
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/ss17.png" alt="ss17" width="600" height="400" loading="lazy"></p>
<p>The input fields, textarea, labels and placeholders definitely need some styling as well to help with alignment and clarity:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.contact-form-container</span> <span class="hljs-selector-tag">label</span> {
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">2.7em</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-built_in">var</span>(--bold-font);
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--primary-color);
}

<span class="hljs-selector-class">.contact-form-container</span> <span class="hljs-selector-tag">textarea</span> {
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">6.25rem</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">14px</span>;
}

<span class="hljs-selector-class">.contact-form-container</span> <span class="hljs-selector-class">.input-field</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">padding-top</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> outset <span class="hljs-built_in">var</span>(--primary-color);
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">0.875rem</span>;
  <span class="hljs-attribute">outline</span>: none;
}
</code></pre>
<p>The form looks better now: <img src="https://www.freecodecamp.org/news/content/images/2021/10/ss18.png" alt="ss18" width="600" height="400" loading="lazy"></p>
<p>But the placeholders are not consistent with the labels. So we need to give it a color and some padding. I will be giving it the primary color set in the CSS variable lists. </p>
<p>To select the placeholders for styling, you can use the placeholder pseudo-class:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.input-field</span><span class="hljs-selector-pseudo">::placeholder</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--primary-color);
}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/ss19.png" alt="ss19" width="600" height="400" loading="lazy"></p>
<p>In the contact form, the only thing left is to style the button. Buttons are quite easy to style:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.submit-btn</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-built_in">var</span>(--primary-color);
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-built_in">var</span>(--bold-font);
  <span class="hljs-attribute">transition</span>: <span class="hljs-built_in">var</span>(--transition);
}
</code></pre>
<p>In the CSS code snippet above, I made the button go all the way across in the form container by giving it a width of 100%. I also made it more visible with some padding, a margin, a border, and a bolder font weight.</p>
<p>The <code>border-radius</code> property with a value of 5px removes the sharp edges and the transition serves to slow things down a little when the button is in the hover state. </p>
<p>The hover state is defined in the CSS code snippet below:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.submit-btn</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--primary-color);
  <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid <span class="hljs-built_in">var</span>(--primary-color);
  <span class="hljs-attribute">cursor</span>: pointer;
}
</code></pre>
<p>The form looks much better now:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/contact-form-hover-effect.gif" alt="contact-form-hover-effect" width="600" height="400" loading="lazy"></p>
<p>Remember that having your social media links in your portfolio website is a plus for anyone who might want to contact you. That’s the next thing we are going to do, and we are going to do it in a unique way.</p>
<p>The HTML for the social buttons is in the code snippet below:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"socials"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
        &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span>
          <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-twitter-circled.gif"</span>
          <span class="hljs-attr">alt</span>=<span class="hljs-string">"Twitter"</span>
          <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"socicon"</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">"#"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
        &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span>
          <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-instagram.gif"</span>
          <span class="hljs-attr">alt</span>=<span class="hljs-string">"Instagram"</span>
          <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"socicon"</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">"#"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
        &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span>
          <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-linkedin-circled.gif"</span>
          <span class="hljs-attr">alt</span>=<span class="hljs-string">"Linkedin"</span>
          <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"socicon"</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">"#"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
        &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-github.gif"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Github"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"socicon"</span>
      /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>The social icons I chose are animated gif icons from icons8. I put all of them in a container with the class of <code>socials</code>, and gave them an individual class of <code>socicon</code> for styling.</p>
<p>Take a look at some animated social media icons winking at you below:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/animated-social-icons.gif" alt="animated-social-icons" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-style-the-social-icons">How to Style the Social Icons</h3>
<pre><code class="lang-css"><span class="hljs-selector-class">.socials</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">1%</span>;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">50%</span>;
}

<span class="hljs-selector-class">.socicon</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">2rem</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">2rem</span>;
}
</code></pre>
<p>With the CSS above, the social icons will be fixed to the right on the web page, so anyone who visits the website sees them no matter where they scroll. </p>
<p>I also reduced the size of the icons by assigning them all reduced <code>width</code> and <code>height</code> property values.
<img src="https://www.freecodecamp.org/news/content/images/2021/10/good-icons.gif" alt="good-icons" width="600" height="400" loading="lazy"></p>
<p>Look at that!</p>
<p>The only thing left to do is the footer. There’s nothing complex in the footer HTML and CSS apart from the reserved character entity for copyright symbol and heart:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"copy"</span>&gt;</span><span class="hljs-symbol">&amp;copy;</span> Copyright 2021<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"copy"</span>&gt;</span>
        Built with <span class="hljs-symbol">&amp;#x2661;</span> by
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://twitter.com/koladechris"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
          &gt;</span>Kolade Chris (Ksound)<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>
        &gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</code></pre>
<pre><code class="lang-css"><span class="hljs-selector-tag">footer</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--bg-color);
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.25rem</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>We now have a full-fledged portfolio website! 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/full-fledged-portfolio.gif" alt="full-fledged-portfolio" width="600" height="400" loading="lazy"></p>
<p>But we need to make it responsive, because it doesn’t look good on smaller devices:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/unresponsive-portfolio.gif" alt="unresponsive-portfolio" width="600" height="400" loading="lazy"></p>
<p>We need to make all the content of the individual sections display on section on top of the another (in a column layout). We can do this pretty easily with media queries and Flexbox. </p>
<p>Before adding the media queries for responsiveness, lets implement a scroll-to-top button with HTML, CSS, and JavaScript.</p>
<h2 id="heading-how-to-add-the-scroll-to-top-button">How to Add the Scroll to Top Button</h2>
<h3 id="heading-the-html-for-the-scroll-to-top-button">The HTML for the Scroll to Top Button</h3>
<p>For the HTML, I got an animated icon from Icons8 and decided to put it in an i tag. </p>
<p>The i tag has a class of <code>scroll-up</code> for styling and an id of <code>scroll-up</code> for selecting it with JavaScript. This is because in my projects, I like to use classes for styling and ids for JavaScript functionalities.</p>
<pre><code class="lang-html">  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"scroll-up"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"scroll-up"</span>
      &gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">img</span>
        <span class="hljs-attr">src</span>=<span class="hljs-string">"assets/icons/icons8-upward-arrow.gif"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"socicon up-arrow"</span>
        <span class="hljs-attr">alt</span>=<span class="hljs-string">"scroll-up"</span>
    /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-style-the-scroll-to-top-icon">How to Style the Scroll to Top Icon</h3>
<p>I will make the scroll-to-top icon fixed just like the social icons. I'll also give it a cursor property of pointer, so the cursor changes when the user hovers on it.</p>
<p>With the class of up-arrow attached to the scroll-to-top icon, I will also increase the size of the icon for visibility:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.scroll-up</span> {
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">0.5%</span>;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">3%</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-class">.up-arrow</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">3rem</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">3rem</span>;
}
</code></pre>
<p>The icon looks good: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/scroll-up-first.gif" alt="scroll-up-first" width="600" height="400" loading="lazy"></p>
<p>But it doesn’t do anything yet. So we need to make it functional with a few lines of JavaScript:</p>
<pre><code class="lang-js"><span class="hljs-comment">// scroll to top functionality</span>
<span class="hljs-keyword">const</span> scrollUp = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#scroll-up"</span>);

scrollUp.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">window</span>.scrollTo({
    <span class="hljs-attr">top</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">left</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span>,
  });
});
</code></pre>
<p><strong>What is the script above doing?</strong></p>
<p>The first line selects the scroll-to-top button with the id attribute attached to it in the HTML. We used the <code>querySelector()</code> method here. You can also use the <code>getElementById()</code> method.</p>
<p>In the remaining lines, I used the click <code>eventListener</code> to get the user’s click action and exploit the <code>scrollTo</code> part of the windows object to make the button functional.</p>
<p>With this functionality, when the user clicks on the scroll-to-top button, the page scrolls to the top and left side of the website smoothly. I did this by setting top to <code>0</code>, left to <code>0</code>, and behavior to <code>smooth</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/scroll-up.gif" alt="scroll-up" width="600" height="400" loading="lazy"></p>
<p>You can learn more about the windows object by opening up your browser’s developer tools console. Type in window and hit enter, then you see everything available in the windows object, like I did below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/window-object.gif" alt="window-object" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-make-your-portfolio-website-responsive">How to Make Your Portfolio Website Responsive</h2>
<p>To make the website responsive, we will be using CSS media queries and Flexbox.</p>
<p>First, we'll need to make the images and text look smaller, and then we'll make the content of each section display in a vertical layout by setting the <code>flex-direction</code> to column.</p>
<p>In the media query, I will be using 2 breakpoints – <code>720px</code> and <code>420px</code>.</p>
<p>The 720px breakpoint is for tablets and mobile phones, and 420px is for small phones like an iPhone 6, and small Android phones. </p>
<p>Media query Breakpoints are the points at which you want the content of a website to respond according to the width of a device. So, any code put under the 720px breakpoint reflects on devices with a screen less than or more than 720px, depending on whether you specify max-width or min-width.</p>
<p>In the case of a max-width of <code>720px</code>, the media query syntax looks like this:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">720px</span>) {
  <span class="hljs-comment">/*changes reflects on screen with a width of 720px and below*/</span>
}
</code></pre>
<h3 id="heading-how-to-create-the-media-query-for-tablets-and-mobile-phones-max-width-720px">How to Create the Media Query for Tablets and Mobile Phones (max-width 720px)</h3>
<p>We will start making the website responsive right from the navbar, because the navbar doesn’t look good on smaller devices. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/ss21.png" alt="ss21" width="600" height="400" loading="lazy"></p>
<p>First, I’m going to reduce the padding of the navbar so the <code>h1</code> logo and nav menu items fit in nicely:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">nav</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5rem</span> <span class="hljs-number">1rem</span>;
  }
</code></pre>
<p>Things now look a bit better:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss22.png" alt="ss22" width="600" height="400" loading="lazy"></p>
<p>On small devices, the nav menu items need to be on top of one another, and they need to be hidden. So, its time to update the code so the hamburger menu is initially hidden.</p>
<h3 id="heading-how-to-build-the-hamburger-menu">How to Build the Hamburger Menu</h3>
<p>To make the hamburger menu, we need to take the nav menu items out of the viewport. Then we need to set a class of <code>show</code> on the nav list items that will be toggled with few lines of JavaScript (remember the nav items are in an unordered list).</p>
<pre><code class="lang-css"> <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">position</span>: fixed;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--bg-color);
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">top</span>: <span class="hljs-number">86px</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">10%</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(<span class="hljs-number">120%</span>);
    <span class="hljs-attribute">transition</span>: transform <span class="hljs-number">0.5s</span> ease-in;
  }

   <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">ul</span> <span class="hljs-selector-tag">li</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">8px</span>;
  }
</code></pre>
<p>In the CSS code snippet above, I set a position of fixed on the unordered list (<code>ul</code>) to make it float on the screen. I also pushed it down 86px from the top with <code>top: 86px</code>, and 10% to the left. </p>
<p>I gave it a width of 80% of its parent (the nav element from the HTML), pushed it to the center with <code>text-align: center</code>, and finally hid it with the transform property set to <code>translateX(120%)</code>. This will push it to the right and force it out of the viewport. </p>
<p>And now, when the user clicks to show the nav items, they all slide in from the right. Awesome. </p>
<p>If you want the nav menu items to slide in from the left, change the <code>transform</code> property value to <code>transform: translateX(-120%)</code> (this is the direct opposite of <code>transform: translateX(120%)</code>). It's as easy as that, depending on your preference.</p>
<p>I also assigned a margin of 8px to the nav items to give them more space.</p>
<p>The navbar now looks like this: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss22-1.png" alt="ss22-1" width="600" height="400" loading="lazy"></p>
<p>The hamburger menu bar remains hidden. So we need to show it by giving it a display of block, setting a class of show to translate on the x-axis to 0 in order to show it, and then toggle it with JavaScript.</p>
<pre><code class="lang-css"> <span class="hljs-selector-class">.burger-menu</span> {
    <span class="hljs-attribute">display</span>: block;
  }

  <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">ul</span><span class="hljs-selector-class">.show</span> {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(<span class="hljs-number">0</span>);
  }
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/ss24.png" alt="ss24" width="600" height="400" loading="lazy"></p>
<p>Our hamburger menu bars now gets shown, but the nav items remain hidden. To show it, we need to toggle the show class on and off with JavaScript.</p>
<h3 id="heading-the-javascript-for-the-hamburger-menu">The JavaScript for the Hamburger Menu</h3>
<p>To toggle the navbar nav menu items on and off with JavaScript, we first need to select all relevant items of the navbar and store them in some variables:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Nav hamburgerburger selections</span>

<span class="hljs-keyword">const</span> burger = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"#burger-menu"</span>);
<span class="hljs-keyword">const</span> ul = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"nav ul"</span>);
<span class="hljs-keyword">const</span> nav = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"nav"</span>);
</code></pre>
<ul>
<li>The <code>burger</code> variable select the hamburger menu bars</li>
<li>The <code>ul</code> variable selects the list items (the nav links altogether)</li>
<li>The <code>nav</code> variable selects the container itself (the nav element)</li>
</ul>
<p>What we need to do next is toggle the <code>nav ul.show</code> class when the user clicks the hamburger menu bar. We'll do this by adding a click <code>eventListener</code> to the hamburger menu bar, and then using the toggle method to remove and add the class of <code>show</code>.</p>
<p>Remember that we selected it and stored it in a variable called <code>burger</code>.</p>
<pre><code class="lang-js">burger.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
  ul.classList.toggle(<span class="hljs-string">"show"</span>);
});
</code></pre>
<p>Our nav items can now be toggled on and off:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/nav-toggling-quirk.gif" alt="nav-toggling-quirk" width="600" height="400" loading="lazy"></p>
<p>But there is a problem – the mobile nav is not hidden any time any of the nav item links are clicked. So we need to remove the class of nav ul.show when any of the nav item links are clicked. </p>
<p>We can do this with a few lines of JavaScript too:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Close hamburger menu when a link is clicked</span>

<span class="hljs-comment">// Select nav links</span>
<span class="hljs-keyword">const</span> navLink = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">".nav-link"</span>);

navLink.forEach(<span class="hljs-function">(<span class="hljs-params">link</span>) =&gt;</span>
  link.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
    ul.classList.remove(<span class="hljs-string">"show"</span>);
  })
);
</code></pre>
<p>Remember that the nav links have a class of <code>nav-link</code> from the HTML. So I selected all of them with that class and put them in a variable called navLink. We did this with the <code>querySelectorAll(</code>) method.</p>
<p>I then looped through all the links with the <code>forEach</code> array method and listened for a click event on all of them. Then I used the <code>remove()</code> method provided by the DOM to remove the class of <code>show</code> any time any of the nav menu items are clicked. This will take all the list items out of the viewport.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/nav-toggling-quirk-fixed.gif" alt="nav-toggling-quirk-fixed" width="600" height="400" loading="lazy"></p>
<p>Look at that!</p>
<p>That’s a lot of work. With what we just covered you can make a hamburger menu for any website.</p>
<h3 id="heading-how-to-make-the-hero-section-responsive">How to Make the Hero Section Responsive</h3>
<p>The hero section doesn't look that great at the moment: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss25.png" alt="ss25" width="600" height="400" loading="lazy"></p>
<p>All we need to do is give it a flex direction of column in the media query, reduce the width and height of Jane Doe's image, and make the About Me text (bio text) readable.</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.hero</span> {
    <span class="hljs-attribute">margin-top</span>: -<span class="hljs-number">4rem</span>;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">gap</span>: <span class="hljs-number">0</span>;
  }

  <span class="hljs-selector-class">.hero</span> <span class="hljs-selector-tag">img</span> {
        <span class="hljs-attribute">height</span>: <span class="hljs-number">37.5rem</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">30rem</span>;
    }

  <span class="hljs-selector-class">.bio</span> {
    <span class="hljs-attribute">margin-top</span>: -<span class="hljs-number">7rem</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">20.5rem</span>;
  }
</code></pre>
<p>The hero section looks better now:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss26.png" alt="ss26" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-make-the-more-about-me-section-responsive">How to Make the More About Me Section Responsive</h3>
<p>The More About Me section doesn’t look bad, but we can certainly improve it:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss27.png" alt="ss27" width="600" height="400" loading="lazy"></p>
<p>I have the following CSS to make it readable and more presentable:</p>
<pre><code class="lang-css"> <span class="hljs-selector-class">.more-about</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">2rem</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span> <span class="hljs-number">3.5rem</span>;
  }

  <span class="hljs-selector-class">.more-about</span> <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">text-align</span>: center;
  }

  <span class="hljs-selector-class">.more-about</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">text-align</span>: justify;
  }
</code></pre>
<p>I pushed the whole section down a little bit and increased the padding on all sides, aligned the <code>h2</code> to center, and justified the text.
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss28.png" alt="ss28" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-make-the-skills-section-responsive">How to Make the Skills Section Responsive</h3>
<p>The skill icons appear too big:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss29.png" alt="ss29" width="600" height="400" loading="lazy"></p>
<p>All we need to do in the media query is reduce the sizes of the icons with the width and height properties: </p>
<pre><code class="lang-css"><span class="hljs-selector-class">.icon</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">5.875rem</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">5.25rem</span>;
  }
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/ss30.png" alt="ss30" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-make-the-projects-section-responsive">How to Make the Projects Section Responsive</h3>
<p>In the projects section, we need to make the three projects stack on top of one another by setting the flex direction to column. I will also reduce the width of the individual containers a little bit.</p>
<pre><code class="lang-css"> <span class="hljs-selector-class">.projects-container</span> {
    <span class="hljs-attribute">flex-direction</span>: column;
  }

  <span class="hljs-selector-class">.project-container</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">20.875rem</span>;
  }
</code></pre>
<h3 id="heading-how-to-make-the-contact-form-responsive">How to Make the Contact Form Responsive</h3>
<p>The width of the contact form needs to be reduced to push it away from the sides and make sure that the fixed social media icons are not on top of it. </p>
<p>All we need to do is set a maximum width:</p>
<pre><code class="lang-css"> <span class="hljs-selector-class">.contact-form-container</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">23.75rem</span>;
  }
</code></pre>
<p>The contact form now looks better:
<img src="https://www.freecodecamp.org/news/content/images/2021/10/ss32.png" alt="ss32" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-make-the-website-responsive-on-small-phones">How to Make the Website Responsive on Small Phones</h3>
<p>On small phones such as the iPhone 6, 7, and 8 plus, the social icons and scroll-to-top icon are not showing. There is also a horizontal scrollbar.
<img src="https://www.freecodecamp.org/news/content/images/2021/10/smaller-phones-responive-quirks.gif" alt="smaller-phones-responive-quirks" width="600" height="400" loading="lazy"></p>
<p>To fix these quirks, I will be adding some media queries at the 420px breakpoint:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">420px</span>) {
  <span class="hljs-selector-class">.hero</span> <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">37.5rem</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">23rem</span>;
  }

  <span class="hljs-selector-class">.bio</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">18.3rem</span>;
  }

  <span class="hljs-selector-class">.project-container</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">17.875rem</span>;
  }

  <span class="hljs-selector-class">.contact-form-container</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">17.75rem</span>;
  }
}
</code></pre>
<p>I reduced the size of our Jane Doe image, and also reduced the width of the bio text (About Me text), the project container, and the contact form container as well. </p>
<p>All these changes will make the icons fixed to the right side of the website show – the social media and scroll-to-top icons.
<img src="https://www.freecodecamp.org/news/content/images/2021/10/smaller-phones-responsiveness-quirk-fixed.gif" alt="smaller-phones-responsiveness-quirk-fixed" width="600" height="400" loading="lazy"></p>
<p>Everything now looks good: 
<img src="https://www.freecodecamp.org/news/content/images/2021/10/fully-responsive.gif" alt="fully-responsive" width="600" height="400" loading="lazy"></p>
<p>That’s the end of it all. We have a fully responsive portfolio website.</p>
<p>You can download the finished version as a zip file from this <a target="_blank" href="https://github.com/Ksound22/developer-portfolio">GitHub repo</a>. </p>
<p>You can also check out the <a target="_blank" href="https://eager-williams-af0d00.netlify.app/?">live demo</a> of the portfolio website as well. It has a readme that contains information about the tools I used, and how you can customize the website. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you learned what a developer portfolio website is and why you should have one. </p>
<p>You also learned how to make a fully responsive portfolio website with HTML, CSS, and JavaScript. </p>
<p>The different parts of this tutorial are each small projects that, when combined, turn into a giant one-page website. For example, you can make card design, a responsive menu bar, a functional contact form, and a scroll-to-top button as the tutorial covers them all.</p>
<p>Feel free to customize the website to your taste. </p>
<p>If you find this tutorial useful, you can share it with your friends and family. I would really appreciate that.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Portfolio Site with Next.js and TailwindCSS ]]>
                </title>
                <description>
                    <![CDATA[ By Manu Arora If you're a web developer, it's important for you to have a personal portfolio website – especially when you're applying for jobs.  If you have a nice online portfolio site, you'll have a better chance of getting attention from recruite... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-portfolio-site-with-nextjs-tailwindcss/</link>
                <guid isPermaLink="false">66d460149f2bec37e2da063d</guid>
                
                    <category>
                        <![CDATA[ Job Hunting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 18 Aug 2021 18:02:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/Blue-and-White-Modern-Corporate-Travel-YouTube-Thumbnail--3-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Manu Arora</p>
<p>If you're a web developer, it's important for you to have a personal portfolio website – especially when you're applying for jobs. </p>
<p>If you have a nice online portfolio site, you'll have a better chance of getting attention from recruiters than if you don't.</p>
<p>Instead of sending your CV and asking a recruiter to have a look, a portfolio website can help you stand out by directly showcasing your skills, projects, education, and your personal brand.</p>
<p>You can use your portfolio to showcase your projects, your education, share re-usable code snippets with the world, provide helpful resources, and write your own blogs. There are countless things that you can do with your own website.</p>
<p>I built a template that you can use to create, edit, and deploy your own portfolio website for free on the internet in no time. And if you want to build your own version from scratch, I'll show you how in this article.</p>
<p>So let's dive in – I'll show you what technology I used, how I structured the portfolio, and I'll break it down by section so you can see how each part works. </p>
<h2 id="heading-tech-stack">Tech Stack</h2>
<p>Let's talk about the tech stack I used to build this template:</p>
<ul>
<li><strong>Next.js</strong> - a React framework used for building blazing-fast websites along with server-side rendering, which makes it easier for people to find your site on the internet.</li>
<li><strong>tailwindcss</strong> - a CSS framework to that lets you quickly prototype and style your web applications.</li>
<li><strong>Rough Notation</strong> - a styling library used in the Hero section, great for highlighting important text on your webpage.</li>
</ul>
<p>Why <code>Next.js</code>? Because it is a React framework with <code>Server-Side Rendering</code>, which is good for SEO (Good for us if we get found on Google, right?).</p>
<p>Also, Next.js helps us build blazing-fast websites along with benefits such as Image optimization.</p>
<p>Why <code>tailwindcss</code>? Because TailwindCSS is a framework which reduces a lot of styling efforts. It has low level CSS classes that you can directly embed into the HTML code.</p>
<p>Not only that, it comes with amazing support for responsiveness. For example <code>&lt;div className="text-sm md:text-xl"&gt;&lt;/div&gt;</code> signifies that text will be <code>small</code> on smaller screens and <code>xl</code> on medium to large screens.</p>
<p>Finally, we are going to deploy the application on <strong>Vercel</strong>. Vercel gives us an easy way to deploy our application with CI/CD. The code is pushed to a remote GitHub repository and with every push, it's deployed.</p>
<h2 id="heading-portfolio-site-features">Portfolio Site Features</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/websitegif-1.gif" alt="websitegif-1" width="600" height="400" loading="lazy"></p>
<p>The website includes what I consider to be the absolute minimum you should have in your portfolio website, along with some extra features that are cool to have.</p>
<ul>
<li><code>Dark Mode</code> - Comes with dark mode support. Toggle the mode button to switch between dark and light mode.</li>
<li><code>Built with Next.js</code> - The website is built with Next.js which comes with awesome features out of the box like Image Optimization and SEO support.</li>
<li><code>Customizable styling</code> - Using TailwindCSS, I built this website in such a way that you can customize primary colors and change the look of your website according to your needs.</li>
<li><code>Custom Meta Component</code> - Every page is wrapped with a Meta Component tag which you can use to provide meta information for every separate page that you create.</li>
<li><code>Responsive Design</code> - Pages look beautiful on all the devices - desktop, tablet, and mobile.</li>
</ul>
<h2 id="heading-portfolio-pages-well-build">Portfolio Pages We'll Build</h2>
<p>We are going to include all the necessary pages you should have in your portfolio website, such as:</p>
<ul>
<li><code>Homepage</code> - A landing page for the visitor. This is what they'll see when they land on your website.</li>
<li><code>About</code> - A brief intro that includes what you do, your technical skills, and your social links.</li>
<li><code>Experience</code> - A history of your work, your personal projects that you've undertaken, and your relevant projects.</li>
<li><code>Projects</code> - A grid of all the projects you have built.</li>
<li><code>Contact</code> - A form where the recruiter / end-user can reach out to you.</li>
</ul>
<h2 id="heading-components-and-layouts">Components and Layouts</h2>
<p>The entire website is divided into components – small chunks of reusable code that you can use anywhere on the webpage. The folder structure is quite simple and self explanatory:</p>
<ul>
<li><code>components</code> is where all the components live, like the hero section, navbar, and layouts.</li>
<li><code>public</code> is where all your static assets go, like images, fonts, and/or any external script for generating dynamic sitemaps.</li>
<li><code>styles</code> is where your global styling lives. We will integrate Tailwind's base library here.</li>
<li><code>pages</code> is where all your routes live, and it's one of the best features of Next.js. Just create a new file in the <code>pages</code> folder and it'll serve as a new route.</li>
</ul>
<h3 id="heading-container-block">Container Block</h3>
<p><code>&lt;ContainerBlock /&gt;</code> is the parent of all the components. It provides a way for the user to have custom meta tags for every page. I designed the layout so that it accepts props as <code>children</code> and provides a <code>Navbar</code>, <code>&lt;meta&gt;</code> tags, and <code>Footer</code> for every page.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Head <span class="hljs-keyword">from</span> <span class="hljs-string">"next/head"</span>;
<span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/router"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./Navbar"</span>;
<span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">"./Footer"</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">ContainerBlock</span>(<span class="hljs-params">{ children, ...customMeta }</span>) </span>{
  <span class="hljs-keyword">const</span> router = useRouter();

  <span class="hljs-keyword">const</span> meta = {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Manu Arora - Developer, Writer, Creator and YouTuber"</span>,
    <span class="hljs-attr">description</span>: <span class="hljs-string">`I've been developing websites for 5 years straight. Get in touch with me to know more.`</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"/avatar.png"</span>,
    <span class="hljs-attr">type</span>: <span class="hljs-string">"website"</span>,
    ...customMeta,
  };
  <span class="hljs-keyword">return</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">Head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>{meta.title}<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"robots"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"follow, index"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.description}</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span>
          <span class="hljs-attr">property</span>=<span class="hljs-string">"og:url"</span>
          <span class="hljs-attr">content</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">yourwebsite.com</span>${<span class="hljs-attr">router.asPath</span>}`}
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span>
          <span class="hljs-attr">rel</span>=<span class="hljs-string">"canonical"</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">yourwebsite.com</span>${<span class="hljs-attr">router.asPath</span>}`}
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:type"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.type}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:site_name"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Manu Arora"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.description}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:title"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.title}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"og:image"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.image}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:card"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"summary_large_image"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:site"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"@mannupaaji"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:title"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.title}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.description}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"twitter:image"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.image}</span> /&gt;</span>
        {meta.date &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"article:published_time"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">{meta.date}</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">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"dark:bg-gray-800 w-full"</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">div</span>&gt;</span>{children}<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 class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>After creating <code>ContainerBlock.js</code>, you can simply wrap your page component in a <code>ContainerBlock</code> tag, providing meta tags for <code>title</code>, <code>description</code>, and <code>image</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Head <span class="hljs-keyword">from</span> <span class="hljs-string">"next/head"</span>;
<span class="hljs-keyword">import</span> styles <span class="hljs-keyword">from</span> <span class="hljs-string">"../styles/Home.module.css"</span>;
<span class="hljs-keyword">import</span> ContainerBlock <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/ContainerBlock"</span>;
<span class="hljs-keyword">import</span> FavouriteProjects <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/FavouriteProjects"</span>;
<span class="hljs-keyword">import</span> LatestCode <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/LatestCode"</span>;
<span class="hljs-keyword">import</span> Hero <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Hero"</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">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ContainerBlock</span>
      <span class="hljs-attr">title</span>=<span class="hljs-string">"Manu Arora - Developer, Writer, Creator"</span>
      <span class="hljs-attr">description</span>=<span class="hljs-string">"Building a template with Next.js and Tailwindcss - for FreeCodeCamp users."</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Hero</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">FavouriteProjects</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">LatestCode</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ContainerBlock</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-enable-dark-mode">How to Enable Dark Mode</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-18-at-9.09.35-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Dark mode support is provided by a <code>npm</code> package called <code>next-themes</code>. The aim is to wrap the parent container with a provider <code>ThemeProvider</code> through which the <code>theme</code> is available to the children at all times.</p>
<p><strong>_app.js</strong></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> <span class="hljs-string">"../styles/globals.css"</span>;
<span class="hljs-keyword">import</span> { ThemeProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"next-themes"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyApp</span>(<span class="hljs-params">{ Component, pageProps }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ThemeProvider</span> <span class="hljs-attr">defaultTheme</span>=<span class="hljs-string">"light"</span> <span class="hljs-attr">attribute</span>=<span class="hljs-string">"class"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ThemeProvider</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyApp;
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Copy-of-Untitled--6--1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To toggle the theme between light mode and dark mode, we need a button. You can reuse this button anywhere in the application, but we are going to integrate it in the <code>Navbar</code> so it's available for the end user at all times.</p>
<p><strong>Navbar.js</strong></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React, { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>;
<span class="hljs-keyword">import</span> { useTheme } <span class="hljs-keyword">from</span> <span class="hljs-string">"next-themes"</span>;
<span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/router"</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">Navbar</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> router = useRouter();
  <span class="hljs-built_in">console</span>.log(router.asPath);
  <span class="hljs-keyword">const</span> { theme, setTheme } = useTheme();
  <span class="hljs-keyword">const</span> [mounted, setMounted] = useState(<span class="hljs-literal">false</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    setMounted(<span class="hljs-literal">true</span>);
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-6xl  mx-auto px-4 py-10 md:py-20"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex  md:flex-row justify-between items-center"</span>&gt;</span>
        {/* Logo / Home / Text */}
         // Rest of the code
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Toggle Dark Mode"</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 h-10 p-3 rounded focus:outline-none"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setTheme(theme === "dark" ? "light" : "dark")}
          &gt;
            {mounted &amp;&amp; (
              <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">viewBox</span>=<span class="hljs-string">"0 0 24 24"</span>
                <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span>
                <span class="hljs-attr">stroke</span>=<span class="hljs-string">"currentColor"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"w-4 h-4 text-yellow-500 dark:text-yellow-500"</span>
              &gt;</span>
                {theme === "dark" ? (
                  <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                    <span class="hljs-attr">strokeLinecap</span>=<span class="hljs-string">"round"</span>
                    <span class="hljs-attr">strokeLinejoin</span>=<span class="hljs-string">"round"</span>
                    <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">{2}</span>
                    <span class="hljs-attr">d</span>=<span class="hljs-string">"M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"</span>
                  /&gt;</span>
                ) : (
                  <span class="hljs-tag">&lt;<span class="hljs-name">path</span>
                    <span class="hljs-attr">strokeLinecap</span>=<span class="hljs-string">"round"</span>
                    <span class="hljs-attr">strokeLinejoin</span>=<span class="hljs-string">"round"</span>
                    <span class="hljs-attr">strokeWidth</span>=<span class="hljs-string">{2}</span>
                    <span class="hljs-attr">d</span>=<span class="hljs-string">"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"</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">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
     <span class="hljs-comment">//Rest of the code</span>
  );
}
</code></pre>
<p>Once the button is clicked, the theme changes. Pretty cool, right? 😍</p>
<h2 id="heading-how-to-build-the-hero-section">How to Build the Hero Section</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/hero.png" alt="hero" width="600" height="400" loading="lazy"></p>
<p>The hero section lets you grab people's attention. If you do it right, it can help you land your first job.</p>
<p>I've used <code>react-rough-notation</code>, a library that dynamically highlights text with different colors and delays.</p>
<p>The good thing about this effect is that the end-user immediately pays attention to the text that's highlighted. You can put your best foot forward here and tell them WHO you are and WHAT you do.</p>
<p>The code for <code>rough-notation</code> is simple: we wrap the text to be highlighted in <code>&lt;RoughNotationGroup&gt;</code> and <code>&lt;RoughNotation&gt;</code> tags with additional parameters such as colors and delays.</p>
<p>Here, I'll create a custom component called <code>RainbowHighlight</code> which takes in a color and highlights the text enclosed which can be used everywhere.</p>
<p><strong>RainbowHighlight.js</strong></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { RoughNotation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-rough-notation"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> RainbowHighlight = <span class="hljs-function">(<span class="hljs-params">{ color, children }</span>) =&gt;</span> {
  <span class="hljs-comment">// Change the animation duration depending on length of text we're animating (speed = distance / time)</span>
  <span class="hljs-keyword">const</span> animationDuration = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-number">30</span> * children.length);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">RoughNotation</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">"highlight"</span>
      <span class="hljs-attr">multiline</span>=<span class="hljs-string">{true}</span>
      <span class="hljs-attr">padding</span>=<span class="hljs-string">{[0,</span> <span class="hljs-attr">2</span>]}
      <span class="hljs-attr">iterations</span>=<span class="hljs-string">{1}</span>
      <span class="hljs-attr">animationDuration</span>=<span class="hljs-string">{animationDuration}</span>
      <span class="hljs-attr">color</span>=<span class="hljs-string">{color}</span>
    &gt;</span>
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">RoughNotation</span>&gt;</span></span>
  );
};
</code></pre>
<p><strong>Hero.js</strong></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { RoughNotation, RoughNotationGroup } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-rough-notation"</span>;
<span class="hljs-keyword">import</span> { RainbowHighlight } <span class="hljs-keyword">from</span> <span class="hljs-string">"./RainbowHighlight"</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">Hero</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> colors = [<span class="hljs-string">"#F59E0B"</span>, <span class="hljs-string">"#84CC16"</span>, <span class="hljs-string">"#10B981"</span>, <span class="hljs-string">"#3B82F6"</span>];
  <span class="hljs-keyword">return</span> (
    &lt;div className="flex flex-row justify-center items-start overflow-hidden"&gt;
      {/* Text container */}

      &lt;div className="w-full md:w-1/2 mx-auto text-center md:text-left lg:p-20"&gt;
        &lt;RoughNotationGroup show={true}&gt;
          &lt;RainbowHighlight color={colors[0]}&gt;
            &lt;h1 className="text-4xl md:text-8xl font-bold text-gray-700 dark:text-gray-200 my-2"&gt;
              Developer.
            &lt;/h1&gt;
          &lt;/RainbowHighlight&gt;
       &lt;RoughNotationGroup&gt;
     &lt;/div&gt;
     ....
     ....
     ....
   );
</code></pre>
<h2 id="heading-how-to-fetch-the-latest-repositories-from-github">How to Fetch the Latest Repositories From GitHub</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screenshot-2021-08-18-at-9.09.35-AM-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Fetching repositories with the <a target="_blank" href="https://docs.github.com/en/rest">GitHub API</a> is quite easy.</p>
<p>The GitHub API comes with a provision to fetch the repositories with descending <code>updated_time</code> field, so that we get the latest repositories.</p>
<pre><code class="lang-js"> <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(
      <span class="hljs-string">`https://api.github.com/search/repositories?q=user:<span class="hljs-subst">${username}</span>+sort:author-date-asc`</span>
    );
</code></pre>
<p>Once we fetch the latest repository, we <code>splice</code> the array to only take into account the most recent 6 repositories.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> repos = res.data.items;
    <span class="hljs-keyword">let</span> latestSixRepos = repos.splice(<span class="hljs-number">0</span>, <span class="hljs-number">6</span>);
    <span class="hljs-keyword">return</span> latestSixRepos;
</code></pre>
<p>So the entire function looks like this:</p>
<p><strong>getLatestRepos.js</strong></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-keyword">const</span> getLatestRepos = <span class="hljs-keyword">async</span> (data) =&gt; {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"data"</span>, data);
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> username = data.githubUsername;

    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.get(
      <span class="hljs-string">`https://api.github.com/search/repositories?q=user:<span class="hljs-subst">${username}</span>+sort:author-date-asc`</span>
    );

    <span class="hljs-keyword">let</span> repos = res.data.items;
    <span class="hljs-keyword">let</span> latestSixRepos = repos.splice(<span class="hljs-number">0</span>, <span class="hljs-number">6</span>);
    <span class="hljs-keyword">return</span> latestSixRepos;
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.log(err);
  }
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> getLatestRepos;
</code></pre>
<p>Once the data is fetched from the function, we can then use it inside of our React Component <code>&lt;GetReposCard /&gt;</code> and pass the parameters accordingly.</p>
<pre><code class="lang-jsx">&lt;div className=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto px-10 lg:-mt-10 gap-y-20"</span>&gt;
        {<span class="hljs-comment">/* Single github Repo */</span>}

        {repos &amp;&amp;
          repos.map(<span class="hljs-function">(<span class="hljs-params">latestRepo, idx</span>) =&gt;</span> (
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">GithubRepoCard</span> <span class="hljs-attr">latestRepo</span>=<span class="hljs-string">{latestRepo}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">"idx"</span> /&gt;</span></span>
          ))}
      &lt;/div&gt;
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> GithubRepoCard = <span class="hljs-function">(<span class="hljs-params">{ latestRepo }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"github-repo"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold text-xl dark:text-gray-200 text-gray-700"</span>&gt;</span>
        {latestRepo.name}
      <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base font-normal my-4 text-gray-500"</span>&gt;</span>
        {latestRepo.description}
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
        <span class="hljs-attr">href</span>=<span class="hljs-string">{latestRepo.clone_url}</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold group flex flex-row space-x-2 w-full items-center"</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>View Repository <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">className</span>=<span class="hljs-string">"transform  group-hover:translate-x-2 transition duration-300"</span>&gt;</span>
          <span class="hljs-symbol">&amp;rarr;</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>There is a small problem here – the GitHub API provides only a limited number of calls per IP address.</p>
<p>To resolve this issue, one can create a GitHub application and generate <code>Auth Tokens</code>, which we can embed with the GitHub API request and you'll get more requests per IP address. You can <a target="_blank" href="https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting">read the documentation here</a> for more info about it.</p>
<h2 id="heading-how-to-include-projects-in-your-portfolio">How to Include Projects in Your Portfolio</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/projects.png" alt="projects" width="600" height="400" loading="lazy"></p>
<p>I've kept the projects section as simple as possible with a huge image area, because the recruiter / end-user is most interested in seeing what you've done. If it looks good, you already have an edge.</p>
<p>I've divided the page into Tailwind <code>grids</code> of two columns, which breaks on mobile screens into 1 column.</p>
<p>The image container contains a header text that displays the project's name and a number at the bottom.</p>
<p>The hover animations on the images are subtle. The image scales slowly to gather the user's attention. On click, it takes the user to the live website / GitHub repository of the project.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">Projects</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white dark:bg-gray-800"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-6xl mx-auto h-48 bg-white dark:bg-gray-800"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">" text-5xl md:text-9xl font-bold py-20 text-center md:text-left"</span>&gt;</span>
          Projects
        <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>
      {/* Grid starts here */}
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-[#F1F1F1] dark:bg-gray-900"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8 py-20 pb-40"</span>&gt;</span>
          {/* Single card */}
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
            <span class="hljs-attr">href</span>=<span class="hljs-string">"https://tailwindmasterkit.com"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full block shadow-2xl"</span>
          &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative overflow-hidden"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                <span class="hljs-attr">src</span>=<span class="hljs-string">"/tmk.jpg"</span>
                <span class="hljs-attr">alt</span>=<span class="hljs-string">"portfolio"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"transform hover:scale-125 transition duration-2000 ease-out"</span>
              /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute top-10 left-10 text-gray-50 font-bold text-xl bg-red-500 rounded-md px-2"</span>&gt;</span>
                Tailwind Master Kit
              <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute bottom-10 left-10 text-gray-50 font-bold text-xl"</span>&gt;</span>
                01
              <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">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">section</span>&gt;</span></span>
    ...
    ...
    ...
  );
</code></pre>
<h2 id="heading-how-to-build-the-contact-page">How to Build the Contact Page</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/contact.png" alt="contact" width="600" height="400" loading="lazy"></p>
<p>I took the contact section directly from the <a target="_blank" href="http://tailwindmasterkit.com">Tailwind Master Kit</a>, which is a components and templates marketplace for Tailwind web app projects. I didn't want spend more time styling a contact form myself and used some help.</p>
<p>The component is absolutely free and you can embed it into Tailwind related websites easily.</p>
<p><strong>Contact.js</strong></p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;section&gt;
      &lt;div className="max-w-6xl mx-auto h-48 bg-white dark:bg-gray-800 antialiased"&gt;
        &lt;h1 className=" text-5xl md:text-9xl font-bold py-20 text-center md:text-left"&gt;
          Contact
        &lt;/h1&gt;
      &lt;/div&gt;
      &lt;div className="relative z-10 rounded-md shadow-md bg-[#02044A] p-4 md:p-10 lg:p-20 max-w-6xl mx-auto mb-20 -mt-4"&gt;
        &lt;div className="grid grid-cols-1 md:grid-cols-2 gap-4"&gt;
          &lt;div className="md:ml-4"&gt;
            &lt;header className=""&gt;
              &lt;h1 className="text-gray-50 font-semibold text-2xl"&gt;
                Get in touch, let's talk.
              &lt;/h1&gt;
              &lt;p className="font-light text-base text-gray-200 mt-2"&gt;
                Fill in the details and I'll get back to you as soon as I can.
              &lt;/p&gt;
            &lt;/header&gt;
            &lt;div className="icons-container inline-flex flex-col my-20"&gt;
              &lt;div className="flex flex-row items-center space-x-6 rounded-md border border-[#02044A] hover:border hover:border-blue-500 p-4"&gt;

                &lt;p className="text-gray-50 font-light text-sm"&gt;
                  +91 9987384723
                &lt;/p&gt;
              &lt;/div&gt;
              &lt;div className="flex flex-row items-center space-x-6 rounded-md border border-[#02044A] hover:border hover:border-blue-500 p-4"&gt;
                ....
                ....
                &lt;p className="text-gray-50 font-light text-sm"&gt;
                  contact@yourwebsite.com
                &lt;/p&gt;
              &lt;/div&gt;
              &lt;div className="flex flex-row items-center space-x-6 rounded-md border border-[#02044A] hover:border hover:border-blue-500 p-4"&gt;
                ....
                ....
          &lt;form className="form rounded-lg bg-white p-4 flex flex-col"&gt;
            &lt;label htmlFor="name" className="text-sm text-gray-600 mx-4"&gt;
              {" "}
              Your Name
            &lt;/label&gt;
            &lt;input
              type="text"
              className="font-light rounded-md border focus:outline-none py-2 mt-2 px-1 mx-4 focus:ring-2 focus:border-none ring-blue-500"
              name="name"
            /&gt;
            &lt;label htmlFor="email" className="text-sm text-gray-600 mx-4 mt-4"&gt;
              Email
            &lt;/label&gt;
            &lt;input
              type="text"
              className="font-light rounded-md border focus:outline-none py-2 mt-2 px-1 mx-4 focus:ring-2 focus:border-none ring-blue-500"
              name="email"
            /&gt;
            &lt;button
              type="submit"
              className="bg-blue-500 rounded-md w-1/2 mx-4 mt-8 py-2 text-gray-50 text-xs font-bold"
            &gt;
              Send Message
            &lt;/button&gt;
          &lt;/form&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/section&gt;
  );
}
</code></pre>
<h2 id="heading-how-to-deploy-the-portfolio">How to Deploy the Portfolio</h2>
<p>Deploying the application is fairly simple and just takes 8 simple steps.</p>
<ol>
<li><p>Clone the repository</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/manuarora700/simple-developer-portfolio-website
</code></pre>
</li>
<li><p>Install dependencies</p>
</li>
</ol>
<pre><code class="lang-bash">npm install
</code></pre>
<ol start="3">
<li>Start the local development server</li>
</ol>
<pre><code class="lang-bash">npm run dev
</code></pre>
<ol start="4">
<li><p>Make modifications to the website. You should include all your projects, education, social links, and Meta information.</p>
</li>
<li><p>Push the code to your remote repository</p>
</li>
</ol>
<pre><code class="lang-bash">git add *
git commit -m <span class="hljs-string">"add changes to the cloned repo"</span>
git push
</code></pre>
<ol start="6">
<li><p>Create a Vercel account (or login to your Vercel account) </p>
</li>
<li><p>Add the newly created GitHub repository with the changes pushed and Vercel will automatically deploy it for you on a link.</p>
</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/vercel.png" alt="vercel" width="600" height="400" loading="lazy"></p>
<ol start="8">
<li>Once the site is live, share the test link with your friends or add it in your résumé. You can also go ahead and connect a custom domain to make it more professional.</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This portfolio site will give a recruiter or visitor everything they're looking for if they want to learn more about you and your work. The goal of your portfolio should be to showcase your skills in the best way possible.</p>
<p>Also, we've built the site with <code>Next.js</code>, which shows that you're comfortable with React and its frameworks. (Recruiters are looking for you! 😍)</p>
<p>The website uses <code>tailwindcss</code> for styling, which shows that you can work with a CSS framework and cut down on styling time.</p>
<p>The components are granular and each serves its own purpose. The folder structure is simple and easy to understand.</p>
<p>You can customize the website in any way you like - I've open sourced it and the code is linked to the GitHub repo below.</p>
<p>My <a target="_blank" href="https://manuarora.in">personal website</a> has helped me land interviews at big tech companies and it is one of the main reasons I've been able to get a job (along with practicing on freeCodeCamp and learning how to code).</p>
<p>I really enjoyed building this website. If you liked it, leave a star on the GitHub repo and help spread the word. ⭐️</p>
<h4 id="heading-source-code-and-live-demo">Source Code and Live Demo</h4>
<p><a target="_blank" href="https://github.com/manuarora700/simple-developer-portfolio-website">Source Code</a><br><a target="_blank" href="https://simple-developer-portfolio-website.vercel.app/">Live Demo</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 13 Freelance Developer Portfolios to Inspire You ]]>
                </title>
                <description>
                    <![CDATA[ By Kyle Prinsloo So you're thinking of building your portfolio site as a freelance web developer.   Or maybe you're looking to create an online space where potential employers can get a feel for who you are and what you've done in the past.   Either ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/13-awesome-freelance-developer-portfolios/</link>
                <guid isPermaLink="false">66d4601337bd2215d1e24592</guid>
                
                    <category>
                        <![CDATA[ Freelancing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 21 Jul 2021 15:24:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/07/freelance-portfolio-inspiration.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Kyle Prinsloo</p>
<p>So you're thinking of building your <a target="_blank" href="https://studywebdevelopment.com/portfolio-tips-freelance-developer.html">portfolio site as a freelance web developer</a>.  </p>
<p>Or maybe you're looking to create an online space where potential employers can get a feel for who you are and what you've done in the past.  </p>
<p>Either way, your portfolio site is the perfect place to show your past work and provide a convenient way for potential clients or employers to contact you. Even if you don’t have past work, a great portfolio can help you <a target="_blank" href="https://studywebdevelopment.com/how-to-get-clients-freelance-developer.html">get that first client</a>.  </p>
<p>We're going to take a look at the standard features your portfolio site should include to bring across a clear message. After we've gone over the basics, we're going to delve into some inspirational, beautiful, and a few simply amazing portfolios of freelancers out there.</p>
<h2 id="heading-include-an-about-me-page-on-your-portfolio-site">Include an “About Me” Page on Your Portfolio Site</h2>
<p>A portfolio website, and business-website landing pages in general, are the modern-day business card.  </p>
<p>Bringing across the information that will make potential clients and employers stick around to find out more about you, in the hope of working with you, is crucial.  </p>
<p>That being said, it's considered good practice to include an "About" section on your site. In this section, give a well-revised version of the most important (relevant) information about yourself, your experience, and maybe your approach as well.  </p>
<p>Of course, there can be variation, but the most important thing is to try to identify your unique selling points and validators (past work and/or skills) and point them out as clearly as possible.  </p>
<p>We'll see in some of the inspirational portfolios below that you can get creative with this while remaining clear in the main message.</p>
<h2 id="heading-show-proof-of-past-work-in-your-portfolio">Show Proof of Past Work in Your Portfolio</h2>
<p>Your portfolio site serves one main purpose: to show credibility and build trust in the minds of your visitors.  </p>
<p>There is one sure way of doing so and that is through the clear display of previous projects or work.  </p>
<p>This becomes even more relevant and important if your ideal visitor is not technically-savvy and just wants to see proof and quality of previous work.  </p>
<p>This is a good place to show a range of work illustrating what you're capable of and that you're able to adapt to the needs of different project scopes. </p>
<p>Keep in mind that clarity is important here. Showing a screenshot of a sample project is not enough, especially if you do not have a huge amount of experience.  </p>
<p>What you could do is describe the process for each project: use this space as a way to communicate your thinking and explain certain design or technical choices you made.  </p>
<p>If done well, a potential client or employer can understand how and why you do the things you do which will give them a much more informed impression of you and the way you work.</p>
<h2 id="heading-add-testimonials-to-your-portfolio-site">Add Testimonials to Your Portfolio Site</h2>
<p>Adding testimonials, if you have them, is probably the best way to provide social proof about your work. These can be quotes from past clients and colleagues. If you've worked with brands, include their names and logos.  </p>
<p>Demonstrating how past clients and employers feel about their experience with you can settle the nerves of an anxious visitor and solidify the trustworthy impression you're trying to make with your portfolio site.</p>
<h2 id="heading-include-your-contact-information">Include Your Contact Information</h2>
<p>This may seem like an obvious one, but many portfolio sites make it more difficult than it should be to find the most important contact info.  </p>
<p>Leave your email address, social media links, or a contact form in an easy-to-find spot on your site.  </p>
<p>You need to make the whole experience of visitors interacting with your site as easy as possible, including the process to follow when they've decided they want to get in touch.</p>
<h2 id="heading-10-awesome-freelancing-portfolios-to-inspire-you">10 Awesome Freelancing Portfolios to Inspire You</h2>
<h3 id="heading-stef-ivanov">Stef Ivanov</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/stef_ivanov.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Freelance UX and UI designer, <a target="_blank" href="https://stefivanov.com/">Stef Ivanov</a>, has worked for more than 100 companies throughout a 13-year career, and there's a great selection of his work on show in his portfolio.  </p>
<p>However, that's not why I included him on this list. It wouldn't make sense to include only the most experienced freelancers here. Where's the inspiration in that? Rather, he's been included for the attention-grabbing landing page.  </p>
<p>It features a simple block of introductory text which is pretty standard for sites in general.  </p>
<p>How the landing page shines is with a beautifully cross-hatched pen-and-ink portrait of Stef with an animated halo of bees buzzing around his head.  </p>
<p>Not only does he nail the basics we mentioned above but he's added a little extra touch that shows off a bit of his creative personality and piques visitors' interest.</p>
<h3 id="heading-erika-senft-miller">Erika Senft Miller</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/erika.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When you visit <a target="_blank" href="https://www.erikasenftmiller.com/">Erika's site</a>, you're met with a very minimalist loading screen.  </p>
<p>Something most unusual happens once the page is fully-loaded. You'd have to visit Erika's site to get a real feel for it but let's give a wordy description a go, shall we?  </p>
<p>Her website is presented as a lined page that you can move around by dragging with the mouse. As you move the on-screen pointer, you'll notice that the lines making up the page move and bend around the pointer.  </p>
<p>When you come to a portfolio item – represented as a slowly morphing, multi-layered 2D orb which itself expands as you mouse over it – the lines around the item also warp.  </p>
<p>Once you click on one of these morphing, moving orbs, the project info expands outwards. And the intriguing concept continues. Instead of scrolling down or across, you sort of follows= a meandering path as you see fit.  </p>
<p>The way this is presented is highly creative but where it shines is in its ability to draw a visitor in with its unique interaction style.  </p>
<p>Erika has presented a fascinating way of exploring her work.</p>
<h3 id="heading-jessica-haines">Jessica Haines</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-21-at-10.02.33.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Jessica has a great <a target="_blank" href="https://jessicahainesdesign.com/">portfolio website</a>!</p>
<p>It's designed with conversions in mind, it is well thought out, the colors are great, it's niched down - it's just superb.</p>
<p>The cool thing about her website that stands out to me is that she clearly showcases her expertise.</p>
<p>Take notes for your portfolio site. I always use her example when chatting to freelance students.</p>
<h3 id="heading-rey-van-den-berg">Rey van den Berg</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/rey.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Clarity is key in <a target="_blank" href="https://www.fromthemountain.co.za/">Rey's portfolio</a>.  </p>
<p>Rey seems to have chosen a less creative approach in favour of really nailing the fundamentals of a good-quality portfolio site.  </p>
<p>On the landing page, you see some intro text and an illustration of a developer sitting at a laptop. What this landing page does so well is provide a clear description of exactly what he does and provides the crucial information we were talking about earlier.  </p>
<p>You do not need to scroll any further if you're looking to contact him via a form or social media and he directs visitors to a section where they can see an elaboration on the services he provides.  </p>
<p>Rey has clearly designed his website copy to build trust with the site visitors in a more personal way. There is little technical jargon which is inviting to less tech-savvy visitors while he still includes his tech skills for would-be employers  </p>
<p>This site nails the fundamentals by being clear and providing proof-of-work. Rey goes a step further by being personal yet professional in drawing potential clients and employers into his communication approach with "Promises", "Ways You'll Benefit", and "FAQs".</p>
<h3 id="heading-adam-brooks">Adam Brooks</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/adam.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A clean design and generous use of whitespace give <a target="_blank" href="http://www.adambbrooks.com/">Adam's site</a> a modern feel that will appeal to most.  </p>
<p>The landing page includes a smiling picture of Adam which immediately adds some personality to the site. He is clear with what he does and includes some credibility right off the bat by stating that he has worked with brands all over the world.  </p>
<p>After a gentle introduction to what he does, a scroll down the page reveals a two-sentence pitch of his services with contact links. This might be all that some visitors are looking for, especially if they have been referred from elsewhere about Adam's services. He has done well to include a way of contacting him so early in his site.  </p>
<p>For those that don't quite grasp that two-sentence pitch, he expands on that a little by explaining his services a bit more – especially useful for more lay visitors.  </p>
<p>Following that section, we're met by some sample work as device mockups presented in a manner that is easy-breathing, thanks to the generous whitespace, and minimal which gives off a professional feel.  </p>
<p>This site is fantastic at coming across as credible, professional, and, thanks to the sign off at the bottom of the site, approachable.</p>
<h3 id="heading-geoff-house">Geoff House</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/geoff.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><a target="_blank" href="https://geoff.house/">Geoff</a> wastes absolutely no time in reassuring would-be clients and employers that he can get a job done – he shows past work almost immediately!  </p>
<p>You're met with a clear one-liner of what he does and where he is currently working.  </p>
<p>Scrolling down reveals several projects that Geoff has worked on in the past.  </p>
<p>Clicking on one of these projects is where this portfolio site excels. You'll find a case study of the project where he provides a brief description of it and expands on his role and team within the project.  </p>
<p>This manner of presenting past work does wonders in the minds of visitors.<br>It provides information about the project's rationale, a deeper dive into the project's purpose, and a chance to get an idea for his communication style.</p>
<h3 id="heading-jennie-lakenan">Jennie Lakenan</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-21-at-10.06.53.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I love <a target="_blank" href="https://jennielakenan.com/">Jennie's website</a>, because it's simple but effective.</p>
<p>She has a great headline, great images, testimonials, clear call-to-actions, and she's <a target="_blank" href="https://studywebdevelopment.com/niche.html">niched down</a>.</p>
<p>Way too many freelancers go overboard with animations and fluff - Jennie's portfolio is a good example of keeping it simple and not losing sight of the core focus: getting more clients.</p>
<h3 id="heading-adhemas-batista">Adhemas Batista</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/adhemas.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><a target="_blank" href="http://www.adhemas.com/">Adhemas</a> has worked on a huge amount of very impression projects but that is not what I like the most about his portfolio site.  </p>
<p>He has managed to include all of the fundamentals of a great portfolio on a single view with a ton of whitespace.  </p>
<p>His design is minimal but very clever. At the top, you can find social media contact info and an email address. In the top-center, you can read about what Adhemas does, his current work and location, and his availability.  </p>
<p>A little lower on the landing page, his display of previous work begins with large, multi-coloured images that have an animated hover effect.  </p>
<p>Clicking on one of these past projects takes you to a long case study page which is media-heavy with a ton of whitespace. He has detailed these project "case studies" extensively allowing you to get a real feel for the project and its purpose.  </p>
<p>Adhemas' portfolio site is a fantastic example of how lots of whitespace can be used while still packing a ton of useful information on one view.</p>
<h3 id="heading-flamy">Flamy</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/flamy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A full-screen loading splash screen gives way to <a target="_blank" href="http://fla.my/">Flamy's</a> unconventional yet eye-catching portfolio design.  </p>
<p>Clean minimalism is not the name of the game here with a strong cartoon theme being preferred. Flamy's site goes to show that you can be quirky and fun without forgoing a professional appeal.  </p>
<p>When you reach the landing page, you'll see what can only be described as a man with a flame for a head and cards describing the services Flamy offers.  </p>
<p>The rest of the site sees clever parallax effects being used to add an interactive element to the site.  </p>
<p>This site is undoubtedly fun, although not for everyone. This being said, they do get the fundamentals right and all the quirkiness is executed in a manner that doesn't sacrifice UX or clarity in the offering.  </p>
<p>If nothing else, this site will leave a lasting impression in visitors' minds, and sometimes that's all you need to do to get visitors returning to your site and contacting you.</p>
<h3 id="heading-ivo-mynttinen">Ivo Mynttinen</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/ivo.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><a target="_blank" href="https://ivomynttinen.com/">Ivo's site</a> is one of almost-Scandinavian minimalism.  </p>
<p>It's clean, uses a lot of whitespace with large, clear headings, and gets all the other design fundamentals right such as beautiful visual hierarchy and well-matched text opacity.  </p>
<p>His landing page includes a clear path to see past projects, which include lengthy project descriptions, and an intuitive contact button.  </p>
<p>Smooth page transitions complement a flawless portfolio site.  </p>
<p>Ivo has thought of everything potential clients and employers may be interested in seeing or finding out.  </p>
<p>If you want to see what a perfect portfolio site looks like, without unnecessary bells and whistles, this is it.</p>
<h3 id="heading-sara-michelazzo">Sara Michelazzo</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-21-at-10.14.36.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you're into minimalist websites, <a target="_blank" href="http://www.saramichelazzo.com/ux-designer-1">Sara's portfolio</a> is a good example.</p>
<p>She answers what she does and who she is within a quick glance of viewing her website.</p>
<p>My only critique is there's no contact form, but other than that, it's a clean minimalist example to be inspired from.</p>
<h3 id="heading-jordan-flaig">Jordan Flaig</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/jordan.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here is a minimal description one of the <a target="_blank" href="https://www.jordanflaig.com/">most minimal site's</a> you'll see.  </p>
<p>It answers all the important site visitor questions except for one missing, crucial question - "How do I contact you, Jordan?"  </p>
<p>Add a contact section to your unique spin off of this site and you'll have the perfect minimalist modern-day business card on your hands.</p>
<h3 id="heading-ruben-kuipers">Ruben Kuipers</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/ruben.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The style of <a target="_blank" href="https://rubenkuipers.design/">Ruben's portfolio site</a> is one that many developers will probably recognize. It even starts with a "Hi there, I'm Ruben"!  </p>
<p>As a developer, you would be able to appreciate the little extras that make us happy like a toggle switch for dark-mode (and corresponding dark-mode profile image), subtle animations, and a section on "Skills &amp; Tools".  </p>
<p>What you may not notice is how these extras complement an already brilliant site.<br>As you land on Ruben's page, you immediately know what he looks like (which increases familiarity), know what he does, know that he is credible (a list of past clients), and have a way to contact him.  </p>
<p>He has cleverly included testimonials interlaced with project cards which, together, make a convincing case for "Let's hire this guy!"  </p>
<p>Ultimately, this site is a great example of a single page that alleviates all of the visitor's concerns without the page feeling too long or bloated. He manages to show his credibility, technical expertise, and testimonials in a seamless package.  </p>
<p>This is a top portfolio and could serve as some real inspiration for your next portfolio build.</p>
<h2 id="heading-summary">Summary</h2>
<p>We've had a look at 10 fantastic freelancing portfolio sites that were designed in a multitude of styles and personalities.  </p>
<p>If you were to take away one thing from this article, it should be this:  </p>
<p>Nail those fundamentals that all portfolio sites should have.  </p>
<p>Once you've figured out how you want to present the most important information for the visitors of your site, you can start becoming fancy.  </p>
<p>Don't do it the other way or you run the risk of losing potential clients or jobs.  </p>
<p>Hopefully, you've found some inspiration to improve upon or build your freelance portfolio site!  </p>
<p>I'll leave you with one last portfolio site to play with by <a target="_blank" href="http://www.rleonardi.com/">Robby Leonardi</a>.  </p>
<p>That's it!</p>
<p>Hope you found this article helpful.</p>
<p>See you <a target="_blank" href="https://twitter.com/study_web_dev">on Twitter</a></p>
<p>Kyle</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Portfolio Website with React ]]>
                </title>
                <description>
                    <![CDATA[ Today you're going to create one of the most important apps you can build for yourself: your developer portfolio. Every React developer or web developer in general needs to be able to show off what they can do to any potential client or employer. Tha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-portfolio-website-react/</link>
                <guid isPermaLink="false">66d0375bc1024fe75b758f37</guid>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tailwind ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Reed ]]>
                </dc:creator>
                <pubDate>Mon, 21 Jun 2021 18:15:53 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/react-portfolio-2021.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Today you're going to create one of the most important apps you can build for yourself: your developer portfolio.</p>
<p>Every React developer or web developer in general needs to be able to show off what they can do to any potential client or employer.</p>
<p>That's exactly what we're going to be building right now, with the help of a number of industry standard tools, including React, Tailwind CSS, and Netlify.</p>
<p>Let's get started!</p>
<h2 id="heading-what-will-the-portfolio-look-like">What Will the Portfolio Look Like?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/portfolio-1-min.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is the final version of the portfolio you will be building.</p>
<p>It will feature information about yourself, what projects you have made, what skills you've used in making those projects, and will include a contact form for clients or employers to reach out to you.</p>
<h2 id="heading-what-tools-will-we-be-using">What Tools Will We Be Using?</h2>
<ul>
<li>We will use React to create the app's user interface. It will allow us to compose each part of our landing page through reusable components and extend our app if we want to add additional features, such as a blog.</li>
<li>To style our application, we will use Tailwind CSS. To give our app a professional appearance, Tailwind will allow us to easily apply multiple styles through combining classnames on our React elements. </li>
<li>For pushing our app to the web, we will use the free service Netlify. It will serve our project on a custom domain to users very quickly with the help of a CDN (content delivery network).</li>
</ul>
<h2 id="heading-how-to-get-started">How to Get Started</h2>
<p><strong><a target="_blank" href="https://reedbarger.com/resources/react-portfolio">You can download the starting files for our project here</a>.</strong></p>
<p>When you grab the code, all you will have to do is drag your (unzipped) project folder into your code editor and run the command:</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>And you're good to go!</p>
<h2 id="heading-what-tools-do-i-need-to-build-my-portfolio">What Tools Do I Need to Build my Portfolio?</h2>
<p>To go through the entire process of creating our app from start to deployment, you will need to have the following:</p>
<ol>
<li>Node.js installed on your computer. You can download it at nodejs.org.</li>
<li>Git installed on your computer. You can download it at git-scm.com.</li>
<li>I would recommend using VS Code as your code editor. You can download it at code.visualstudio.com.</li>
<li>A free Netlify account at netlify.com.</li>
<li>A free GitHub account at github.com.</li>
</ol>
<h2 id="heading-how-to-build-the-portfolio-structure">How to Build the Portfolio Structure</h2>
<p>The benefit of using React is that we could expand our app to as many pages as we like, very simply, and add a lot of additional content. </p>
<p>However, since we're just working with one page, we can within our app component figure out the different components that we need very quickly. We will have a Navbar on top with all of the links to jump to different sections of our portfolio. </p>
<p>After that, we will include an about section, a section for our projects, testimonials, and finally our contact form.</p>
<p>This quick planning allows us to figure out what our components should be named and in what order. We can go ahead and add all of them to our App.js file (in src):</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/App.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</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">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Projects</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Skills</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Testimonials</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-create-our-components">How to Create our Components</h2>
<p>Now that we have all these components listed out we need to go ahead and create them. </p>
<p>Within our source (src) folder, we're going to create a folder called components with all of the files that we need:</p>
<pre><code>my-portfolio
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.js
    ├── data.js
    ├── index.css
    ├── index.js
    └── components
        ├── About.js
        ├── Contact.js
        ├── Navbar.js
        ├── Projects.js
        ├── Skills.js
        └── Testimonials.js
</code></pre><p>Then we will create the basic structure of each React component and export it from that file with <code>export default</code>:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/About.js</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">About</span>(<span class="hljs-params"></span>) </span>{}

<span class="hljs-comment">// repeat the same basic structure for all 6 components</span>
</code></pre>
<p>And finally make sure to import it back in App.js:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/App.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/About"</span>;
<span class="hljs-keyword">import</span> Contact <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Contact"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar"</span>;
<span class="hljs-keyword">import</span> Projects <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Projects"</span>;
<span class="hljs-keyword">import</span> Skills <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Skills"</span>;
<span class="hljs-keyword">import</span> Testimonials <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Testimonials"</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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</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">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Projects</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Skills</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Testimonials</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p><em>Note that there should be six components in total.</em> </p>
<h2 id="heading-intro-to-tailwind-css">Intro to Tailwind CSS</h2>
<p>Once that's done, we can start working with Tailwind CSS, in order to start to give our app a basic appearance. </p>
<p>The benefit of using Tailwind CSS is that we don't have to write any styles manually in a CSS stylesheet. All we have to do is combine multiple classes to create the appearance that we want. </p>
<p>For example, to give our portfolio a dark background with gray text applied to all of our child components, you can add the following classes to our <code>main</code> element:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/App.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/About"</span>;
<span class="hljs-keyword">import</span> Contact <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Contact"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar"</span>;
<span class="hljs-keyword">import</span> Projects <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Projects"</span>;
<span class="hljs-keyword">import</span> Skills <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Skills"</span>;
<span class="hljs-keyword">import</span> Testimonials <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Testimonials"</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">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-400 bg-gray-900 body-font"</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">About</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Projects</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Skills</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Testimonials</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Contact</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-build-the-about-component">How to Build the About Component</h2>
<p>Let's start on our first section, the about section. This will consist of a basic introduction to ourselves and what skills we specialize in. </p>
<p>It's also going to include some links to the contact form as well as our past projects. Since these links will be to different parts of the same page, we can use the hashes: "/#projects" and "/#contact".</p>
<p>To make these links work and to be able to jump to each section, we will set the <code>id</code> attribute of the projects section to "projects" and those of the contact section to "contact".</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/About.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">About</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"about"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mx-auto flex px-10 py-20 md:flex-row flex-col items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font sm:text-4xl text-3xl mb-4 font-medium text-white"</span>&gt;</span>
            Hi, I'm Reed.
            <span class="hljs-tag">&lt;<span class="hljs-name">br</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"hidden lg:inline-block"</span> /&gt;</span>I love to build amazing
            apps.
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-8 leading-relaxed"</span>&gt;</span>
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui
            laborum quasi, incidunt dolore iste nostrum cupiditate voluptas?
            Laborum, voluptas natus?
          <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">className</span>=<span class="hljs-string">"flex justify-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
              <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex text-white bg-green-500 border-0 py-2 px-6 focus:outline-none hover:bg-green-600 rounded text-lg"</span>&gt;</span>
              Work With Me
            <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">"#projects"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg"</span>&gt;</span>
              See My Past Work
            <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">className</span>=<span class="hljs-string">"lg:max-w-lg lg:w-full md:w-1/2 w-5/6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"object-cover object-center rounded"</span>
            <span class="hljs-attr">alt</span>=<span class="hljs-string">"hero"</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">"./coding.svg"</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">section</span>&gt;</span></span>
  );
}
</code></pre>
<p>For the image on the righthand side of the section, I am using an svg file from the <code>public</code> folder, coding.svg.</p>
<p>This image serves merely as a temporary placeholder. I would highly recommend using an actual image of yourself.</p>
<h2 id="heading-how-to-build-the-projects-component">How to Build the Projects Component</h2>
<p>Our projects section will consist of a <code>section</code> element with an <code>id</code> of "projects". This will feature a gallery of all the projects that we've built, which will include images.</p>
<p>It'll have the title of the project, along with the technologies we use to make it, and a link to it (if it is deployed). </p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Projects.js</span>

<span class="hljs-keyword">import</span> { CodeIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { projects } <span class="hljs-keyword">from</span> <span class="hljs-string">"../data"</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">Projects</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-400 bg-gray-900 body-font"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto text-center lg:px-40"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col w-full mb-20"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">CodeIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mx-auto inline-block w-10 mb-4"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:text-4xl text-3xl font-medium title-font mb-4 text-white"</span>&gt;</span>
            Apps I've Built
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-2/3 mx-auto leading-relaxed text-base"</span>&gt;</span>
            Lorem ipsum, dolor sit amet consectetur adipisicing elit. Explicabo
            facilis repellat ab cupiditate alias vero aliquid obcaecati quisquam
            fuga dolore.
          <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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap -m-4"</span>&gt;</span>
          {projects.map((project) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
              <span class="hljs-attr">href</span>=<span class="hljs-string">{project.link}</span>
              <span class="hljs-attr">key</span>=<span class="hljs-string">{project.image}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:w-1/2 w-100 p-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex relative"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                  <span class="hljs-attr">alt</span>=<span class="hljs-string">"gallery"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0 w-full h-full object-cover object-center"</span>
                  <span class="hljs-attr">src</span>=<span class="hljs-string">{project.image}</span>
                /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-8 py-10 relative z-10 w-full border-4 border-gray-800 bg-gray-900 opacity-0 hover:opacity-100"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"tracking-widest text-sm title-font font-medium text-green-400 mb-1"</span>&gt;</span>
                    {project.subtitle}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font text-lg font-medium text-white mb-3"</span>&gt;</span>
                    {project.title}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed"</span>&gt;</span>{project.description}<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">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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span></span>
  );
}
</code></pre>
<p>Note that we are also going to use the library <code>@heroicons/react</code> in order to be able to write some SVG icons as React components. </p>
<p>We are importing an array of projects from a data.js file in the same folder. There we are exporting an array of objects which each include an individual project's data:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/data.js</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> projects = [
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"React Reserve"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"MERN Stack"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-1.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://reactbootcamp.com"</span>,
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"React Tracks"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"React and Python"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-2.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://reedbarger.com"</span>,
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"DevChat"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"React and Firebase"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-3.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://jsbootcamp.com"</span>,
  },
  {
    <span class="hljs-attr">title</span>: <span class="hljs-string">"Epic Todo App"</span>,
    <span class="hljs-attr">subtitle</span>: <span class="hljs-string">"React Hooks"</span>,
    <span class="hljs-attr">description</span>:
      <span class="hljs-string">"Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?"</span>,
    <span class="hljs-attr">image</span>: <span class="hljs-string">"./project-4.gif"</span>,
    <span class="hljs-attr">link</span>: <span class="hljs-string">"https://pythonbootcamp.com"</span>,
  },
];
</code></pre>
<h2 id="heading-how-to-build-the-skills-component">How to Build the Skills Component</h2>
<p>Let's fill out the section for all the skills and technologies that we know. </p>
<p>This will consist of a simple list of all of the major tools that we're familiar with and can use in our employers or clients projects. </p>
<p>Once again, we are going to import an array from our data folder. But this array consists of number of strings which represent each of the skills that we know such as JavaScript, React, and Node:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Skills.js</span>

<span class="hljs-keyword">import</span> { BadgeCheckIcon, ChipIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { skills } <span class="hljs-keyword">from</span> <span class="hljs-string">"../data"</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">Skills</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center mb-20"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChipIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 inline-block mb-4"</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:text-4xl text-3xl font-medium title-font text-white mb-4"</span>&gt;</span>
            Skills <span class="hljs-symbol">&amp;amp;</span> Technologies
          <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base leading-relaxed xl:w-2/4 lg:w-3/4 mx-auto"</span>&gt;</span>
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nisi sit
            ipsa delectus eum quo voluptas aspernatur accusantium distinctio
            possimus est.
          <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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap lg:w-4/5 sm:mx-auto sm:mb-2 -mx-2"</span>&gt;</span>
          {skills.map((skill) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{skill}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 sm:w-1/2 w-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-800 rounded flex p-4 h-full items-center"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">BadgeCheckIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-green-400 w-6 h-6 flex-shrink-0 mr-4"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-medium text-white"</span>&gt;</span>
                  {skill}
                <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>&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">section</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-build-the-testimonials-component">How to Build the Testimonials Component</h2>
<p>In the Testimonials component, we are going to list a couple of testimonials maybe from past clients or people who are familiar with our work. </p>
<p>These are going to consist of a couple of cards that feature the testimonial itself as well as who it's from and the company that this person is from. </p>
<p>We are also importing a testimonials array with a number of objects that feature the quote, image, name, and company. </p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Testimonials</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { TerminalIcon, UsersIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<span class="hljs-keyword">import</span> { testimonials } <span class="hljs-keyword">from</span> <span class="hljs-string">"../data"</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">Testimonials</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"testimonials"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">UsersIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-10 inline-block mb-4"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"sm:text-4xl text-3xl font-medium title-font text-white mb-12"</span>&gt;</span>
          Client Testimonials
        <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">className</span>=<span class="hljs-string">"flex flex-wrap m-4"</span>&gt;</span>
          {testimonials.map((testimonial) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-4 md:w-1/2 w-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-full bg-gray-800 bg-opacity-40 p-8 rounded"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">TerminalIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"block w-8 text-gray-500 mb-4"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-relaxed mb-6"</span>&gt;</span>{testimonial.quote}<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">className</span>=<span class="hljs-string">"inline-flex items-center"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                    <span class="hljs-attr">alt</span>=<span class="hljs-string">"testimonial"</span>
                    <span class="hljs-attr">src</span>=<span class="hljs-string">{testimonial.image}</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12 rounded-full flex-shrink-0 object-cover object-center"</span>
                  /&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-grow flex flex-col pl-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-medium text-white"</span>&gt;</span>
                      {testimonial.name}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm uppercase"</span>&gt;</span>
                      {testimonial.company}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-build-the-contact-component">How to Build the Contact Component</h2>
<p>At the end of our landing page, we're going to include our contact form to allow potential employers to reach out to us. </p>
<p>This form will have 3 inputs: a name, email, and message input. </p>
<p>To receive these form submissions, we will use the tool Netlify Forms to very easily take care of saving those messages.</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Contact.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto flex sm:flex-nowrap flex-wrap"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-2/3 md:w-1/2 bg-gray-900 rounded-lg overflow-hidden sm:mr-10 p-10 flex items-end justify-start relative"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">iframe</span>
            <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">height</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">title</span>=<span class="hljs-string">"map"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0"</span>
            <span class="hljs-attr">frameBorder</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginHeight</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginWidth</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">filter:</span> "<span class="hljs-attr">opacity</span>(<span class="hljs-attr">0.7</span>)" }}
            <span class="hljs-attr">src</span>=<span class="hljs-string">"https://www.google.com/maps/embed/v1/place?q=97+warren+st+new+york+city&amp;key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-900 relative flex flex-wrap py-6 rounded shadow-md"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                ADDRESS
              <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">className</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
                97 Warren St. <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
                New York, NY 10007
              <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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6 mt-4 lg:mt-0"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                EMAIL
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-indigo-400 leading-relaxed"</span>&gt;</span>
                reedbarger@email.com
              <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs mt-4"</span>&gt;</span>
                PHONE
              <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">className</span>=<span class="hljs-string">"leading-relaxed"</span>&gt;</span>123-456-7890<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">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">form</span>
          <span class="hljs-attr">netlify</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/3 md:w-1/2 flex flex-col md:ml-auto w-full md:py-8 mt-8 md:mt-0"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white sm:text-4xl text-3xl mb-1 font-medium title-font"</span>&gt;</span>
            Hire Me
          <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">className</span>=<span class="hljs-string">"leading-relaxed mb-5"</span>&gt;</span>
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illum
            suscipit officia aspernatur veritatis. Asperiores, aliquid?
          <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">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Name
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</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">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Email
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</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">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
              <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Message
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 h-32 text-base outline-none text-gray-100 py-1 px-3 resize-none leading-6 transition-colors duration-200 ease-in-out"</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">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"</span>&gt;</span>
            Submit
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span></span>
  );
}
</code></pre>
<h2 id="heading-how-to-embed-a-google-maps-location">How to Embed a Google Maps Location</h2>
<p>To the left of the form we will include a Google Maps embedded Google map of where we are located. </p>
<p>We can do so with the help of an online tool: embed-map.com. All you have to do is just enter your location and hit "Generate HTML code". </p>
<p>In the code we are given, don't copy all of the code, just the <code>src</code> attribute from the iframe element. We will replace that value with the default <code>src</code> value we have for our iframe.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/portfolio-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To send over any submitted form data to Netlify, Netlify Forms needs to recognize a form as static HTML. Because our React app is controlled by JavaScript and doesn't consist of plain HTML, we need to add a hidden form to our index.html file in the public folder.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- public/index.html --&gt;</span>

<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- head content skipped --&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>

  <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">netlify</span> <span class="hljs-attr">netlify-honeypot</span>=<span class="hljs-string">"bot-field"</span> <span class="hljs-attr">hidden</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">noscript</span>&gt;</span>You need to enable JavaScript to run this app.<span class="hljs-tag">&lt;/<span class="hljs-name">noscript</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"root"</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">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>We need to hide this form, because it doesn't need to be seen by the user, just Netlify. </p>
<p>We'll give it the attribute of <code>hidden</code> as well as a <code>name</code> attribute that matches the JSX form in Contact.js. We also need to give it the <code>netlify</code> attribute so that Netlify Forms recognizes it. Finally, we need to include all of the same inputs as our JSX form: for name, email, message.</p>
<h2 id="heading-how-to-submit-the-contact-form">How to Submit the Contact Form</h2>
<p>Once that's done, we'll head back to Contact.js. We're going to use JavaScript in order to submit this form. </p>
<p>First of all, we're going to create some dedicated state for each of the values that are typed in the form for name, email, and message:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> [name, setName] = React.useState(<span class="hljs-string">""</span>);
<span class="hljs-keyword">const</span> [email, setEmail] = React.useState(<span class="hljs-string">""</span>);
<span class="hljs-keyword">const</span> [message, setMessage] = React.useState(<span class="hljs-string">""</span>);
</code></pre>
<p>We will store what the user types in to each of the inputs in state with the help of the <code>onChange</code> handler.</p>
<p>To handle submission of the form, we will add the <code>onSubmit</code> prop to it. The function that will be called, <code>handleSubmit</code>, will make a post request to the endpoint "/" with all of our form data.</p>
<p>We will set the headers of the request to indicate that we are sending over form data. For the request body, we will include the form name as well as all of the form data from the <code>name</code>, <code>email</code>, and <code>message</code> state variables.</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Contact.js</span>

<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [name, setName] = React.useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [email, setEmail] = React.useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [message, setMessage] = React.useState(<span class="hljs-string">""</span>);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encode</span>(<span class="hljs-params">data</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.keys(data)
      .map(
        <span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> <span class="hljs-built_in">encodeURIComponent</span>(key) + <span class="hljs-string">"="</span> + <span class="hljs-built_in">encodeURIComponent</span>(data[key])
      )
      .join(<span class="hljs-string">"&amp;"</span>);
  }

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleSubmit</span>(<span class="hljs-params">e</span>) </span>{
    e.preventDefault();
    fetch(<span class="hljs-string">"/"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/x-www-form-urlencoded"</span> },
      <span class="hljs-attr">body</span>: encode({ <span class="hljs-string">"form-name"</span>: <span class="hljs-string">"contact"</span>, name, email, message }),
    })
      .then(<span class="hljs-function">() =&gt;</span> alert(<span class="hljs-string">"Message sent!"</span>))
      .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> alert(error));
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"contact"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"relative"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container px-5 py-10 mx-auto flex sm:flex-nowrap flex-wrap"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-2/3 md:w-1/2 bg-gray-900 rounded-lg overflow-hidden sm:mr-10 p-10 flex items-end justify-start relative"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">iframe</span>
            <span class="hljs-attr">width</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">height</span>=<span class="hljs-string">"100%"</span>
            <span class="hljs-attr">title</span>=<span class="hljs-string">"map"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"absolute inset-0"</span>
            <span class="hljs-attr">frameBorder</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginHeight</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">marginWidth</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">filter:</span> "<span class="hljs-attr">opacity</span>(<span class="hljs-attr">0.7</span>)" }}
            <span class="hljs-attr">src</span>=<span class="hljs-string">"https://www.google.com/maps/embed/v1/place?q=97+warren+st+new+york+city&amp;key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-900 relative flex flex-wrap py-6 rounded shadow-md"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                ADDRESS
              <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">className</span>=<span class="hljs-string">"mt-1"</span>&gt;</span>
                97 Warren St. <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
                New York, NY 10007
              <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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/2 px-6 mt-4 lg:mt-0"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs"</span>&gt;</span>
                EMAIL
              <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-indigo-400 leading-relaxed"</span>&gt;</span>
                reedbarger@email.com
              <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-semibold text-white tracking-widest text-xs mt-4"</span>&gt;</span>
                PHONE
              <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">className</span>=<span class="hljs-string">"leading-relaxed"</span>&gt;</span>123-456-7890<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">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">form</span>
          <span class="hljs-attr">netlify</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"contact"</span>
          <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"lg:w-1/3 md:w-1/2 flex flex-col md:ml-auto w-full md:py-8 mt-8 md:mt-0"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white sm:text-4xl text-3xl mb-1 font-medium title-font"</span>&gt;</span>
            Hire Me
          <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">className</span>=<span class="hljs-string">"leading-relaxed mb-5"</span>&gt;</span>
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illum
            suscipit officia aspernatur veritatis. Asperiores, aliquid?
          <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">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Name
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"name"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"name"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setName(e.target.value)}
            /&gt;
          <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">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Email
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setEmail(e.target.value)}
            /&gt;
          <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">className</span>=<span class="hljs-string">"relative mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
              <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"leading-7 text-sm text-gray-400"</span>&gt;</span>
              Message
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 h-32 text-base outline-none text-gray-100 py-1 px-3 resize-none leading-6 transition-colors duration-200 ease-in-out"</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setMessage(e.target.value)}
            /&gt;
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"</span>&gt;</span>
            Submit
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span></span>
  );
}
</code></pre>
<p>As you can see above, we are encoding the form data with a special <code>encode</code> function that you see here. </p>
<p>If the message is sent correctly, we will display an alert that says "Message sent". Otherwise if there is an error, we are going to alert the user of that error.</p>
<h2 id="heading-how-to-build-the-navbar-component">How to Build the Navbar Component</h2>
<p>The last step is to build out our Navbar component. </p>
<p>We want this navbar to stick to the top of our app on large devices and not be sticky on mobile devices. </p>
<p>Additionally, we want to include links to each of our relevant sections for our project skills testimonials and our contact form:</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/components/Navbar.js</span>

<span class="hljs-keyword">import</span> { ArrowRightIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/solid"</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">Navbar</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-800 md:sticky top-0 z-10"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"title-font font-medium text-white mb-4 md:mb-0"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#about"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"ml-3 text-xl"</span>&gt;</span>
            Reed Barger
          <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">nav</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"md:mr-auto md:ml-4 md:py-1 md:pl-4 md:border-l md:border-gray-700    flex flex-wrap items-center text-base justify-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#projects"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-5 hover:text-white"</span>&gt;</span>
            Past Work
          <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">"#skills"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-5 hover:text-white"</span>&gt;</span>
            Skills
          <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">"#testimonials"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-5 hover:text-white"</span>&gt;</span>
            Testimonials
          <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">"#contact"</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0"</span>&gt;</span>
          Hire Me
          <span class="hljs-tag">&lt;<span class="hljs-name">ArrowRightIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-4 h-4 ml-1"</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">header</span>&gt;</span></span>
  );
}
</code></pre>
<p>How does this stick to top of the page on a larger device? With the help of the class <code>md:sticky</code> on our <code>header</code> element. </p>
<p>This class means that it will have the style rule <code>position: sticky;</code> applied starting on a medium-sized breakpoint (768px).</p>
<h2 id="heading-how-to-deploy-your-portfolio">How to Deploy Your Portfolio</h2>
<p>Now to make our portfolio live, we need to push our application to GitHub. </p>
<p>If you're not familiar with Git and GitHub, I would take a little while just to learn how to push your code to your GitHub account for the first time. It's an essential skill for any developer to know.</p>
<p>Once you're familiar with this process, we can first create a new Github repository. After that, we will run <code>git add .</code>, <code>git commit -m "Deploy"</code>, create our git remote, and <code>git push -u origin master</code>. </p>
<p>Once our project is on GitHub, we can head over to Netlify and select the option "Choose Site from Git". Then we will choose GitHub for our continuous deployment, and pick the GitHub repository to which we just pushed our code.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/portfolio-3-min.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After that, our project will be automatically deployed to the web!</p>
<h2 id="heading-whats-next">What's Next</h2>
<p>Congratulations! You now have a portfolio app live on the web that shows off all of your projects and skills to potential employers.</p>
<p>The next step to take would be to set up a custom domain, preferably with your name (i.e. <a target="_blank" href="https://reedbarger.com/">reedbarger.com</a>). Since Netlify includes a DNS you can easily set up a custom domain with them.</p>
<p>Look into maybe adding a blog to your React app to show off even more of your developer knowledge to potential employers.</p>
<p>Make your personal portfolio an expression of yourself and what you are passionate about as a developer and you'll have success!</p>
<h2 id="heading-become-a-professional-react-developer">Become a Professional React Developer</h2>
<p>React is hard. You shouldn't have to figure it out yourself.</p>
<p>I've put everything I know about React into a single course, to help you reach your goals in record time:</p>
<p><a target="_blank" href="https://www.thereactbootcamp.com"><strong>Introducing: The React Bootcamp</strong></a></p>
<p><strong>It’s the one course I wish I had when I started learning React.</strong></p>
<p>Click below to try the React Bootcamp for yourself:</p>
<p><a target="_blank" href="https://www.thereactbootcamp.com"><img src="https://reedbarger.nyc3.digitaloceanspaces.com/reactbootcamp/react-bootcamp-cta-alt.png" alt="Click to join the React Bootcamp" width="600" height="400" loading="lazy"></a>
<em>Click to get started</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build and Deploy a Portfolio with Vue.js Axios, the GitHub REST API, and Netlify ]]>
                </title>
                <description>
                    <![CDATA[ By Fabio Pacific In this free book, we will build two simple projects and deploy them on Netlify. We will use Vue.js as our front-end framework, and use different technologies to build our projects.  If you follow this tutorial to the end, you will b... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-portfolio-with-vuejs/</link>
                <guid isPermaLink="false">66d45edcb3016bf139028d31</guid>
                
                    <category>
                        <![CDATA[ axios ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ portfolio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ projects ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 12 May 2021 20:53:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/05/VUE.JS-_-Article-Cover.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Fabio Pacific</p>
<p>In this free book, we will build two simple projects and deploy them on Netlify. We will use Vue.js as our front-end framework, and use different technologies to build our projects. </p>
<p>If you follow this tutorial to the end, you will build a simplified version of Twitter and a single page application for a portfolio using the GitHub API.</p>
<h2 id="heading-what-you-need-to-know-to-follow-this-tutorial">What you need to know to follow this tutorial</h2>
<p>To follow along, you will need at least some basic knowledge of HTML, CSS, and JavaScript. </p>
<p>Knowledge of Vue.js isn't required, as you will learn the basics first and then we'll move into building the projects together.</p>
<p>At the end of each section, you'll find that information in video form via a YouTube link/embed. That way you can watch the videos to cement your knowledge of what you just read.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-introduction">Introduction</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-vue">How to Install Vue</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-vue-instance">How to Create a Vue Instance</a></li>
<li><a class="post-section-overview" href="#heading-how-to-work-with-templates-in-vue">How to Work with Templates in Vue</a></li>
<li><a class="post-section-overview" href="#heading-vue-directives">Vue Directives</a></li>
<li><a class="post-section-overview" href="#heading-methods-in-vue">Methods</a></li>
<li><a class="post-section-overview" href="#heading-conditionals-in-vue-v-ifv-else-ifv-elsev-show">Conditionals</a></li>
<li><a class="post-section-overview" href="#heading-loops-in-vue">Loops</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-user-input-with-event-handling-v-on-in-vue">How to handle user inputs with events Handling</a></li>
<li><a class="post-section-overview" href="#heading-two-way-model-binding-v-model-in-vue">Two way model binding (v-model)</a></li>
<li><a class="post-section-overview" href="#heading-computed-properties-and-methods">Computed Properties and methods</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-simple-twitter-clone"><strong>Project</strong>: Simple Twitter Clone</a></li>
<li><a class="post-section-overview" href="#heading-vue-component-basics">Component basics</a></li>
<li><a class="post-section-overview" href="#heading-how-to-update-your-simpletwitter-project-with-components">Project Update: Simple Twitter clone with components</a></li>
<li><a class="post-section-overview" href="#heading-how-to-perform-api-calls-with-axios">Axios and RestAPI</a></li>
<li><a class="post-section-overview" href="#heading-how-to-handle-routing-with-vuerouter">Routing with VueRouter</a></li>
<li><a class="post-section-overview" href="#heading-final-project-how-to-build-a-portfolio-with-vuejs-vuerouter-axios-github-api-and-deploy-to-netlify"><strong>Final Project</strong>: build a Portfolio with VueJS, VueRouter, Axios, GitHub API</a></li>
<li><a class="post-section-overview" href="#heading-continuos-deployment-with-bitbucket-and-netlify"><strong>Deploy</strong> Continuous deployment with BitBucket and Netlify</a></li>
</ul>
<h2 id="heading-introduction">Introduction</h2>
<p>VueJS is a JavaScript framework that has become really popular in recent years. </p>
<p>In this guide, we will start by looking at the fundamentals first, with a quick look at two libraries: VueRouter and Axios. We will use them to build a cool portfolio project at the end.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/CzgP6GamIMc" 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>
<p>Click to view the video on <a target="_blank" href="https://youtu.be/CzgP6GamIMc">YouTube</a>.</p>
<h2 id="heading-how-to-install-vue">How to Install Vue</h2>
<p>You can use Vue in your projects by installing it using a package manager like NPM or by using its CDN. If you've never used Vuejs before, I suggest that you use the CDN, as it will be easier if you want to code along with me. </p>
<p>Click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/1-installation/">Repository</a></p>
<p>Click to view the <a target="_blank" href="https://youtu.be/enz0Vi3NuDA">YouTube-Video</a> or find it at the end of this section to reinforce what you've learned.</p>
<h3 id="heading-the-vue-cdn">The Vue CDN</h3>
<p>For the CDN, we only need to include the script tag below inside our HTML file:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Development version for prototyping and learning --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Alternatively, you can use a production-ready script that uses a specific stable release, like this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Production version --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>In production, Vue suggests using the optimized version to replace vue.js with vue.min.js.</p>
<p>There is also an ES Modules-compatible build:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js'</span>
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-install-vue-via-npm">How to Install Vue via NPM</h3>
<p>If you plan to build large scale applications, I recommend installing via NPM like this:</p>
<pre><code class="lang-bash">npm install vue
</code></pre>
<p>As I said above, we will use the Vue CDN so that anyone can follow this guide. So our final HTML file will look something like this: </p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJS Tutorial<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- vue development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

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



    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>Let's break this code down. First, we've add a basic markup for an HTML file. Then we've included the script tag for the VueJs framework. </p>
<p>In the end, before closing the body tag, we've added our main.js script where we placed all the JavaScript code for our application. </p>
<p>Let's now move to the next step and add our first Vue instance inside the main.js file.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/enz0Vi3NuDA" 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-how-to-create-a-vue-instance">How to Create a Vue Instance</h2>
<p>Once you've installed Vue or included it via its CDN, you can create a Vue instance. You can do that using the <code>new Vue()</code> function. This function accepts an object of options.</p>
<p>If you read the documentation, you will see that the vue instance is often stored inside a variable called <code>vm</code>, but you can call it anything you like. I'll call it <code>app</code> during this guide.</p>
<p>So now, inside the main.js file you need to create a variable and store in it the Vue instance like so:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options goes here</span>
})
</code></pre>
<p>The object you pass to the Vue instance is called the options object.
Inside the options object, you can add all the options described in the Vue API reference pages to build our application. </p>
<p>The options object has properties divided into multiple sections: </p>
<ul>
<li>Data </li>
<li>DOM </li>
<li>Life Cycle Hooks </li>
<li>Assets</li>
<li>Composition </li>
<li>Misc categories </li>
</ul>
<p>The first property that you need to build you Vue application is used to connect Vue with a root DOM element. Then you will need some data options to work with.</p>
<p>Let's start by connecting the Vue instance with a root DOM element.</p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/2-create-vue-instance/">Repository</a> here.</p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/gBJaL7Jqh4w">YouTube-Video</a> or find it at the end of this section so you can review what you've learned.</p>
<h3 id="heading-optionsdom-how-to-select-the-root-dom-element">Options/DOM: How to select the root DOM element</h3>
<p>The Options/DOM API gives you an <code>el</code> property that you can use to select an existing DOM element that Vue will use to mount your application instance. </p>
<p>The <code>el</code> property accepts a string that contains a CSS selector for the element or directly a DOM element.</p>
<p>NOTE: Vue discourages using the body or HTML tags and suggests using a different element as a mounting point.</p>
<p>Let's do it. Inside the body of the index.html file, you need to put the following code:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now you have a root element that you can use to connect the Vue instance.
Back inside the main.js file, let's select this element inside the options object. </p>
<p>You can now use the <code>el</code> property to select the element you created with an id of <code>app</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
})
</code></pre>
<p>You now have an element to work with. You can move on to the next step and add to the options object the data object.</p>
<p>You can read more about it in the documentation here:[https://vuejs.org/v2/api/#Options-DOM]</p>
<h3 id="heading-optionsdata-how-to-add-the-data-object-or-function-when-used-in-a-component">Options/Data: How to add the data object (or function when used in a component)</h3>
<p>When a new instance is created, it adds all properties found in its data object to the Vue reactivity system. And when a value in the data object changes, the view will reflect these changes. This is at the base of the VueJS reactivity system. </p>
<p>To explain it, let's see a practical example.</p>
<h4 id="heading-create-a-data-object">Create a data object</h4>
<p>Inside the main.js file you can create a data property that has an object as its value, like so:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {}
})
</code></pre>
<p>The data object can be defined directly inside the Vue instance like in the code above, or outside the instance like in the code below. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> dataObject = {}
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-comment">// all options go here</span>
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: dataObject
})
</code></pre>
<p>You can pick the one you like.</p>
<h4 id="heading-add-properties-to-the-data-object">Add properties to the Data Object</h4>
<p>Since VueJs is a JavaScript framework, it's helpful to remember that what you know about JavaScript is still valuable here. </p>
<p>Vue is just a JavaScript object that has a number of methods and properties that you can use to simplify and speed up your workflow.</p>
<p>Let's add some properties to the data object to see how it works.</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create a data object</span>
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">"#app"</span>,
    <span class="hljs-comment">// create a vue instance, add the data property and the dataObject created</span>
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">alert</span>: <span class="hljs-string">"This is an alert message! "</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ];
    }
})
</code></pre>
<p>With the code above, you simply add two properties to the data object: an <code>alert</code> property and a  <code>projects</code> property. </p>
<p>The alert property just a string while the projects property is an array of objects. </p>
<p>Now that you have some data to work with, let's see how you can access and modify their values.</p>
<h4 id="heading-manipulate-properties-in-the-data-object">Manipulate properties in the data object</h4>
<p>You can access and manipulate the properties of a data object using the variable that contains the Vue instance <code>app</code>. Then you can reference the properties using dot notation, like <code>app.alert</code>. </p>
<p>In the browser, if you open the console you can see that when you write <code>app</code> you get the Vue instance object. So, like any other object with dot notation, you get its properties and methods.</p>
<p>Let's try this out inside the console:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Access the alert property in the data object</span>
app.alert <span class="hljs-comment">// This is an alert message!</span>
<span class="hljs-comment">// update a data property value</span>
app.alert = <span class="hljs-string">"This is a new alert message!"</span> 
app.projects
</code></pre>
<p>The code above does three simple things:</p>
<ul>
<li>the first line accesses the <code>alert</code> property and prints its content "this is an alert message"</li>
<li>the second line assigns a new value to the <code>alert</code> property with the equals operator</li>
<li>finally, the third line returns the value of the projects array. </li>
</ul>
<p>You can also access the entire data object using the shortcuts $data or _data</p>
<p>Back in the console:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Access the entrie data object</span>
app.$data <span class="hljs-comment">// {__ob__: Observer} option 1</span>
app._data <span class="hljs-comment">// {__ob__: Observer} option 2</span>
</code></pre>
<p>You can read more about this in the documentation here: [https://vuejs.org/v2/api/#Options-Data]</p>
<h3 id="heading-options-data-methods">Options Data Methods</h3>
<p>The Vue instance gives you access to a number of properties and methods.
You can access default methods and properties using the <code>$</code> sign. It is used to differentiate Vue defined methods from those defined by the user.</p>
<p>There are a number of instance methods and properties predefined and split into four different categories:</p>
<ul>
<li>Instance Properties</li>
<li>Instance Methods / Data</li>
<li>Instance Methods / Events</li>
<li>Instance Methods/life cycle hooks</li>
</ul>
<p>For instance, with the following code, you can get the <code>data</code> and <code>options</code> objects or access the <code>watch</code> or the <code>on</code> methods. </p>
<pre><code class="lang-js">app.$data <span class="hljs-comment">// returns the data object</span>
app.$options <span class="hljs-comment">// returns the options object</span>
app.$watch() <span class="hljs-comment">// function that watched for changes on the vue instance</span>
app.$on() <span class="hljs-comment">// listen for a custom event on the vue instance</span>
</code></pre>
<p>I won't dive deeper into this since it's out of the scope of this guide. But if you are interested and want to learn more, here is the <a target="_blank" href="https://vuejs.org/v2/api/#Instance-Properties">documentation</a>.</p>
<h3 id="heading-lifecycle-hooks">Lifecycle Hooks</h3>
<p>Vue gives you access to a series of functions called lifecycle hooks. They allow you to run code at specific stages of the Vue initialization steps.</p>
<p>Inside all lifecycle hooks you have access to their <code>this</code> variable that points to the Vue instance. </p>
<p>You will see how this works in more detail in future sections. But for now this is a short summary of the available hooks and what they let you do:</p>
<ul>
<li>beforeCreate (you can run code before the Vue instance is created)</li>
<li>created (you can run code after the Vue instance is created )</li>
<li>beforeMount (you can run code before your element is mounted to the DOM)</li>
<li>mounted (you can run code when the element is mounted to the DOM)</li>
<li>beforeUpdate (you can run code before values are updated in the DOM)</li>
<li>updated (you can run code after values in the DOM have been updated)</li>
<li>beforeDestroy (you can run code before an instance is destroyed)</li>
<li>destroyed (you can run code when an instance is destroyed)</li>
</ul>
<p>During the course, we will often use the mounted hook. If you are curious to learn more about this topic, I suggest you look at the diagram in the documentation first. Find the lifecycle Hooks <a target="_blank" href="https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram">diagram</a> here.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/gBJaL7Jqh4w" 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-how-to-work-with-templates-in-vue">How to Work with Templates in Vue</h2>
<p>VueJS uses mustache syntax <code>{{ }}</code> to render data from the Vue instance inside the HTML element. </p>
<p>Using this syntax you can grab properties and methods defined in the Vue instance. The property is then parsed and rendered to the page.</p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/3-work-with-templates/">Repository here</a>.</p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/pDj3SQ8TNzs">YouTube-Video here</a> here, or find it at the end of this section to review what you've just learned.</p>
<h3 id="heading-text-data-binding">Text Data binding</h3>
<p>This is called text data binding. Let's see an example of how you can bind data between the Vue instance and your template file. </p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{ title }}<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>
</code></pre>
<p>The code above has an <code>h1</code> tag inside the root element with an id of <code>app</code> we defined in the previous chapter. </p>
<p>Inside the <code>h1</code> tag you use the double curly brackets syntax to render onto the page the value of a property in the data object called that you called <code>title</code>. </p>
<p>You don't have a <code>title</code> property yet inside your data object, so let's add it.</p>
<p>Inside the main.js file</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ]
    }
})
</code></pre>
<p>Now, with the code above you can render the content of the property <code>title</code> inside the <code>h1</code> tag in your template. The final result will be something like this: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>However, with this method, you can only pass a string. If you want to use HTML tags inside the string these will not be parsed but instead will be shown as simple strings.</p>
<p>For instance if you assign the following string to the <code>title</code> property</p>
<pre><code class="lang-js">    title: <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>
</code></pre>
<p>And then try to render it inside our HTML like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>The property <code>title</code> will be rendered as a plain string including the HTML tags 
ie. <code>John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;</code></p>
<p>Of course you can parse HTML too.</p>
<h3 id="heading-how-to-parse-raw-html">How to parse raw HTML</h3>
<p>To render a raw HTML element we need to introduce another important Vue concept called directives. </p>
<p>In this case, you will use the v-html directive inside your HTML tag as an attribute and pass to it the property title. </p>
<p>When you're using Vue directives, the text inside the quotes is considered a JavaScript expression. This means that it's computed and its result is rendered.  </p>
<p>Let's create a separate property for the title with HTML tags inside so that you can see how both render onto the page. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe Portfolio"</span>, 
        <span class="hljs-attr">titleHTLM</span> : <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ]
    }
})
</code></pre>
<p>Now inside your HTML file, you will use this <code>{{}}</code> syntax to render the property <code>title</code>. But on the tag where you want to render raw HTML, with the <code>titleHTML</code> property, you use the v-html directive instead.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"title"</span>&gt;</span>{{ title }}<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">v-html</span>=<span class="hljs-string">"titleHTML"</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>
</code></pre>
<p>Both elements will now render correctly including the second property that has HTML tags inside.</p>
<p>NOTE: Rendering HTML can expose XSS vulnerabilities. Never use this approach on user-provided content.</p>
<p>Now that you know how to render data onto the page, let's dig deeper into directives. </p>
<p>If you want to read more, visit the documentation 
<a target="_blank" href="https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions">here</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pDj3SQ8TNzs" 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-vue-directives">Vue Directives</h2>
<p>Inside your HTML files, you can use directives to interact with HTML attributes. A directive applies effects to the DOM when its expression changes. </p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/4-Directives/">Repository here</a></p>
<p>And you can click to view the <a target="_blank" href="https://youtu.be/LICvNmhsTEs">YouTube-Video here</a>, or you can find it at the end of this section to review what you've learned.</p>
<h3 id="heading-the-v-bind-directive-on-html-attributes">The v-bind directive on HTML attributes</h3>
<p>So far, you have used the <code>{{}}</code> syntax to render something between HTML opening and closing tags. But inside an HTML tag you cannot use the {{ }} syntax. </p>
<p>So how do you connect an HTML attribute to the Vue instance? You use the v-bind directive instead which lets you access data object properties as you have done before. </p>
<p>The v-bind directive is one of the directives that take arguments which are specified after the colon. In our case here, what's specified after the colon is the HTML attribute name like id, class, href, src and so on. </p>
<p>If you need to dynamically assign an attribute like href or even a class, you can bind it with the Vue instance using the v-bind directive. It will then be able to get what's in the options object, like properties in the data object.</p>
<p>Let's see v-bind in action and start connecting the <code>id</code> and <code>class</code> attributes so you can assign them values dynamically with Vue.</p>
<p>Inside our index.html file:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-bind:class</span>=<span class="hljs-string">"dynamicClass"</span> <span class="hljs-attr">v-bind:id</span>=<span class="hljs-string">"dynamicId"</span>&gt;</span>Dinamically assign a class and an id to the div<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>
</code></pre>
<p>Let's break the code above and see what it's doing. </p>
<p>First, you have a <code>div</code> tag inside the root element. Then you use the v-bind directive on the class and the id attributes. </p>
<p>Inside the quotes, you specify two properties that later you'll define inside the data object of your Vue instance.</p>
<p>Remember that when using Vue directives the content between quotes is treated as a JavaScript expression.</p>
<p>Let's define these two properties inside the Vue instance.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">title</span>: <span class="hljs-string">"John Doe Portfolio"</span>, 
        <span class="hljs-attr">titleHTLM</span> : <span class="hljs-string">"John Doe &lt;span class='badge'&gt;Portfolio&lt;/span&gt;"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],
        <span class="hljs-attr">dynamicId</span> : <span class="hljs-string">"projects_section"</span>,
        <span class="hljs-attr">dynamicClass</span> : <span class="hljs-string">"projects"</span>
    }
})
</code></pre>
<p>So you have defined <code>dinamicId: "projects_section"</code> and <code>dynmicClass: "projects"</code> properties and assigned them two values.</p>
<p>Thanks to the data binding on the attributes, your HTML tag will be rendered as follows (and you can now dynamically change your attributes values and see them change reactively):</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"projects_section"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span>&gt;</span>Dynamically assign a class and an id to the div<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h3 id="heading-v-bind-with-boolean-values">V-bind with Boolean values</h3>
<p>With attributes using a boolean value, the v-bind directive works differently. It will show the attribute only if the property's value is true. In all other cases, it won't render the attribute and its content. </p>
<p>For the next example, you will use a button with the disabled attribute.</p>
<p>Inside your root HTML element:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">v-bind:disabled</span>=<span class="hljs-string">"disabled"</span>&gt;</span>You can't click this button<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside a Vue instance:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    <span class="hljs-comment">//disabled: false, // wont render the attribute</span>
    <span class="hljs-comment">//disabled: null, // wont render the attribute</span>
    <span class="hljs-comment">//disabled: undefined, // wont render the attribute</span>
    <span class="hljs-attr">disabled</span>: <span class="hljs-literal">true</span> <span class="hljs-comment">// renders the attribute</span>
}
})
</code></pre>
<p>Only if the disabled property is set to true does the attribute become visible and render its property content.</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">disabled</span>&gt;</span>You can't click this button<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>This is something to keep in mind when working with such attributes.</p>
<p>Another thing to consider is that bindings can include a single JavaScript expression, with some restrictions:</p>
<ul>
<li>only expressions are allowed</li>
<li>only a single expression</li>
<li>no statements</li>
<li>no flow control tools, but the ternary operator works.</li>
</ul>
<p>If you want to read more, visit the documentation 
here:[https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions]</p>
<p>So far we have seen only two Vue directives, v-html and v-bind. But there are a number of directives available, and here are some more (to list just a few):</p>
<ul>
<li>v-html</li>
<li>v-bind</li>
<li>v-if</li>
<li>v-else-if</li>
<li>v-else</li>
<li>v-for</li>
<li>v-on
All directives have a v- prefix, but there is shorthand for v-bind (:) and v-on (@). </li>
</ul>
<p>They work in the same way. Here's a quick reference for the v-bind and v-on directives: </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Long syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:href</span>=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Long syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Shorthand for v-on</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Long syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Long syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Shot syntax with dynamic arguments --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> @<span class="hljs-attr">:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Now let's see what dynamic arguments are and how they work.</p>
<h3 id="heading-dynamic-arguments-in-vue">Dynamic arguments in Vue</h3>
<p>Directives have been able to have dynamic arguments since Vue 2.6.0. You can use a JavaScript expression in the directive argument if you wrap it inside square brackets.</p>
<p>But there are some restrictions:</p>
<ul>
<li>expressions should evaluate to a string</li>
<li>spaces and quotes are invalid</li>
</ul>
<p>Let's see a practical example</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:</span>[<span class="hljs-attr">attribute_name</span>]=<span class="hljs-string">"url"</span>&gt;</span>Visit my Website<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>Inside your data object you can define the directive arguments as if they were properties, where the property value is the name of your HTML attribute like <code>hef</code> in the following example:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">attribute_name</span>: <span class="hljs-string">'href'</span>,
        <span class="hljs-attr">url</span>: <span class="hljs-string">'https://fabiopacifici.com'</span>
    }
})
</code></pre>
<p>The code above renders the attribute name <code>href</code> and its value dynamically when you bind it using the v-bind directive.</p>
<p>The result will be this:  </p>
<pre><code class="lang-htm">&lt;a href="https://fabiopacifici.com"&gt;Visit my Website&lt;/a&gt;
</code></pre>
<h3 id="heading-dynamic-events-in-vue">Dynamic events in Vue</h3>
<p>You can apply the same concept to event directives like v-on. This directive does the job of the JavaScript event listener. </p>
<p>v-on accepts an argument like click, for example <code>v-on:click="doSomething"</code>.</p>
<p>To apply the concept of dynamicity, let's create a v-on directive and use the square brackets after it to specify a dynamic event.</p>
<p>Inside the index.html file you will place the following code:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">v-on:</span>[<span class="hljs-attr">event_name</span>]=<span class="hljs-string">"runFunction"</span>&gt;</span>Some link<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>
</code></pre>
<p>Let's break down the code above. </p>
<p>First you have your root element, the <code>div</code> with an <code>id</code> of <code>app</code>. Inside the root element you add an anchor tag <code>&lt;a&gt;Some link&lt;/a&gt;</code>. </p>
<p>The anchor tag has a <code>v-on</code> directive in it. After the directive you specify a dynamic argument <code>v-on:[event_name]</code> where <code>event_name</code> will be a property inside your Vue instance that you can change as you need. </p>
<p>The v-on directive works like any event listener, so between quotes you need to specify the name of the function that you want to run when the event is triggered, therefore <code>runFunction</code>.</p>
<p>Now, inside your main.js file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
    <span class="hljs-attr">event_name</span>: <span class="hljs-string">"click"</span>
    },
    <span class="hljs-attr">methods</span>: {
        runFunction() {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"test click function"</span>);
        }
    }
})
</code></pre>
<p>Let's review what the code above does. </p>
<p>First, you create the Vue instance. Then you add the <code>event_name</code> property inside the data object and you assign to it a value of <code>click</code>. This is the event you will listen for.</p>
<p>Finally, we said that the v-on directive runs a function when the event is triggered, therefore you need to write a method inside your Vue instance. So inside the methods object, create a new function called <code>runFunction</code> that will simply output a message inside the console. </p>
<p>The power of dynamic events is clear when you replace the value of the <code>event_name</code> property with a different event name.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/LICvNmhsTEs" 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-methods-in-vue">Methods in Vue</h2>
<p>So far we've learned how to bind data using the v-bind directive inside your template. In the next section, you will learn more directives – but before diving into that, let's quickly talk about how to store your functions. </p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/5-Methods/">Repository here</a> </p>
<p>And you can see the video version of this section on <a target="_blank" href="https://youtu.be/dESmaEvkZ2I">YouTube</a> if you want to review what you learn.</p>
<p>Since you are working in a big object, the Vue instance function will take the name of the methods. And as you might guess, the Options object has a property called <code>methods</code> where you can store your functions as you do for your data. </p>
<p>Inside your Vue instance, define a method that you can call anything you like – just remember to use a naming convention that clearly describes your code.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">firstName</span>: <span class="hljs-string">"Fabio"</span>,
        <span class="hljs-attr">lastName</span>: <span class="hljs-string">"Pacific"</span> 
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-comment">// es6 syntax</span>
        getFullName(){
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.firstName + <span class="hljs-string">" "</span> + <span class="hljs-built_in">this</span>.lastName;
        }
        <span class="hljs-comment">// es5 syntax</span>
    <span class="hljs-comment">/* getFullName: function(){

        } */</span>
    }
});
</code></pre>
<p>In the code above, you created a method inside the methods object. You called it <code>getFullName</code>. Inside a method, you have access to the <code>this</code> keyword that refers to the object instance, so you can use it to access from a method the properties stored in the data object.</p>
<p>When you call the method <code>getFullName</code> the method will return a single string that contains both the first and the last name. </p>
<p>Now inside your HTML file, you can simply call the method as you did when you needed to access properties in the data object <code>{{ getFullName() }}</code> </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{ getFullName() }}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that you know how to create a method and where to put it in the Vue instance, let's move forward and learn more about directives.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/dESmaEvkZ2I" 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-conditionals-in-vue-v-ifv-else-ifv-elsev-show">Conditionals in Vue (v-if/v-else-if/v-else/v-show)</h2>
<p>Now it's time to learn more about directives. We will start by looking at how conditionals work in VueJS. The first directive of this section is the <code>v-if</code>, which allows you to render blocks of code based on a certain condition. </p>
<p>You can click to view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/6-Conditionals/]">Repository here</a>.</p>
<p>And you can click to view the video on <a target="_blank" href="https://youtu.be/VNaCsloA1ZU">YouTube here</a> or use it to review at the end of the section.</p>
<p>Like the <code>if-else</code> statements in vanilla JavaScript, the v-if will check if the returned value of a conditional expression evaluates to true. If so it will render the HTML element and everything you place inside it. </p>
<p>Since it's a directive, it works on a single element (HTML tag). If you want to extend its behaviour on multiple elements, then you need to wrap them inside a <code>&lt;template&gt;</code> tag.</p>
<p>The v-if directive works in the same way as the v-bind directive works: it has access to the properties in the data object and accepts an expression between its quotes. </p>
<p>If the returned value of the expression or the value of the data property you use evaluates to <code>true</code>, then the directive renders the HTML element. Otherwise it doesn't.</p>
<p>Of course, you can check for multiple conditions and end up rendering an element if none of these evaluates to true. You do that using v-if together with the v-else-if and v-else directives.</p>
<p>Let's see a simple example and write some code inside your main.js file to show or hide an element.</p>
<p>The first thing to note is that if you have a property that returns a boolean value, it is enough to use it inside the v-if directive to show/hide an element, like so: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"showTitle"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
</code></pre>
<p>And a vue instance with a showTitle property set to true.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">showTitle</span>: <span class="hljs-literal">true</span>,
    }
})
</code></pre>
<p>In such a case, you are saying show the title property only if the value of <code>showTitle</code> is <code>true</code>. If you change it to false, the title won't show.</p>
<p>You can put a simple expression inside the quotes of a v-if directive that once computed evaluates to a boolean.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt;= 18"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
</code></pre>
<p>Inside your main.js file</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: <span class="hljs-number">18</span>,
    }
})
</code></pre>
<p>In the code above we wrote an expression on the v-if directive that checks if the <code>age</code> property is greater than or equal to 18. If the result is true then the <code>h2</code> will be shown onto the page.</p>
<p>Now let's move to a more complex example and add another condition using the v-else-if.</p>
<h4 id="heading-v-ifv-else-if">v-if/v-else-if</h4>
<p>In the following example, you will first create a v-if condition similar to the one above – but this time you'll check if the user is over 18 but under 21 using the <code>&amp;&amp;</code> operator. </p>
<p>If true, then you'll show the time with an additional note. If false, and the user is over 21, then we will simply show the title of the movie. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt; 21"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"age &gt; 18 &amp;&amp; age &lt; 21"</span>&gt;</span> {{ movieTitle }} | Watch with an adult<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
</code></pre>
<p>Inside the Vue instance you could have an <code>age</code> property. But to make your simple program dynamic, you can instead use a prompt to ask the user their age. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> userAge = <span class="hljs-built_in">Number</span>(prompt(<span class="hljs-string">"What's your age?"</span>))
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: userAge,
    }
})
</code></pre>
<p>So the code here first asks the user their age, then stores the result as a number inside the variable <code>userAge</code>. </p>
<p>You'll later use the <code>userAge</code> variable inside the data object to assign a value to the <code>age</code> property so that based on its value you will render one element or the other.</p>
<p>Let's move forward and use the v-else directive to show a different message in case the user is under 18.</p>
<h4 id="heading-v-else-directive">v-else directive:</h4>
<p>The <code>v-else</code> directive works differently. You don't have to pass it anything. It simply enters into action when none of the previous conditions evaluate to a true value.</p>
<p>So the new HTML element is fairly simple:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"age &gt; 21"</span>&gt;</span>{{movieTitle}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"age &gt; 18 &amp;&amp; age &lt; 21"</span>&gt;</span> {{ movieTitle }} | Watch with an adult<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">v-else</span>&gt;</span> Sorry You are too young to see this movie<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>
</code></pre>
<p>Here we have a <code>p</code> tag with a v-else directive attached. As you can see, it looks like an attribute without values (like the disabled or required HTML attributes).</p>
<p>Your JavaScript file has not changed.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> userAge = <span class="hljs-built_in">Number</span>(prompt(<span class="hljs-string">"What's your age?"</span>))
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">movieTitle</span>: <span class="hljs-string">'Shining'</span>,
        <span class="hljs-attr">age</span>: userAge,
    }
})
</code></pre>
<p>That's all you need to know about conditional rendering to be able to move forward with your first project. But if you want to learn more, here is the documentation: [https://vuejs.org/v2/guide/conditional.html]</p>
<p>You need to learn a few more things before you'll be able to build your first project, which is a simplified Twitter clone. The next topic is about Loops.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VNaCsloA1ZU" 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-loops-in-vue">Loops in Vue</h2>
<p>Let's go back to the previous example and learn how to use the v-for directive to output each project of the array onto the page. </p>
<p>You can click to view the Repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/7-loops/">here</a></p>
<p>And you can click to view the YouTube-Video <a target="_blank" href="https://youtu.be/aViHg80-7Bs">here</a>, or you can find it at the end of this section to review what you've learned.</p>
<p>For our next task, it would be useful if we could use a loop, and the v-for directive is here to help.</p>
<p>Its syntax doesn't have much in common with a classic <code>for</code> loop in JavaScript, but rather with a Python <code>for in</code> loop or with the <code>for in</code> JavaScript loop used to iterate over objects.</p>
<p>With this directive, you specify the elements of the array and the single element between quotes using the syntax <code>project in projects</code>. Here, projects are the property inside the data object that contains an array of objects, and project is the single element of the array. </p>
<p>You can call this as you like, but keep in mind that what follows the <code>in</code> keyword must be an iterable from your data object while what comes before can be anything you like to refer to each element of the iterable. </p>
<p>In your case, project seems the most appropriate choice since you have an array of projects.</p>
<p>Your JavaScript file will look like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],

    }

});
</code></pre>
<p>Now inside the HTML file, let's use the v-for to render the title of each project.</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{name}} {{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>{{project.title}}<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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the code above, you used <code>{{name}} {{title}}</code> to render the main title for your portfolio. Then you used the v-for directive and specified inside the quests that you want to assign each element of the iteration to a project variable <code>v-for="project in projects"</code>.</p>
<p>Now on each iteration, the <code>project</code> variable holds an object from which you can retrieve its properties using dot notation like so: <code>{{ project.title }}</code>.</p>
<p>One thing to note is that the v-for directive also gives you access to the index of the element at each iteration. You can store it in a variable as you did with the single element you called project. </p>
<p>To do that, you need to wrap them between parentheses and separate the element and its index with a comma, like so <code>v-for="(project, index) in projects"</code>. </p>
<p>Also, note that when working with objects Vue can show an alert to inform you that the use of a key is recommended. This means that it expects a key to identify each element when it's rendered. </p>
<p>You can do this using the <code>key</code> attribute and bind it, for instance to an id property on the object or to another different property, like so</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{name}} {{title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"project.title"</span>&gt;</span>{{project.title}}<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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Here you used the v-bind shorthand directive to bind the key attribute to the project.id property if it exists or to another property if not. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"portfolio"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"VueJS"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"grocery shop"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"blog"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"automation script"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"Python"</span>]},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"eCommerce"</span>, <span class="hljs-attr">languages</span>: [<span class="hljs-string">"HTML"</span>, <span class="hljs-string">"CSS"</span>, <span class="hljs-string">"PHP"</span>]},
        ],

    }

});
</code></pre>
<p>v-for can also be used to iterate over objects. In such a case, you have access to the value, the key, and also the index like so <code>v-for="(value, key, index) in object"</code> where 'object' is a property in the data object.</p>
<p>If you want to learn more, visit the documentation here: [https://vuejs.org/v2/guide/list.html]</p>
<p>Let's now move on to another important feature of Vue: how to handle user inputs and events. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/aViHg80-7Bs" 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-how-to-handle-user-input-with-event-handling-v-on-in-vue">How to Handle User Input with Event Handling (v-on) in Vue</h2>
<p>To make the application react to a user's input, Vue provides a straightforward directive called <code>v-on</code>. This is one of the directives that accepts arguments, similar to the v-bind directive. </p>
<p>With such a directive, it's easy to listen for events triggered by a user.</p>
<p>The v-on directive lets you run a function that executes a block of code when the user performs an action, like when they click on a button, hover on an element, or press a specific key on the keyboard.</p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/8-user-inputs-events-handling/">Repository here</a>.</p>
<p>And you can view the video on <a target="_blank" href="https://youtu.be/9_U1eagqOJY">YouTube here</a> or at the end of this section to review what you've learned.</p>
<h3 id="heading-v-on-syntax-and-events">V-on Syntax and Events</h3>
<p>There are two types of syntax we can use, the long-form or the short. They're equivalent, so pick the one you prefer. What follows is just a representation of the syntax, and I'll explain it in detail in a minute.</p>
<p>Long syntax: <code>v-on:EventName='doSomething'</code>
Shot syntax: <code>@EventName='doSomething'</code></p>
<p>There are many events you can listen to, such as: </p>
<ul>
<li>click</li>
<li>submit</li>
<li>mouseover</li>
<li>mouseenter</li>
<li>mouseleave</li>
<li>keyup</li>
<li>keydown</li>
<li>keypress</li>
</ul>
<p>But you can also create custom events (which you will see when you reach the components section).</p>
<p>Let's pick the long-form syntax: <code>v-on:EventName='doSomething</code>. I'll explain it more now.</p>
<p>First, you have the directive <code>v-on</code>. Then you have an argument that is the event name you want to listen for, like <code>click</code>. After that, the <code>doSomething</code> can be any method that you have defined inside the methods object of the Vue instance.</p>
<p>This method is like any other function that you define inside a JavaScript object. It can have parameters or not. If it has them, you can call the method and pass parameters to it as usual like this: <code>doSomething(param, param_2, param3)</code>. </p>
<p>You can have something like this <code>&lt;div v-on:click="likeProject"&gt;Like&lt;/div&gt;</code> and when the user clicks on this element, it will trigger a method and run some code to increase a like counter inside a project.</p>
<p>Let's first create the HTML you need for that:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(project,index) in projects"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<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>Like
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"likeProject(index)"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        {{project.likes}}
    <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>
</code></pre>
<p>In the code here, first you'll use the v-for directive to loop over the array of projects. Note that you should use the syntax <code>(project, index) in projects</code> because you will need to pass the index to the like method that you defined earlier. </p>
<p>After that, you output some data onto the page (like the project name in uppercase letters) then the description, and a <code>div</code> tag with an icon for the likes (remember to add font awesome to get the icon). </p>
<p>On the heart icon, add the directive v-on using the short syntax <code>@click="likeProject(index)"</code> between quotes that you used to invoke your <code>likeProject(index)</code> method. Then pass to it the index as a parameter so you can find the current project the user clicked on. </p>
<p>Finally, you'll render the likes onto the page for the current project using the <code>{{project.likes}}</code> syntax.</p>
<p>Now it's time to go in the Vue instance and write your method.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
        <span class="hljs-attr">methods</span>: {
            likeProject(index){
                <span class="hljs-keyword">const</span> project = <span class="hljs-built_in">this</span>.projects[index]
                project.likes++
                <span class="hljs-built_in">console</span>.log(project.likes)
            }

    }
});
</code></pre>
<p>As I said earlier, you needed to define a method to call when the user clicks on a link. So you create the <code>likeProject</code> method, which accepts a parameter that will be the index of the element the user clicked on. </p>
<p>You can then add a likes property inside your projects array and access it for the current project to increment its value every time the user clicks on your link.</p>
<h3 id="heading-how-to-access-the-original-event">How to access the original event</h3>
<p>If for any reason you need to access the original DOM event, you could have used the special <code>$event</code> variable inside the method like this on the v-on directive: <code>doSomething(param1, param2, $event)</code>. Let's see an example of that now.</p>
<p>You need to add the special variable in the method call on your v-on directive like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"likeProject(index, $event)"</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
</code></pre>
<p>Then you can access the original event inside your method like so: </p>
<pre><code class="lang-js">likeProject(index, event){
    <span class="hljs-built_in">console</span>.log(event); <span class="hljs-comment">// get the original event</span>
    <span class="hljs-keyword">const</span> project = <span class="hljs-built_in">this</span>.projects[index]
    project.likes++
    <span class="hljs-built_in">console</span>.log(project.likes)
}
</code></pre>
<p>Now that you know how the v-on directive works, let's improve our Likes example and put something more in it. We will use key modifiers in the next example, so let's quickly see what they are and what you can do with them.</p>
<h3 id="heading-event-modifiers-in-vue">Event Modifiers in Vue</h3>
<p>With events, Vue provides access to a number of Event modifiers. They are divided into 4 main groups. You can add these modifiers to a directive to change the way your event behaves. They are like postfix and you can chain them using dot notation. </p>
<p>Below there is a quick reference.</p>
<p>Categories:</p>
<ul>
<li>event modifiers</li>
<li>key modifiers</li>
<li>system modifiers keys</li>
<li>mouse buttons modifiers</li>
</ul>
<p>Event Modifiers:
.stop
.prevent 
.capture
.self
.once
.passive</p>
<p>Key Modifiers:
You can add these modifiers to the @keyup listener to listen for when these keys are pressed or use them as a combination with the @click event to listen for a click+space, for example. <code>@click.enter="doSomething"</code></p>
<p>.enter
.tab
.delete (captures both “Delete” and “Backspace” keys)
.esc
.space
.up
.down
.left
.right</p>
<p>System modifiers:
With these modifiers, you can trigger mouse or keyboard event listeners when the corresponding key is pressed.
.ctrl 
.alt
.shift
.meta
.exact (allows control of the exact combination of system modifiers needed to trigger an event)</p>
<p>Mouse buttons modifiers:
These modifiers allow you to trigger a mouse event listener if the corresponding mouse button is clicked. </p>
<p>.left
.right
.middle</p>
<p>If you want to learn more, read the docs <a target="_blank" href="https://vuejs.org/v2/guide/events.html#Event-Modifiers">here</a>.</p>
<h3 id="heading-how-to-like-a-project-with-key-modifiers">How to like a project with key modifiers</h3>
<p>In the previous example, you used the v-on:click directive to trigger a mouse event listener that aimed to simulate a like on a project. </p>
<p>But the user was able to add as many likes they wanted by clicking on the icon. </p>
<p>In the next example, you will do things a bit differently.</p>
<ul>
<li>First, you will prevent the user from adding more than one like to each project,</li>
<li>Then you will let the user remove a like </li>
<li>Finally, you will keep the likes on the page even after the user refreshes the page.</li>
</ul>
<p>Let's get started. This time you will use mouse button modifiers to listen for clicks. The left mouse button click will trigger the add like behaviour and the right mouse button click will trigger the remove behaviour.</p>
<p>Inside your HTML file: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Users can like a project with a left click and dislike it with right click --&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<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>Like 
                <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> 
                    @<span class="hljs-attr">click.left</span>=<span class="hljs-string">"addLike(project)"</span> 
                    @<span class="hljs-attr">click.right</span>=<span class="hljs-string">"removeLike(project, $event)"</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> 
               {{project.likes}}
            <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>
</code></pre>
<p>In the code above you have taken what you had before and simply added a left mouse button key modifier to the click event <code>@click.left</code>. Then you invoked the <code>addLike</code> method. This will make your project like counter increase by one as we have seen before. </p>
<p>Then you added another event listener to the same element, but this time you used the <code>.right</code> mouse button key modifier to listen when the user clicks on our icon using the right button <code>@click.right="removeLike()"</code>. </p>
<p>In the remove like method, you have also passed the special variable $event so that you can use the original event later in your method to prevent its default behaviour and open the contextual menu. </p>
<p>But we said earlier that you can also chain key modifiers and indeed there is a <code>.prevent</code> key modifier that you can use here instead of the <code>$event</code> variable. You could do the same like this: <code>@click.right.prevent="removeLike(project)"</code> </p>
<p>Let's see how to structure your main.js file:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
    <span class="hljs-attr">methods</span>: {
        addLike(project){
           <span class="hljs-built_in">console</span>.log(project)  
        },
        removeLike(project, event){
            <span class="hljs-built_in">console</span>.log(project)
            <span class="hljs-built_in">console</span>.log(event)
        }
    }

});
</code></pre>
<p>So in the data object, you have a <code>projects</code> property that is an array of objects. Each object has a likes property that you will increment or decrement depending on what mouse button the user clicks.</p>
<p>Inside the <code>methods</code> object, you created the two methods you referenced in your v-on directives <code>addLike()</code> and <code>removeLike()</code>. For now, you are only logging to the console the project parameter value and the event value. You will implement the logic in a minute.</p>
<p>Let's start with the add likes method – it could look like this:</p>
<pre><code class="lang-js">addLike(project){
    <span class="hljs-keyword">const</span> projectTitle = project.title;
    <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
        project.likes++;
        <span class="hljs-built_in">localStorage</span>.setItem(projectTitle, <span class="hljs-literal">true</span>);
    }              
}
</code></pre>
<p>There are a few things going on here. In the first line, you're storing the project title inside the <code>projectTitle</code> variable. Then, you said you wanted data to persist if you refresh the page so you are using the <code>localStorage</code> API to store information inside the client browser. </p>
<p>You increment the likes count by one, but you do that depending on a value inside the local storage. </p>
<p>You can do this by first checking to see if there is a key in the <code>localStorage</code> matching your project title, <code>if(!localStorage.getItem(projectTitle))</code>. </p>
<p>If this evaluates to false, then you will run the code inside the if block and first increment the likes <code>project.likes++</code>. </p>
<p>Second, use the <code>.setItem()</code> method of the local storage API to set a key-value pair with the project title as the key and a boolean value as its value <code>localStorage.setItem(projectTitle, true)</code>.</p>
<p>To put an item in the local storage, you'll use <code>localStorage.setItem()</code>. The set item method accepts a key-value pair. Your key will be the title you saved in the variable <code>projectTitle</code> and the value will the boolean value <code>true</code>.</p>
<p>Now let's see if this works. </p>
<pre><code class="lang-js">removeLike(project, event){
    event.preventDefault(); <span class="hljs-comment">// This can be omitted if we use the prevent key modifier</span>
    <span class="hljs-keyword">const</span> projectTitle = project.title;
    <span class="hljs-keyword">if</span>(project.likes &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
        project.likes--;
        <span class="hljs-built_in">localStorage</span>.removeItem(projectTitle);
    }
}
</code></pre>
<p>This function does the opposite of the previous. When the user clicks their right mouse button, the method <code>removeLikes()</code> is executed and you do the following:</p>
<p>First things first, you need to prevent its default behaviour. Otherwise when the user right-clicks on the icon, the contextual menu will pop up and we don't want that. So, you'll use the <code>event.preventDefault()</code> method on the original event that is represented by the <code>event</code> parameter on your method. </p>
<p>Alternatively, you can omit this if you use the prevent key modifier in the v-on directive <code>@click.right.prevent="removeLike(project)</code>.</p>
<p>The next step is to grab the project title. Since you also passed a parameter to the method to represent the current project object <code>removeLike(project, event)</code>, you can store the project title in a variable <code>projectTitle</code>.</p>
<p>Then you need to make a couple of checks. First, you want to decrement the likes only if its value is greater than zero. Then you want to make sure the project title is in the local storage as a key with a value. </p>
<p>So, in your condition, you have done both checks <code>if(project.likes &gt; 0 &amp;&amp; localStorage.getItem(projectTitle))</code>. Now, if both conditions evaluate to true, the code inside the if block can run. </p>
<p>First you remove the like by decrementing its value <code>project.likes--</code>. Then you remove the project title from the local storage using the <code>removeItem</code> method and pass to it the key you want to remove (which is the project title <code>localStorage.removeItem(projectTitle)</code>).   </p>
<p>To put it all together, you should now have the following code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">"#app"</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">name</span>: <span class="hljs-string">"John Doe"</span>,
        <span class="hljs-attr">title</span>: <span class="hljs-string">"Portfolio"</span>,
        <span class="hljs-attr">projects</span>: [
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My first project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"A simplified Twitter clone"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
            {<span class="hljs-attr">title</span>: <span class="hljs-string">"My second project"</span>, <span class="hljs-attr">description</span>: <span class="hljs-string">"Projects portfolio with GitHub"</span>, <span class="hljs-attr">likes</span>: <span class="hljs-number">0</span>},
        ]
    },
        <span class="hljs-attr">methods</span>: {
        addLike(project)
        {
            <span class="hljs-comment">//console.log(project, "like");</span>
            <span class="hljs-keyword">const</span> projectTitle = project.title;
            <span class="hljs-comment">// check if the current project is not in the local storage</span>
            <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">localStorage</span>.getItem(projectTitle)) {
                <span class="hljs-comment">// set the item in the storage and increase the likes counter</span>
                project.likes++;
                <span class="hljs-built_in">localStorage</span>.setItem(projectTitle, <span class="hljs-literal">true</span>);
            }

        },
        removeLike(project){ 
            <span class="hljs-keyword">const</span> projectTitle = project.title;
            <span class="hljs-built_in">console</span>.log(project, <span class="hljs-string">"dislike"</span>);  
            <span class="hljs-keyword">if</span>(project.likes &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">Boolean</span>(<span class="hljs-built_in">localStorage</span>.getItem(projectTitle))) {
                project.likes--;
                <span class="hljs-built_in">localStorage</span>.removeItem(projectTitle);
            }

        }
    },
    mounted(){
        <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span> {
            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(project.title) !== <span class="hljs-literal">null</span>) {
                project.likes = <span class="hljs-number">1</span>; 
            }
        });
    }

});
</code></pre>
<p>To make the code work, you also need to add a life cycle hook called mounted. This will let you run code when the root element is mounted on the Vue instance. With it, you can check if the localStorage has a key corresponding to your project title and if so, update the value of the likes counter.</p>
<p>And your HTML is still the same:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Users can like a project with a left click and dislike it with right click --&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"projects"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{project.title.toUpperCase()}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<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>Like 
            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-heart fa-lg fa-fw"</span> 
                @<span class="hljs-attr">click.left</span>=<span class="hljs-string">"addLike(project)"</span> 
                @<span class="hljs-attr">click.right</span>=<span class="hljs-string">"removeLike(project, $event)"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> 
            {{project.likes}}
        <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>
</code></pre>
<p>Remember that you can get rid of the <code>$event</code> variable passed to the <code>removeLike</code> method by using the event key modifier like so: <code>@click.right.prevent="removeLike(project)"</code></p>
<p>You have learned a lot so far! And now that you have seen key modifiers in action, we can move forward to the next topic: two-way model binding and the v-model directive. Then we'll start building our Twitter clone. </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/9_U1eagqOJY" 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-two-way-model-binding-v-model-in-vue">Two Way Model Binding (v-model) in Vue</h2>
<p>All right, so far we have seen how to bind properties from the data object to our HTML tags and inside attributes, 
how to loop over a sequence of elements and, how to display conditionally elements onto our template with conditionals. </p>
<p>We have covered how to define methods inside the methods object so that we can perform more complex operations on our data and, 
we have learned how to work with events using the v-on directive.</p>
<p>In the next section, we will look at how Vue opens a two-way communication channel between a form's input and, a property defined
inside the data object. Then we will use this knowledge to build our first project together.</p>
<p>You can check out the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/9-two-way-binding/">Repositoy here</a>.</p>
<p>And you can view the video on <a target="_blank" href="https://youtu.be/pBUXTUvDRCo">YouTube here</a> or at the end of the section if you want to review what you've learned.</p>
<h3 id="heading-how-does-the-v-model-directive-work">How does the v-model directive work?</h3>
<p>The v-model is another Vue directive. You can use it out of the box, and it's useful to simplify the way an input tag can communicate 
with a Vue instance's property in the data object. </p>
<p>It works like all the other directives. The main difference is that when it's implemented, your application will listen for changes 
inside the input with this v-model directive and update the attached property's value immediately inside the data object and vice-versa. </p>
<p>It is effectively a two-way communication channel between your template and the Vue instance. It's the Vue way to interact with user input and simplify your life as a developer.</p>
<p>Let's look at a straightforward example.</p>
<h3 id="heading-how-to-use-the-v-model-on-an-input-tag">How to use the v-model on an input tag</h3>
<p>First, you need an input tag inside the element you defined as the root element in the Vue instance, the div with an id of <code>app</code>. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">placehoder</span>=<span class="hljs-string">"What's happening today?"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>In the HTML you have an input tag, which has the v-model directive attached to it as an HTML attribute. Anything between quotes is computed as a JavaScript expression, so you write the name of a property <code>tweet</code> that you will create inside the Vue data object.</p>
<p>So let's do it.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>
    }
});
</code></pre>
<p>So now you have a Vue instance and, in it, you have a data object with a tweet property that has an empty string as its value. </p>
<p>If you open the console and inspect the Vue element, you can see the two-way data binding in action. </p>
<p>By changing the value of the tweet property, you will immediately update the value inside the input tag and vice-versa. </p>
<p>You have this <code>tweet</code> property in the data object and you already know how to render its content onto the page. So now you can update your markup and add a paragraph under the input tag to see the value dynamically changing while you type.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">placehoder</span>=<span class="hljs-string">"What's happening today?"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{tweet}}<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>
</code></pre>
<p>How cool is that? Now you can see the tweet property changing in real-time as you type. </p>
<p>That's the two-way data binding. If you change the content of the tweet property directly it will be reflected in your template, too.</p>
<p>If you want to learn more, make sure to read the official <a target="_blank" href="https://vuejs.org/v2/guide/forms.html">Documentation</a> too.</p>
<h3 id="heading-how-to-build-a-tweet-box">How to build a tweet box</h3>
<p>Now, let's raise the bar a little and build something together.</p>
<ul>
<li>We will create a simple <code>textarea</code> with a submit button, </li>
<li>We will display the number of characters the user has left while they are typing so that they can submit the form without exceeding the max number of characters allowed. </li>
<li>Like in a tweet, the maximum number of characters will be 200.</li>
</ul>
<h4 id="heading-how-to-define-the-initial-markup">How to define the initial markup</h4>
<p>Now you need to define a markup. So inside our index.html file, write the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
       <span class="hljs-comment">&lt;!-- Code here --&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- More code here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>First you have first created your root element for the Vue instance so that it can monitor the markup and do its magic.</p>
<p>Then you created a form tag with an event listener using the directive v-on, which listens for the submit event and runs a function <code>submitData</code> that you still have to create.</p>
<p>You also added the <code>.prevent</code> modifier so that the page doesn't refresh when you submit the form.</p>
<h4 id="heading-how-to-define-the-vue-instance-and-methods">How to define the Vue instance and methods</h4>
<p>Let's define our Vue instance and create the <code>submitData</code> method so that you can use it later when you need it.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-comment">// data object props here</span>
    },

    <span class="hljs-attr">methods</span>: {
          submitData(){
             <span class="hljs-comment">// Code here</span>
        }
    },

});
</code></pre>
<h4 id="heading-how-to-add-a-text-area-and-a-submit-button">How to add a text area and a submit button</h4>
<p>Now back to the HTML: let's add the text area inside your form. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
       <span class="hljs-comment">&lt;!-- Code here --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- More code here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Inside the form tag, you create a label and a <code>textarea</code> for your tweet box. 
On the <code>textarea</code> you use the directive v-model to bind the <code>textarea</code> value to a <code>tweet</code> property and vice-versa. So now when one changes the other changes, too. </p>
<p>NOTE: The v-model directive is used on form elements like inputs, text areas, check box, and more.</p>
<p>After the <code>textarea</code> you put a button of type submit so that when a user clicks it the form's data are sent to your application's <code>submitData()</code> method and you can process them.</p>
<h4 id="heading-how-to-add-properties-to-the-vue-instance">How to add properties to the Vue instance</h4>
<p>Now inside your JavaScript file, you need to create the tweet property in the data object and do something with this information so that you can later show a list of tweets sent. </p>
<p>We also said that we want to limit the characters to 200 and show an error when they're in excess.</p>
<p>So let's add a few more properties here like:</p>
<ul>
<li><code>tweet</code> for the current tweet message that the user inputs in the text area</li>
<li><code>tweets</code> for the list of tweets</li>
<li><code>max_length</code> for the characters limit</li>
</ul>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
    },

    <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-comment">/* Handle the tweet */</span>
        }
    },

});
</code></pre>
<p>So now with the tweets property as an array and using the two-day binding between the tweet property and the <code>textarea</code>, you can push all tweets inside the <code>tweets</code> array when the user submits the form by triggering the 
<code>submitData</code> method.</p>
<h4 id="heading-how-to-implement-the-character-counter">How to implement the character counter</h4>
<p>Before implementing the <code>submitData</code> method, you can show a character counter while the user types in the textarea. </p>
<p>Let's implement this feature so the user knows if they can submit the tweet or not.</p>
<p>Back in the HTML file, you can add a div with a couple of span elements and use a v-if directive to check the length of the character. It'll show the counter while the user is within the characters limit, otherwise it'll show an error message.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show character limits here --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<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>&gt;</span>
</code></pre>
<p>The code above uses two-way data binding between the tweet property and the <code>textarea</code> to find out if the user has reached the character limit that you defined as the <code>max_lenght</code> property. </p>
<p>Since the tweet property is connected to the <code>textarea</code>, you can use the <code>v-if</code> directive combined with the <code>tweet.length</code> and the <code>max_length</code> properties to make the comparison. </p>
<p>Now, every time the user types something in the <code>textarea</code>, the string saved in the <code>tweet</code> property increases by one character. Then you can use the <code>.length</code> property to see how long the whole string is and compare it against your <code>max_length</code> property.</p>
<p>You use the directive  <code>v-if="tweet.length &lt;= max_length"</code> to make your comparison. When this comparison returns true, the user will see the span tag with its content, the counter. </p>
<p>Inside the span tag, you used the moustache syntax to show the user the current length of the property <code>tweet</code> and the character limit.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<p>After the <code>v-if</code> directive, a <code>v-else</code> directive handles the error message that you show to the user when there are no characters left to use. </p>
<p>Here the content of the span element shows a message that tells how many characters there are in excess by subtracting the tweet length from the <code>max_lenght</code> property.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
</code></pre>
<h4 id="heading-how-to-submit-the-form">How to submit the form</h4>
<p>All that's left is to add the tweet to the list of tweets and show them on the page when the user submits the form.</p>
<p>Let's complete the <code>submitData</code> method so that every time it's executed it pushes a new object to the tweets array. </p>
<p>Inside the methods object the <code>submitData</code> methods now looks like this:</p>
<pre><code class="lang-js"> submitData(){
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
        <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
        <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
    } 
}
</code></pre>
<p>The method above first checks if the length of the <code>tweet</code> property is less than or equal to the <code>max_length</code> property. If the condition evaluates to true, then you can add the <code>tweet</code> content to the array using the <code>unshift</code> method (which adds it to the beginning of the array).</p>
<p>Finally, you need to clear the value of the <code>tweet</code> property. You can do this by assigning to it an empty string again.</p>
<p>NOTE that since you are inside a method, you need to use the <code>this</code> keyword to grab properties and eventually methods inside the Vue instance.</p>
<h4 id="heading-how-to-show-a-list-of-tweets">How to show a list of tweets</h4>
<p>Now, you can also show a list of tweets in your template. </p>
<p>To do that you will use a <code>v-for</code> directive and loop over the <code>tweets</code> array to show each tweet.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"text in tweets"</span>&gt;</span>{{text}}<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>
</code></pre>
<h3 id="heading-put-it-all-together">Put it all together</h3>
<p>The final code now looks like this:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJs v-model<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Fontawesome CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span>

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

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you wanna tweet about today<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Tweet form --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Alert the user  --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"my-3"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span>
                {{ ` Max: ${tweet.length} of ${max_length} characters` }}
            <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">v-else</span>&gt;</span> {{ `Max char limit reached! excess characters: ${max_length -
                tweet.length} ` }}<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-comment">&lt;!-- Tweets message --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"tweet in tweets"</span>&gt;</span>
                {{tweet}}
            <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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>Our final javascript file </p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>        
    }, 
    <span class="hljs-attr">methods</span>: {
        submitData(){
            <span class="hljs-comment">// Handle the tweet submission</span>
            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length){
                <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
            }
        }
    }
})
</code></pre>
<h3 id="heading-improvements-you-can-make">Improvements you can make</h3>
<p>If you take this bit of code from your index.html file, there is something you can do to clean up our code... </p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<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>
</code></pre>
<p>To clean this template file, we can follow two approaches that are exactly the same, except that one is cached and the other one isn't. </p>
<ul>
<li>Computed Properties (cached)</li>
<li>Methods (not cached)</li>
</ul>
<p>In the next section, we will learn what computed properties are and how they differ from methods.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/pBUXTUvDRCo" 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-computed-properties-and-methods">Computed Properties and methods</h2>
<p>You should use computed properties instead of in-template expressions for complex logic that has the scope of changing the presentation of our data, not the data itself. </p>
<p>If we need to change the data, then you should use methods instead. Computed properties are cached based on their dependencies, meaning that it will re-evaluate only when its dependencies have changed. </p>
<p>With computed properties, the result of the previously run function is returned if the dependencies have not changed. </p>
<p>You can view the <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/10-computed-properties/">repository here</a> and watch the video on <a target="_blank" href="https://youtu.be/VxFT6cgTHhw">YouTube here</a>. The video is also listed at the end of this section so you can review what you've learned.</p>
<p>In the following example, we will use computed properties but we could also use methods. Generally speaking, we use computed properties when we have an expensive operation that we want to execute and cache so that the next time we don't have to run it again unless something has changed.  </p>
<p>Let's implement a computed property for the following messages. Our HTML file will now change from this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ `Max: ${tweet.length} of ${max_length} characters` }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{`Max char limit reached! excess chars: ${max_length - tweet.length}`}}<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>
</code></pre>
<p>to this much cleaner version:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show the max char messages --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage}}<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>
</code></pre>
<p>We have replaced the contents of both spans with two new properties that will be placed as methods inside our computed object.</p>
<p>Now inside our Vue instance, we will create a new object called <code>computed</code> where we will define two methods that will return the messages we had before. </p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>
    },
    <span class="hljs-comment">// Computed Properties</span>
 <span class="hljs-attr">computed</span>: {
        <span class="hljs-attr">maxCharsText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.tweet.length}</span> of <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length}</span> characters`</span>;
        },
        <span class="hljs-attr">errorMessage</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max char limit reached! excess chars: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length - <span class="hljs-built_in">this</span>.tweet.length}</span>`</span>
        }
    },
    <span class="hljs-comment">// Methods</span>
 <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
                  <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                  <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
              } 
        }
    },

});
</code></pre>
<p>The first method <code>maxCharsText</code> returns exactly the same string we had before inside our HTML file. The only difference is that we are using the keyword <code>this</code> to reference the properties we needed to grab inside the Vue instance <code>this.tweet.length</code> and <code>this.max_length</code>. </p>
<p>The second method works in the exact same way and it also uses the keyword <code>this</code> to pick the properties defined in the Vue instance <code>this.max_length</code> and <code>this.tweet.length</code>.</p>
<h3 id="heading-all-together">All together</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>What do you want to tweet about today?<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"submitData"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"80"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Next<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText }}
        <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage}}<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">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"text in tweets"</span>&gt;</span>{{text}}<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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>JavaScript file</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">tweets</span>: [],
        <span class="hljs-attr">tweet</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">200</span>,  
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>
    },
    <span class="hljs-comment">// Computed Properties</span>
 <span class="hljs-attr">computed</span>: {
        <span class="hljs-attr">maxCharsText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.tweet.length}</span> of <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length}</span> characters`</span>;
        },
        <span class="hljs-attr">errorMessage</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">`Max char limit reached! excess chars: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.max_length - <span class="hljs-built_in">this</span>.tweet.length}</span>`</span>
        }
    },
    <span class="hljs-comment">// Methods</span>
 <span class="hljs-attr">methods</span>: {
          submitData(){
              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tweet.length &lt;= <span class="hljs-built_in">this</span>.max_length) {
                  <span class="hljs-built_in">this</span>.tweets.unshift(<span class="hljs-built_in">this</span>.tweet);
                  <span class="hljs-built_in">this</span>.tweet = <span class="hljs-string">""</span>;
              } 
        }
    },

});
</code></pre>
<p>If we want to use methods instead of computed properties, we can simply move both methods from the <code>computed</code> object inside the <code>methods</code> object and invoke them with parenthesis inside our HTML file like so:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweet.length &lt; max_length"</span>&gt;</span> {{ maxCharsText() }}
    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"errorMessage"</span> <span class="hljs-attr">v-else</span>&gt;</span>{{errorMessage() }}<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>
</code></pre>
<p>Just remember that computed properties are cached while methods are not. 
If you want to learn more make sure to read the official <a target="_blank" href="https://vuejs.org/v2/guide/computed.html">documentation</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/VxFT6cgTHhw" 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-how-to-create-a-simple-twitter-clone">How to Create a Simple Twitter Clone</h2>
<p>Now, let's put together everything we've learned so far and and build our very first project. It will be a minimalist and simplified twitter-like web application. </p>
<p>We want to create a simple application that has some kind of registration form, a box to add new tweets, and a section where we can show all tweets. We also want to be able to remove tweets.</p>
<p>All data must be persistent so that after the page is refreshed the list of tweets is still visible while the registration form will be hidden.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/v1j_bDDd6jI">YouTube here</a> or at the end of this section if you want to review.</p>
<p>You can also view the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/11-project-simple-twitter-cone/">here</a>.</p>
<h3 id="heading-define-our-tasks">Define our tasks</h3>
<p>Let's break this down into large tasks first. Then we will see what we need to do to complete each one. </p>
<ul>
<li>create a registration form</li>
<li>create a tweets box form</li>
<li>create a tweets section</li>
</ul>
<p>To do our project we need to research what tools we need to use to achieve our goals, so lets put them down:</p>
<ul>
<li>VueJS (application logic)</li>
<li>localStorage (make data persistent)</li>
<li>font awesome (icons)</li>
</ul>
<p>This application has no database so we are unable to record multiple users and their tweets. It's just a proof of concept, something we build to use our new knowledge.</p>
<h3 id="heading-create-the-project-structure">Create the project structure</h3>
<p>Now that we know what to do, let's start by creating our project structure and importing the tools we need to complete the first task, the registration form.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Simple tweetter clone<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- CDN Fontawesome --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span> /&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Style sheet --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>

    <span class="hljs-comment">&lt;!-- Add a tweet --&gt;</span>

    <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Link our main.js file --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>Now that our HTML file is ready, let's create the main.js file and create a Vue instance.</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {

    },
    <span class="hljs-attr">methods</span>: {

    }

});
</code></pre>
<p>Finally, we need to create a style.css file that we will place for now in the root folder of our project. </p>
<p>We will use a CSS file that I have already written, and you can download it from <a target="_blank" href="https://bitbucket.org/fbhood/simple-tweet-app/src/master/style.css">here</a>.</p>
<p>OK, our basic structure is ready to go. Inside our HTML file, we have some comments that reflect our 3 main tasks: create a registration form, create an add tweet box and, show a list of tweets. </p>
<p>Let's start with the first task and simulate a registration form.</p>
<h3 id="heading-how-to-simulate-a-registration-form-html">How to simulate a registration form - HTML</h3>
<p>Inside the root element <code>&lt;div id="app"&gt;&lt;/div&gt;</code> we need to create a registration form with the following fields: name, email, password, and a submit button. The form is contained in a card so we will wrap everything in a div and assign to it a card class. </p>
<p>The form won't submit data to a server, but will simply simulate a registration and update some property in the data object of the Vue instance.</p>
<p>We will place the following code inside our HTML file:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"25"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"16"</span> <span class="hljs-attr">required</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Let's break this down. First, the form tag has a <code>v-on</code> directive with a <code>submit</code> argument so it will listen for a submit event. It also has an event modifier <code>.prevent</code> so when we hit the submit button the page doesn't refresh. </p>
<p>Inside the <code>v-on</code> directive there is a method called <code>registerAccount</code> that we need to create inside the methods of the Vue instance.</p>
<p>Inside the form, we have the three input fields: name, email, and password with labels. We wrapped each field inside a div with a class of <code>form_group</code>. Later we can replicate the style of the Twitter registration fields and show a character counter. </p>
<p>Each input field has a <code>v-model</code> directive that binds the input to its data property. </p>
<h3 id="heading-how-to-simulate-a-registration-form-vue">How to simulate a registration form - Vue</h3>
<p>Let's move on to the Vue instance to make the binding between the form and the data object properties.</p>
<p>Here we also need a place where we can store the details that the user submits so we will create another property for that. </p>
<p>Looking at the input fields, we also put a <code>maxlength</code> property on them, which is 25 for the name and email and 16 for the password. </p>
<p>Like we did previously when we learned about the v-model, we can create a property for these limits so that we can use it to show the user how many characters the user has left. </p>
<p>The Vue instance will look like this:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {}
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>
    },
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-comment">// record user details</span>
            <span class="hljs-comment">// add registration to localStorage</span>
            <span class="hljs-comment">// clear the registration fields</span>

        }
    }

});
</code></pre>
<p>Let's break this down. First inside the <code>data:{}</code> object, we defined an object to store and retrieve the <code>userData</code>. Then we added the properties <code>name</code>, <code>email</code>, and <code>password</code> to make the two-way data binding work. 
Finally we added the <code>max_length</code> and <code>max_pass_length</code> propeties.</p>
<h3 id="heading-how-to-show-input-character-counter">How to show input character counter</h3>
<p>OK, now that we have a binding between the input fields and our properties, we can show a character counter to the user while they're typing. </p>
<p>This is fairly simple – all there is to do is to show the length of each input property and compare it with the max length properties we have set in the Vue instance.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>So here we created a string using an in-template expression. We've shown the length of the <code>name</code> property and the value of the <code>max_length</code> so that while the user types we show something like this: 13/25. </p>
<p>We've also used a <code>v-bind</code> directive on the <code>maxlength</code> attribute so that its value is bound to the value of the property we defined in the Vue instance. So in case we want to change it we can do so in one place.</p>
<p>We will do the same for the other fields. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<h3 id="heading-how-to-add-the-logic-to-the-registeraccount-method">How to add the logic to the <code>registerAccount</code> method</h3>
<p>Now it's time to work on the form submission logic. We will simply populate the object stored in the property <code>userData</code> when the user submits the form. </p>
<p>Inside the <code>registerAccount</code> method we will add the details the user passes and build our object. </p>
<pre><code class="lang-js"> registerAccount(){
            <span class="hljs-comment">// record user details</span>
            <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
            <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
            <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            <span class="hljs-comment">// add registration to localStorage</span>

            <span class="hljs-comment">// clear the registration fields</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }
</code></pre>
<p>Here we have taken the value of the properties name, email, and password and assigned them to properties we created in the <code>userData</code> object.</p>
<p>This seems fine mostly because we have put on our input fields the <code>required</code> attribute – but if we remove it we will be able to submit an empty form, and we don't want that. </p>
<p>So let's add a very basic form of validation to at least check if the user has typed something inside our form, otherwise we show an error. </p>
<p>To do this, we need to add an if block inside the method and also an error property to the data object. Our file now looks like this:</p>
<pre><code class="lang-js">

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,

    },  
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) 
            {
                <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
            }

        <span class="hljs-comment">/* Add registration data to the local storage */</span>


        <span class="hljs-comment">/* Clear the registration inputs */</span>
        <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    }

});
</code></pre>
<p>Here in the <code>registerAccount</code> we've written a conditional that checks if the length of the name property is not empty, if the email property isn't empty, and if the password isn't empty  <code>this.name !== "" &amp;&amp; this.email !== "" &amp;&amp; this.password !== ""</code>. </p>
<p>If all these checks evaluate to a true value, then we run the code inside the block. Otherwise we run the code in the <code>else</code> block that updates the value of the <code>error</code> property that we will now use in our template to show the error message. </p>
<p>We also added a new property <code>usersID: 0,</code> that we used inside the if block to assign an id property to the <code>userData</code> object, just to make our application more realistic. But of course it is useless, as we will not have a database where we store all user details. We'll just store a single user inside their browser's local storage.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>


<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now our form is complete and we're also displaying an error message to the user if the required attributes are removed from our markup. </p>
<p>But our data do not persist and when the page is refreshed –everything's all gone. Let's tackle that using the localStorage API.</p>
<p>Inside our Vue instance, we need to set an item in the local storage. But we also need to save the entire <code>userData</code> object so that later we can use its data to display a message to the registered user.</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Add registration data to the local storage */</span>
<span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
<span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
<span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))
</code></pre>
<p>Here we use the <code>setItem</code> method of the Local Storage API to add an item to the local storage so that later we can use it to check if the user is registered or not. </p>
<p>Then we also need to store the entire <code>userData</code> object as a string. To do that we use the <code>JSON.stringify</code> method that will turn the object into a JSON string that can be saved in the localStorage.</p>
<p>Our JS file is now as follows:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    },  
    <span class="hljs-attr">methods</span>: {
        registerAccount(){
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name !== <span class="hljs-string">""</span>  &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {
                <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password

            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
            }

        <span class="hljs-comment">/* Add registration data to the local storage */</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
        <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
        <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))


        <span class="hljs-comment">/* Clear the registration inputs */</span>
        <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
        <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    }

});
</code></pre>
<p>Now when the user visits our application page, we need to check inside the browser's local storage to see if there is a key called <code>simple_tweet_registered</code>. If there is, we can assume that the user is registered and we can show the next section, the tweet box. Otherwise, we show the registration form. </p>
<p>We will do that by creating a <code>registered: false</code> property in the data object and use it to display or hide the registration form.</p>
<pre><code class="lang-js">
<span class="hljs-attr">data</span>: {
    <span class="hljs-attr">userData</span>: {},
    <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,      
}
</code></pre>
<p>Wrap the form around a div with a directive <code>v-if="!registered"</code> like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
    // here goes the form
<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">v-else</span>&gt;</span> Tweetbox <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our final HTML file now looks like this:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">form</span>&gt;</span>


            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<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-comment">&lt;!-- Add tweet --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome username_here write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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>
</code></pre>
<p>Now to make this work, we will use the lifecycle hook we created which lets us inject our code when the Vue instance has been created. This is because we want to check this before mounting our root element. </p>
<p>So let's add a life cycle hook to the Vue instance. We will check if our key is there, and if so we will update the value of the <code>registered</code> property to <code>true</code>. </p>
<p>We have also stored the full <code>userData</code> object in the local Storage so we can use it to repopulate the <code>userData</code> object when the page is refreshed with the details the user submitted.</p>
<pre><code class="lang-js">created(){
    <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
        <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
    }
    <span class="hljs-comment">// repopulate the userData object</span>
     <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

}
</code></pre>
<p>To turn a JSON string back into an object, we can use the <code>JSON.parse</code> method.</p>
<p>Now it's all set for the next task – show a tweet form to the user after registration.</p>
<p>Our code so far looks like this:</p>
<p>The main.js file:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }

    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }
        <span class="hljs-comment">// repopulate the userData object</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

    }

});
</code></pre>
<p>And the HTML inside the <code>app</code> element:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">form</span>&gt;</span>


        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<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-comment">&lt;!-- Add tweet --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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>
</code></pre>
<p>Here in the HTML, since we used the <code>v-else</code> on the add tweet section and used the local storage to retrieve the data submitted by the user, we can use an in-template expression to grab the user name so that we can output a welcome message. </p>
<p>In the next section, we will create a tweet box form so that after the registration the user can write a tweet.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/v1j_bDDd6jI" 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>
<h3 id="heading-how-to-create-a-tweet-box-form-html">How to create a tweet box form - HTML</h3>
<p>Now it's time to build our add tweet form. We did something very similar earlier in this article, but now we will need to store and make the data persistent. This lets us show a list of tweets even when the page refreshes.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"sendTweet"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"tweet"</span>&gt;</span>
                Send your tweet
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ tweetMsg.length + '/' + max_tweet }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>This is nothing new to us now. Inside the <code>tweetBox</code> element we add a form with the usual v-on directive and a method  <code>sendTweet</code> that we will need to define inside the methods object. This will take the tweet and save it somewhere, maybe in a property in the data object.</p>
<p>Inside the form, there is a <code>textarea</code> that has a <code>v-model</code> directive that binds it to a <code>tweetMsg</code> property that we need to create.</p>
<p>Finally, a submit button.</p>
<p>We also have a span inside the tweet label that shows a character counter to the user as we did before in the registration form. </p>
<p>Here we have a new property <code>max_tweet</code> that is used to show the limit and the <code>tweetMsg.length</code> is used to show the current number of the characters inserted.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/xFwfrIciFt0">YouTube here</a> if you want to review what you've learned.</p>
<h3 id="heading-create-a-tweets-box-form-vue">Create a tweets box form - Vue</h3>
<p>Let's go to the Vue instance and add the properties and the <code>sendTweet</code> methods.</p>
<p>Our data object now has three more properties, the <code>max_tweet</code> set to <code>200</code>, the <code>tweetMsg</code> that binds to the <code>textarea</code>, and a <code>tweets</code> array that we will use to store all tweets the user sends.</p>
<pre><code class="lang-js">data: {
    <span class="hljs-attr">userData</span>: {},
    <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
    <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">max_tweet</span>: <span class="hljs-number">200</span>, <span class="hljs-comment">// max tweets lenght</span>
    <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">tweetMsg</span>: <span class="hljs-string">""</span>, <span class="hljs-comment">// current tweet</span>
    <span class="hljs-attr">tweets</span>: [] <span class="hljs-comment">// list of tweets</span>
}
</code></pre>
<p>Inside the methods, we have a new method that will be invoked by the v-on directive when the form is submitted:</p>
<pre><code class="lang-js">
sendTweet(){
    <span class="hljs-comment">/* Store the tweet in the tweets property */</span>
    <span class="hljs-built_in">this</span>.tweets.unshift(
        {
            <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
            <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
        }

    );
    <span class="hljs-comment">/* Empty the tweetMsg property */</span>
    <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;
    <span class="hljs-comment">//console.log(this.tweets);</span>

    <span class="hljs-comment">/* Tranform the object into a string  */</span>
    stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
    <span class="hljs-comment">//console.log(stringTweets);</span>

    <span class="hljs-comment">/* Add to the local storage the stringified tweet object */</span>
    <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
},
</code></pre>
<p>The code above does four things:</p>
<ul>
<li>takes the tweets array and adds in it an object to represent a single tweet with text and date properties. To the text property, we assign the value of the <code>tweetMsg</code> that is bound with the <code>textarea</code>. For the date, we create a new date object with the <code>new Date().toLocaleTimeString()</code> method.</li>
<li>we empty the value of the <code>tweetMsg</code></li>
<li>we transform the tweets property into a string using the method <code>JSON.stringify(this.tweets)</code> </li>
<li>Then we add it to the local storage.</li>
</ul>
<p>Our final main.js file now looks like this:</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        },
        sendTweet(){
            <span class="hljs-comment">/* Store the tweet in the tweets property */</span>
            <span class="hljs-built_in">this</span>.tweets.unshift(
                {
                    <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
                    <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
                }

            );
            <span class="hljs-comment">/* Empty the tweetMsg property */</span>
            <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;
            <span class="hljs-comment">//console.log(this.tweets);</span>

            <span class="hljs-comment">/* Tranform the object into a string  */</span>
            stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            <span class="hljs-comment">//console.log(stringTweets);</span>

            <span class="hljs-comment">/* Add to the local storage the stringified tweet object */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
        },


    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }
        <span class="hljs-comment">// repopulate the userData object</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }

    }

});
</code></pre>
<p>Now that we've completed this part, we can show a list of tweets and also handle when the page is refreshed, and the local storage has in it our tweets object. We will need to parse it back and add its content to the <code>tweets</code> property to see the list.</p>
<p>Next, we will learn how to show the list of tweets using a v-for directive.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/xFwfrIciFt0" 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>
<h3 id="heading-how-to-show-a-list-of-tweets-html">How to show a list of tweets - HTML</h3>
<p>Inside our root element, add the following code:</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
                    {{tweet.text}}
                <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">"tweetDate"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-calendar-alt fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>{{tweet.date}}
                <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">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<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>
</code></pre>
<p>Here we wrap everything in a div with a class <code>card_tweets</code>. Then we use a v-if directive inside a child section to check if there are tweets in the <code>tweets</code> array <code>v-if="tweets.length &gt; 0"</code>. </p>
<p>Inside this section, we can loop over the tweets array using a <code>v-for="(tweet, index) in tweets"</code> directive. After that we use in-template expressions to show the tweet text property <code>{{tweet.text}}</code> and the data <code>{{tweet.date}}</code>.</p>
<p>After the <code>section</code> we can use a <code>v-else</code> directive to show a message in case there are no tweets stored inside the tweets array <code>&lt;div v-else&gt;No tweets to show&lt;/div&gt;</code>. Done. </p>
<p>Now there's one last thing we need to do, and that is to figure out what to do to remove tweets from the list.</p>
<p>But when the user refreshes the page, everything is gone. So we need to work with the <code>localStorage</code> once again to repopulate our tweets array from it before rendering the root element.</p>
<p>Inside the <code>created</code> lifecycle hook we will now write some code to parse the tweets and save them back in the <code>tweets</code> property:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Parse all tweets from the local storage  */</span>
<span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_tweets"</span>)) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"There is a list of tweets"</span>);
    <span class="hljs-built_in">this</span>.tweets = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_tweets'</span>))
}<span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No tweets here"</span>);
}
</code></pre>
<p>Here we used the <code>localStorage</code> API to first check if there was a key called <code>simple_tweet_tweets</code>. If so, we grab the tweets property using <code>this.tweets</code> and assign to it the content of the <code>localStorage</code>. But we parse the string back to <code>JSON</code> with <code>JSON.parse</code> so we write <code>this.tweets = JSON.parse(localStorage.getItem('simple_tweet_tweets'))</code>.</p>
<p>Now everything works. After we refresh the page, the tweets are still there. Let's move on. In the next step, we will add a method to remove tweets from the list.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/3DzBkUHH3bU">YouTube here</a> or at the end of this section to review what you've learned.</p>
<h3 id="heading-how-to-remove-tweets">How to remove tweets</h3>
<p>Inside the div that contains the tweet message, we can add another div to show a link and a trash icon. This lets the user click it and remove that tweet. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-xs fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><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>
</code></pre>
<p>Here we simply used a v-on short syntax directive on the div and invoked a method <code>removeTweet(index)</code>, passing to the method the element index so that we know what to remove.</p>
<p>Let's build our <code>removeTweet</code> method now:</p>
<pre><code class="lang-js">removeTweet(index){
    <span class="hljs-keyword">let</span> removeIt = confirm(<span class="hljs-string">"Are you sure you want to remove this tweet?"</span>)
    <span class="hljs-keyword">if</span>(removeIt) {
        <span class="hljs-built_in">this</span>.tweets.splice(index, <span class="hljs-number">1</span>);
        <span class="hljs-comment">/* Remove the item also from the local storage */</span>
        <span class="hljs-built_in">localStorage</span>.simple_tweet_tweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
    }
}
</code></pre>
<p>This bit of code is pretty straightforward. Our method accepts an index that represents the position of the tweet object in the array obtained from the v-for directive when the method is invoked. </p>
<p>We then create a variable to ask the user to confirm that they want to delete the tweet. We used the <code>confirm</code> function for that. </p>
<p>If the value of the <code>removeIt</code> variable is true, then we execute the code and use <code>this.tweets.splice(index, 1)</code> to remove the tweet from the array using its index. </p>
<p>Finally we update the <code>localStorage</code> value by assigning to is the new array using the <code>localStorage.simple_tweet_tweets = JSON.stringify(this.tweets)</code>.</p>
<h3 id="heading-final-code">Final Code</h3>
<p>Our code is now complete. You can find the final code below or inside the repository here: [https://bitbucket.org/fbhood/simple-tweet-app/src/master/].</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vue 2 Hello World<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.0/axios.min.js"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha512-DZqqY3PiOvTP9HkjIWgjO6ouCbq+dxqWoJZ/Q+zPYNHmlnI2dQnbJ5bxAHpAMw+LXRm4D72EIRXzvcHQtE8/VQ=="</span>
        <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- development version, includes helpful console warnings --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</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"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-twitter fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Register an account --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!registered"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">form</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Create your account<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"register"</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"registerAccount"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"name"</span>&gt;</span>Name
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ name.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"email"</span>&gt;</span>Email
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ email.length + '/' + max_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_length"</span> <span class="hljs-attr">required</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">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"password"</span>&gt;</span>Password
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ password.length + '/' + max_pass_length }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">:maxlength</span>=<span class="hljs-string">"max_pass_length"</span> <span class="hljs-attr">required</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">form</span>&gt;</span>


            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error.length &gt; 0"</span>&gt;</span> {{error}}<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-comment">&lt;!-- Add tweet --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetBox"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome {{ userData.name }} write your first Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">v-on:submit.prevent</span>=<span class="hljs-string">"sendTweet"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form_group"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"tweet"</span>&gt;</span>
                        Send your tweet
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span> {{ tweetMsg.length + '/' + max_tweet }}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"30"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">maxlength</span>=<span class="hljs-string">"200"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Tweet<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</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-comment">&lt;!-- Show all tweets --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweetMsg"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
                    {{tweet.text}}
                <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">"tweetDate"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-calendar-alt fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>{{tweet.date}}
                <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">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-xs fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><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>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<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">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>JavaScript file</p>
<pre><code class="lang-js">
<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">userData</span>: {},
        <span class="hljs-attr">usersID</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">password</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">max_length</span>: <span class="hljs-number">25</span>,
        <span class="hljs-attr">max_pass_length</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">max_tweet</span>: <span class="hljs-number">200</span>,
        <span class="hljs-attr">error</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">registered</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">tweetMsg</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">tweets</span>: []
    },

    <span class="hljs-attr">methods</span>: {
          registerAccount(){

              <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.name.length &gt; <span class="hljs-number">0</span> &amp;&amp; <span class="hljs-built_in">this</span>.name.length &lt;= <span class="hljs-built_in">this</span>.max_length &amp;&amp; <span class="hljs-built_in">this</span>.email !== <span class="hljs-string">""</span> &amp;&amp; <span class="hljs-built_in">this</span>.password !== <span class="hljs-string">""</span> ) {

                    <span class="hljs-built_in">this</span>.userData.id = ++<span class="hljs-built_in">this</span>.usersID,
                    <span class="hljs-built_in">this</span>.userData.name = <span class="hljs-built_in">this</span>.name,
                    <span class="hljs-built_in">this</span>.userData.email = <span class="hljs-built_in">this</span>.email,
                    <span class="hljs-built_in">this</span>.userData.password = <span class="hljs-built_in">this</span>.password
                    <span class="hljs-built_in">this</span>.registered=<span class="hljs-literal">true</span>;



              } <span class="hljs-keyword">else</span> {
                  <span class="hljs-built_in">this</span>.error = <span class="hljs-string">"Complete all the form fields"</span>
              }

            <span class="hljs-comment">/* Add registration data to the local storage */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered'</span>, <span class="hljs-literal">true</span>)
            <span class="hljs-comment">/* Add the whole userData object as JSON string */</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_registered_user'</span>, <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.userData))

            <span class="hljs-comment">/* Clear the registration inputs */</span>
            <span class="hljs-built_in">this</span>.name = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.email = <span class="hljs-string">""</span>;
            <span class="hljs-built_in">this</span>.password = <span class="hljs-string">""</span>;
        }, 
        sendTweet(){
            <span class="hljs-built_in">this</span>.tweets.unshift(
                {
                    <span class="hljs-attr">text</span>: <span class="hljs-built_in">this</span>.tweetMsg,
                    <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toLocaleTimeString()
                }

            );
            <span class="hljs-built_in">this</span>.tweetMsg = <span class="hljs-string">""</span>;

            <span class="hljs-comment">//console.log(this.tweets);</span>
            stringTweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            <span class="hljs-comment">//console.log(stringTweets);</span>
            <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">'simple_tweet_tweets'</span>, stringTweets)
        },
        removeTweet(index){
            <span class="hljs-keyword">let</span> removeIt = confirm(<span class="hljs-string">"Are you sure you want to remove this tweet?"</span>)
            <span class="hljs-keyword">if</span>(removeIt) {
                <span class="hljs-built_in">this</span>.tweets.splice(index, <span class="hljs-number">1</span>);
                <span class="hljs-comment">/* Remove the item also from the local storage */</span>
                <span class="hljs-built_in">localStorage</span>.simple_tweet_tweets = <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">this</span>.tweets)
            }
        }
    },
    created(){
        <span class="hljs-comment">/* Check if the user is registered and set the registered to true */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_registered"</span>) === <span class="hljs-string">'true'</span>){
            <span class="hljs-built_in">this</span>.registered = <span class="hljs-literal">true</span>;
        }

        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>)) {
            <span class="hljs-built_in">this</span>.userData = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_registered_user'</span>))
        }
        <span class="hljs-comment">/* Parse all tweets from the local storage  */</span>
        <span class="hljs-keyword">if</span>(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"simple_tweet_tweets"</span>)) {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"There is a list of tweets"</span>);
            <span class="hljs-built_in">this</span>.tweets = <span class="hljs-built_in">JSON</span>.parse(<span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">'simple_tweet_tweets'</span>))

        }<span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"No tweets here"</span>);
        }
    }

});
</code></pre>
<p>We are ready to move forward with our Vue journey. Now it's time to learn about components.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/3DzBkUHH3bU" 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-vue-component-basics">Vue Component Basics</h2>
<p>A component is a reusable block of code that represents a specific element on the page.</p>
<p>Every web page and web or mobile application can be divided into components. Starting from the main sections we can further divide these into smaller bits and make sub-components. </p>
<p>Every component is reusable and is made of dedicated HTML, CSS, and JavaScript code. </p>
<p>We can use components to organize our code and build complex layouts that are easily maintainable.</p>
<p>Looking at a simple web page, it is usually made of a header, the main content area, and a footer. But each of these three pieces can be sub-divided into smaller parts. </p>
<p>For instance, a header can have the main navigation menu, a secondary menu, and a hero image. The same is true for the main and footer areas.</p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/wrqjPka7puo">YouTube here</a> or at the end of this section to review. You can also view the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/12-components-basics/">here</a>.</p>
<p>To get started with components, we first need to learn how to register them, pass them data, and then we need to learn how to use them. Here are some great overviews of these topics to get you started:</p>
<ul>
<li>Register a component (https://vuejs.org/v2/guide/components-registration.html)</li>
<li>How to use Props (https://vuejs.org/v2/guide/components-props.html)</li>
<li>How to use Slots (https://vuejs.org/v2/guide/components-slots.html)</li>
<li>How the Data object works inside a component   </li>
<li>Child Component Events (https://vuejs.org/v2/guide/components-custom-events.html)</li>
<li>Dynamic Components ( https://vuejs.org/v2/guide/components-dynamic-async.html)</li>
</ul>
<h3 id="heading-how-to-register-a-component-in-vue">How to register a component in Vue</h3>
<p>To register a component, we need to use the <code>component</code> method on the <code>Vue()</code> object. After calling this function we need to define a template property with some markup specific to the component. </p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'component-name'</span>, {
    <span class="hljs-comment">// component properies here</span>
});
</code></pre>
<p>Every component needs to have a template property at least – without it a component doesn't make much sense.</p>
<p>So the next step is to define a template property and pass to it a string literal with some HTML tag:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'test-component'</span>, {
    <span class="hljs-comment">// component properies here</span>
    <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;p&gt;I am a component&lt;/p&gt;`</span>

});
</code></pre>
<p>Now we can use our component multiple times inside our main HTML file by using its name as it was a standard HTML tag.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">test-component</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">test-component</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>However, our component will always render the same content, <code>I am a component</code>. Let's make it more useful and, following our tweets example, build a tweet message component.</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>, {

    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; Tweet text goes here &lt;/p&gt;
           &lt;p&gt; Date of the tweet goes here&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>OK, now that we have the base for our component we need to actually pass data to it.</p>
<p>One thing to notice here is that every component requires a single root element inside the template property. So, since we have two paragraphs, we wrapped them inside a div that will be considered the root element of our component. </p>
<p>Inside it, we can put whatever we want to build our custom component.</p>
<p>Let's move on to the next step and pass some data to the component.</p>
<h3 id="heading-how-to-use-props-in-vue">How to use props in Vue</h3>
<p>Now, given what we've learned so far, we want to pass data to our component as we did previously to bind data between the Vue instance and the markup file via the moustache syntax. </p>
<p>However, with components things work a bit differently. We use props to create a binding between our component and its template.</p>
<p>The <code>props</code> property can be defined as an array or as an object. 
When used as an array, we can specify the properties as strings inside the array and these can later be used inside the component like we usually do. </p>
<p>When we use an object we can pass the prop as the key and its type as the value. That will help to make sure that the exact data type is passed to our component. </p>
<p>Let's see an example of that.</p>
<p>Example of props as an Array:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: [<span class="hljs-string">'text'</span>, <span class="hljs-string">'date'</span>]
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Use props as an object where the key is the property and the value is its type:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Once we have defined our properties, we can use them as HTML attributes and pass them the data we want our component to render onto the page. </p>
<p>For instance, we can use the component above to show a bunch of tweets using our newly created component.</p>
<pre><code class="lang-html">    <span class="hljs-comment">&lt;!-- Manually pass the data to the tweet message component --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is a component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"25/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"26/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">date</span>=<span class="hljs-string">"27/12/2020"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Pass a javascript expression to the date property of the tweet message component --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This another component"</span> <span class="hljs-attr">:date</span>=<span class="hljs-string">"new Date().toLocaleString()"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
</code></pre>
<p>The first examples will render the string we passed between quotes. But 
to render the computed result of the new <code>Date()</code> instance we will need to use the v-bind directive so that its content is interpreted as JavaScript code.</p>
<p>You can review all this in the docs here: [https://vuejs.org/v2/guide/components-props.html].</p>
<h3 id="heading-the-data-property-inside-components">The data property inside components</h3>
<p>So far we have seen that we can bind data by defining properties inside the <code>data</code> object on a Vue instance. </p>
<p>When working with components the data object is not available as an object but as a function. This function can return an object with properties. This will make each component's instance unique and independent from the others.</p>
<p>Following our previous example, let's add a couple of CSS classes to our component.</p>
<p>First, we will edit our component template and bind the class attribute to a data property. Then we will create our data object.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>
});
</code></pre>
<p>Now our template will look for two data properties, <code>tweetBoxWrapper</code> and <code>dateClass</code>, that we can later use inside our CSS to add some style to our elements. Let's add the data function now.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">text</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">date</span>: <span class="hljs-built_in">String</span>
    }
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt; {{date}}&lt;/p&gt;
       &lt;/div&gt;
    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-comment">// Data properties go here</span>
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
        }
    }
});
</code></pre>
<p>Another thing we can do is to define a data property and use it inside our template, for instance, to dynamically show the current date. We can define a <code>now</code> property and use it in the template like we previously did with the <code>date</code> property:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'text'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt;{{now}}&lt;/p&gt;

       &lt;/div&gt;

    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
            <span class="hljs-attr">now</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toDateString(), <span class="hljs-comment">// 3 </span>

        }
    }


});
</code></pre>
<p>In the example above we have used both <code>props</code> and <code>data</code>. We can use the prop <code>text</code> as an attribute when we use our component <code>&lt;tweet-message text="This is a component"&gt;&lt;/tweet-message&gt;</code>. The properties we returned in the <code>data</code> method are bound to the template and will render the information we specify right there in the data method.</p>
<p>When inside the data method, we need to remember that props defined here are accessible using the <code>this</code> keyword.</p>
<p>So if we want to store the value of the text prop inside a property in the data object, we can grab it like this:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'text'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
       &lt;div :class="tweetBoxWrapper"&gt;
           &lt;p&gt; {{text}} &lt;/p&gt;
           &lt;p :class="dateClass"&gt;{{now}}&lt;/p&gt;

       &lt;/div&gt;

    `</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">tweetBoxWrapper</span>: <span class="hljs-string">"tweet-message"</span>,
            <span class="hljs-attr">dateClass</span>: <span class="hljs-string">"tweet-date"</span>,
            <span class="hljs-attr">now</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toDateString(), 
            <span class="hljs-attr">message</span>: <span class="hljs-built_in">this</span>.text
        }
    }


});
</code></pre>
<p>Next, we will learn about slots.</p>
<h3 id="heading-how-to-use-slots">How to use slots</h3>
<p>There are situations when we just don't know or want to strictly define what goes inside a component. Or we might want to let the user decide its content when they use our component.</p>
<p>In such cases, we can use slots when we declare the template of our component.</p>
<p>Let's imagine that we have another component that we want to use to divide tweets into different sections.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-section'</span>, {
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'title'</span>: <span class="hljs-built_in">String</span>,

    },
     <span class="hljs-attr">template</span>: <span class="hljs-string">`
        &lt;div class="tweet_section"&gt;
            &lt;h2&gt;{{title}}&lt;/h2&gt;
           &lt;slot&gt;&lt;/slot&gt;
       &lt;/div&gt;  
    `</span>    
});
</code></pre>
<p>Our new component can be as simple as that, a div with a class <code>tweet_section</code>, an <code>h2</code> that binds to a prop, and a slot. The slot means that inside our component we can put whatever we want, like nesting other elements and even other components.</p>
<pre><code class="lang-html">
<span class="hljs-tag">&lt;<span class="hljs-name">tweet-section</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Latest Tweets"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my first tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my second tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my third tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is my fourth tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">tweet-section</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">tweet-section</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Most popular"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Trendy in IT<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is a very popular tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">text</span>=<span class="hljs-string">"This is another popular tweet"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">tweet-section</span>&gt;</span>
</code></pre>
<p>We've barely scratched the surface here, but with what we know we can already modify our <code>simple_twitter</code> application to use components. Along the way, we will also learn how events work inside components.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/wrqjPka7puo" 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-how-to-update-your-simpletwitter-project-with-components">How to Update Your Simple_twitter Project with Components</h2>
<p>Now that we have a basic understanding of components, we can update the simple Twitter project we built in the previous videos and use components to make our code better.</p>
<p>We need to do a few things to make this happen, and create a component:</p>
<ol>
<li>We need to decide what component we want to build </li>
<li>We need to extract the code from the markup and place it in the template property</li>
<li>We need to refactor our code to make the component work.</li>
</ol>
<p>You can watch the tutorial on <a target="_blank" href="https://youtu.be/HanHyGFC6Sc">YouTube here</a>
or checkout the repository <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/13-simple-twitter-components/">here</a>.</p>
<p>Let's say we want to create a component for the tweet message. </p>
<h3 id="heading-how-to-create-the-component">How to create the component</h3>
<p>Let's create a component for a tweet message like this:</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">template</span>: <span class="hljs-string">``</span>
});
</code></pre>
<h3 id="heading-how-to-move-the-tweetmsg-element">How to move the tweetMsg element</h3>
<p>Then we have to move the <code>tweetMsg</code> element inside the  <code>template</code> property of our component:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg" v-for="(tweet, index) in tweets"&gt;
        &lt;p&gt;
            {{ tweet.text}}
        &lt;/p&gt;
        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar fa-sm fa-fw"&gt;&lt;/i&gt;{{ tweet.date }}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="removeTweet(index)"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-sm fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    `</span>
});
</code></pre>
<p>After that, we need to update the template because the v-for directive now is useless. So we will remove it and add it back later when we are ready to use the component.</p>
<p>Given that we will not have a v-for directive at this point, we still want to use the tweet variable to grab the tweet, so we will pass it as a <code>props</code>.</p>
<pre><code class="lang-js">Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'tweet'</span>: <span class="hljs-built_in">Object</span>,
    },
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg"&gt;
        &lt;p&gt;
            {{ tweet.text}}
        &lt;/p&gt;
        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar fa-sm fa-fw"&gt;&lt;/i&gt;{{ tweet.date }}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="removeTweet(index)"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-sm fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    `</span>
});
</code></pre>
<h3 id="heading-how-to-emit-a-custom-event">How to emit a custom event</h3>
<p>There is also an event listener that needs to change to let our application work as expected. </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"remove"</span>&gt;</span>Delete this tweet <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fas fa-trash fa-sm fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span><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>
</code></pre>
<p>The code here <code>&lt;div class="tweet_remove" @click="removeTweet(index)"&gt;</code> listens to click events so the user can remove a tweet by clicking on it. </p>
<p>This will need to go, and we need to replace it with a special method of the Vue instance called <code>$emit()</code>. Our component instance will need to communicate with the parent instance and tell it that it wants to trigger the remove tweet method.  </p>
<p>To solve this problem, Vue provides a custom events system. It allows us to use the v-on directive to listen not only to native DOM events but also to custom events defined at the component level.</p>
<p>We need to update this line of code: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span>
</code></pre>
<p>and change it like so: </p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweet_remove"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"$emit('remove-tweet', 'index')"</span>&gt;</span>
</code></pre>
<p>Let's break this down: we keep using the v-on directive in its short form <code>@</code>. Then we use the Vue <code>$emit</code> method to define a custom event that our component will emit when we click on this element. </p>
<p>To the <code>$emit</code> method we pass two parameters, the first is the name of the custom event <code>remove-tweet</code>, and the second is a parameter that we want to pass to the event listener when we use <code>index</code>. That will be the index of the element we want to delete. </p>
<p>So that the parent instance can listen to our event, trigger the <code>removeTweet</code> method we defined in the main Vue instance and remove the correct tweet.</p>
<h3 id="heading-put-it-all-together-1">Put it all together</h3>
<p>Our final component now looks like this:</p>
<pre><code class="lang-js">
Vue.component(<span class="hljs-string">'tweet-message'</span>,{
    <span class="hljs-attr">props</span>: {
        <span class="hljs-string">'tweet'</span>: <span class="hljs-built_in">Object</span>,
    },
    <span class="hljs-attr">template</span>: <span class="hljs-string">`
    &lt;div class="tweetMsg"&gt;
        &lt;p&gt;
            {{tweet.text}}
        &lt;/p&gt;

        &lt;div class="tweetDate"&gt;
            &lt;i class="fas fa-calendar-alt fa-sm fa-fw"&gt;&lt;/i&gt;{{tweet.date}}
        &lt;/div&gt;
        &lt;div class="tweet_remove" @click="$emit('remove-tweet', 'index')"&gt;
            &lt;span class="remove"&gt;Delete this tweet &lt;i class="fas fa-trash fa-xs fa-fw"&gt;&lt;/i&gt;&lt;/span&gt;
        &lt;/div&gt;

    &lt;/div&gt;
    `</span>
});
</code></pre>
<p>And we'll change our index.html file as follows:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show all tweets --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card_tweets"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"tweets"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"tweets.length &gt; 0"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Tweets<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tweet-message</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(tweet, index) in tweets"</span>  <span class="hljs-attr">v-bind:tweet</span>=<span class="hljs-string">"tweet"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"index"</span> @<span class="hljs-attr">remove-tweet</span>=<span class="hljs-string">"removeTweet(index)"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tweet-message</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>No tweets to show<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>
</code></pre>
<p>Now that we've completed our first project, let's learn how to make an API request and how to use the GitHub API to build our final portfolio.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/HanHyGFC6Sc" 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-how-to-perform-api-calls-with-axios">How to Perform API Calls with Axios</h2>
<p>For our next project, I have created a simple but nice design using Figma that we will use to kick start our portfolio.</p>
<p>Our portfolio will use the GitHub's rest API to pull projects and fill out the design. </p>
<p>You can watch the video on <a target="_blank" href="https://youtu.be/XJEmPr89HA8">YouTube here</a>
and check out the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/14-axios/">BitBucket</a>.</p>
<h3 id="heading-what-is-a-rest-api">What is a REST API?</h3>
<p>To quote Wikipedia:</p>
<blockquote>
<p>"A REST API is a software architectural style that enables the requesting system to access and manipulate a textual representation of web resources."</p>
</blockquote>
<p>What this means is that our Vue application (the requesting system) will request a textual representation from GitHub of our repositories that we can use later and manipulate to showcase our projects inside our portfolio.</p>
<p>For our final project, we will use a library called Axios that will help us make HTTP requests to the GitHub API. </p>
<p>We can install Axios inside our project in multiple ways. For our example we will keep things simple and use the CDN. </p>
<p>There are also other methods you can use to install Axios. The official documentation for Axios is available <a target="_blank" href="https://www.npmjs.com/package/axios">here</a>
and you can read about how to consume the API in the <a target="_blank" href="https://vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Base-Example">documentation here</a>.</p>
<h3 id="heading-how-to-install-axios-via-cdn">How to install Axios via CDN</h3>
<p>So let's get started and install Axios via the CDN. We will use the UNPKG CDN and insert a script tag inside our main HTML file. </p>
<p>This CDN will always provide the most up to date version of Axios. Alternatively, we can also specify a different version number. </p>
<p>Let's start by inserting the following script in an index.html file that we will use to send our first HTTP request to the GitHub API.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Our final HTML file will look like this now:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJS / GitHub API<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>


<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Axios latest version CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>The code above is nothing new, but let's look at it piece by piece.</p>
<p>We have a basic HTML structure. Before the closing body tag, we've placed two script tags, one for Axios and one for VueJs. </p>
<p>In the body, we created a root element for the Vue application that we called <code>#app</code>.</p>
<p>Finally, before the end of the body tag, we placed a new script tag that points to the file where we will write our code, the main.js file.</p>
<p>Now we have all the building blocks to make our first API call and request data from the GitHub API.</p>
<p>But before that let's quickly see what an HTTP request actually is and what kind of requests we can make.</p>
<h3 id="heading-what-is-an-http-request">What is an HTTP request?</h3>
<p>HTTP stands for Hypertext transfer protocol. It is an application-layer protocol designed for communications between two points:</p>
<ol>
<li>a web client (the browser) </li>
<li>a web server</li>
</ol>
<p>This protocol allows transmission of data like HTML files and. It defines verbs also known as methods that you can use to perform specific actions on a given resource.</p>
<p>The method or verb that we will use for our project is the <code>GET</code> method, that, as you might have guessed, is used to obtain or to get 
a resource from the webserver.</p>
<p>We have also other methods:</p>
<ul>
<li>GET (retrieves data)</li>
<li>POST (sends data)</li>
<li>PUT (updates the entire representation of the data)</li>
<li>PATCH (similar to put but used to partially update data)</li>
<li>DELETE (removes data)</li>
</ul>
<p>Each of these requests performs a specific action on a resource, but there are also other verbs like the HEAD, OPTIONS, CONNECT, and TRACE.</p>
<p>I won't cover HTTP in detail here as it's out of the scope of this guide. But below there are some links to documentation pages 
related to this topic if you want to find out more. </p>
<p>I suggest you to read the following at least:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-the-internet-works/">HTTP Intro</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">HTTP Methods</a></li>
</ul>
<h3 id="heading-how-to-perform-a-get-request">How to perform a GET request</h3>
<p>We will use the GET method to perform get requests from the GitHub API. All data we want to request are publicly accessible (the public repositories of a user), therefore we don't have to authenticate our application. </p>
<p>But unauthenticated requests are limited. For the scope of this tutorial, this is perfectly fine. If you plan to put this in production then you might want to look at how to make authenticated requests and obtain an API key from GitHub.</p>
<p>GitHub provides clear and in-depth documentation about its Rest API, including a list of resources that you can request along with their endpoints. We will use the "List repositories for a user" resources available <a target="_blank" href="https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#list-repositories-for-a-user">here</a>.</p>
<p>Let's look at the documentation. The first thing we notice is that GitHub gives us a GET endpoint where we can send our HTTP requests <code>/users/{username}/repos</code>. </p>
<p>The placeholder <code>{username}</code> needs to be replaced with the actual username of the user we want to request the list of public repositories from.</p>
<p>From the documentation, we also see that there are other parameters that we can use to refine our request. We will use <code>username</code> that goes in the path and needs to be a string as described in the parameters table under Type. </p>
<p>We can also use the <code>per_page</code> and <code>page</code> parameters to paginate our results.</p>
<p>Let's make the first request and see what we get.</p>
<p>Inside our main.js file, we will create a new Vue instance and add a <code>mounted</code> lifecycle hook where we will perform the HTTP request using Axios.</p>
<pre><code class="lang-js"><span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    <span class="hljs-attr">el</span>:<span class="hljs-string">'#app'</span>,
    <span class="hljs-attr">data</span>:{
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>
    },
    mounted(){

         axios
         .get(<span class="hljs-string">`https://api.github.com/users/fabiopacifici/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
         .then(
            <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                <span class="hljs-built_in">console</span>.log(response);
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
        )
        .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {<span class="hljs-built_in">console</span>.log(error);})
    }
});
</code></pre>
<p>Let's break this code down. First, we have created a new Vue instance. Then we used the <code>el</code> property and assigned it a root HTML element. </p>
<p>Then we have defined a <code>data</code> object and the properties that we will use later to perform the HTTP request and handle the response.</p>
<p>After the data object, we have defined a lifecycle hook that will use to run our code once the root element has been mounted.</p>
<p>Inside the <code>mounted</code> method, it's time to use Axios and perform an HTTP request. </p>
<p>Axios is a promise-based HTTP client. When we use the get method to request our data from the GitHub API it will return a promise that needs to be handled. </p>
<p>We do this using the syntax <code>axios.get()</code> to perform the request, then we handle its response using the <code>.then()</code> method on the promise. </p>
<p>If our request fails the <code>.catch()</code> method will handle the error and, in this case, show the error message on the console.</p>
<p>Promises are out of the scope of this guide, but if you want to learn more, you can check out <a target="_blank" href="https://www.freecodecamp.org/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/">this detailed article here</a>.</p>
<p>Inside the <code>.get()</code> method we have put the URL including a query string that uses <code>per_page</code> and <code>page</code> parameters to submit our request. Inside the <code>.then()</code> method we handled the response. The response parameter is given to us by the promise and we use an arrow function to handle it.</p>
<pre><code class="lang-js">response =&gt; {
                <span class="hljs-built_in">console</span>.log(response);
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
</code></pre>
<p>The get method returns a promise. Here we simply handled its <code>response</code> with an arrow function where <code>response</code> is the return value that we obtained by calling <code>axios.get()</code>.</p>
<p>We logged the response object to the console. Then we assigned its content, the <code>response.data</code>, to our <code>projects</code> property so that we can later retrieve each project and show them onto the page as usual with a <code>v-for</code> directive.</p>
<h3 id="heading-how-to-show-each-project">How to show each project</h3>
<p>Now it's time to show our projects inside the portfolio. We can do that with the v-for directive.</p>
<p>The projects property in this case contains an array of objects. Each object has its properties that we can use to populate our template.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'app'</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</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>{{project.full_name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"author"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"me"</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">"view"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span>&gt;</span>View<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>
</code></pre>
<p>Here we use the v-for directive to loop over the array of projects.
Now the <code>project</code> variable contains an object that represents a single repository from the GitHub account.</p>
<p>Looking at the response object we know that we can grab a number of properties. So we picked <code>full_name</code>, the full name for the repository, <code>owner.avatar_url</code>, the URL of the profile's avatar, and <code>html_url</code> that is the actual URL of our repository. That's all we need for now. </p>
<p>If we now look at the page we will immediately see all repositories from our account.</p>
<p>Now that we know how to make an HTTP request with Axios and get data from GitHub, we are almost ready to start building our portfolio. </p>
<p>In the next section, we are going to look at another Vue library called Vue-router that we will use in our final project.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/XJEmPr89HA8" 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-how-to-handle-routing-with-vuerouter">How to Handle Routing with VueRouter</h2>
<p>Our portfolio will surely have more than one page, so we need a system that understands where to send the user when, for instance, they click a link in the navbar for a specific page. </p>
<p>For that Vue has an official routing package that can help us do just that and build a single page application.</p>
<p>A single page application is an application that doesn't refresh the page when a user visits a new page so that the user experience is more fluid.</p>
<p>As for Vue and Axios, we need to install this library and we do that via its CDN. But as always, there are also other methods depending on your needs. I just want to keep things simple for now, so let's start by placing the CDN script tag inside the HTML file and learn the basics of this new library.</p>
<p>You can watch the tutorial on <a target="_blank" href="https://youtu.be/T_avTRFAEAg">YouTube here</a>
and checkout the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/15-routing/">BitBucket</a>.</p>
<p>You can also see the Vue Router Documentation <a target="_blank" href="https://router.vuejs.org/installation.html#direct-download-cdn">here</a>.</p>
<h3 id="heading-how-to-install-vue-router-via-cdn">How to install Vue Router via CDN</h3>
<p>Let's take our previous example index.html and after the VueJS CDN will point to the router <code>https://unpkg.com/vue-router@3.4.9/dist/vue-router.js</code>.</p>
<pre><code class="lang-html">
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>VueJS / GitHub API<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span> 
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projects"</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>{{project.full_name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"author"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"50px"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"me"</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">"view"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span>&gt;</span>View<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-comment">&lt;!-- Axios latest version CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router CDN --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Main scrip file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>
<h3 id="heading-how-to-use-vue-router">How to use Vue Router</h3>
<p>Now our app has access to the router system and we can add a couple of routes for our application.</p>
<p>We can do so using the <code>router-link</code> component provided by the library and its <code>to</code> attribute to point the link to a specific page.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Create a router link using the 'router-link' component and set the path using the 'to' attribute --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Projects<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
</code></pre>
<p>We also used the router-view component that will render a specific component for each route.</p>
<p>Now we need to do something inside our JavaScript file to make this work. </p>
<p>Let's see the steps we need to take:</p>
<ul>
<li>Define route components</li>
<li>Define routes</li>
<li>Create a Vue router instance</li>
<li>Create and mount the Vue root instance.</li>
</ul>
<p>First, we need to define our components that we'll use from each route to render the content of the page.</p>
<p>We will create two components, one for the home page and one for the projects page.</p>
<p>To simplify the steps, we will keep everything in the same file and refactor later on. </p>
<p>Let's create the first two basic components to see if the router works:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create Route components</span>
<span class="hljs-keyword">const</span> Home = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt;My Portfolio&lt;/div&gt;'</span>} 
<span class="hljs-keyword">const</span> Projects = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt; Projects &lt;/div&gt;'</span>}
</code></pre>
<p>Now let's follow the remaining steps and define the routes, create the vue router instance, and create and mount the Vue root instance.</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Define some routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects}
];
<span class="hljs-comment">// Create the router instance and pass the routes to it</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
<span class="hljs-attr">routes</span>: routes
});
<span class="hljs-comment">// Create and mount the root instance.</span>

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    router 
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>That's it. If you visit the homepage you will see two navigation links and the site content will change accordingly.</p>
<p>Let's put it all together and start building our portfolio.</p>
<p>From the previous example in the Axios section, we requested from the GitHub API all public repositories for a user and rendered name, user avatar, and project URL onto the page. </p>
<p>Let's move some of that logic inside our application that uses routes.</p>
<p>The main changes that we need to make here are:</p>
<ul>
<li>move the HTML markup inside the <code>template</code> property of the project's component</li>
<li>move the <code>data</code> properties inside the <code>data</code> object of the component</li>
<li>move the code we wrote in the mounted hook inside our component.</li>
</ul>
<p>The final code looks something like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Define route components</span>

<span class="hljs-keyword">const</span> Home = {<span class="hljs-attr">template</span>: <span class="hljs-string">'&lt;div&gt;My Portfolio&lt;/div&gt;'</span>} 
<span class="hljs-keyword">const</span> Projects = {

    <span class="hljs-attr">template</span>: <span class="hljs-string">`&lt;div&gt; 
         &lt;div v-for="project in projects"&gt;
            &lt;h2 class="title"&gt;{{project.full_name}}&lt;/h2&gt;

            &lt;div class="author"&gt;
                &lt;img width="50px" :src="project.owner.avatar_url" alt="me"&gt;
            &lt;/div&gt;
            &lt;div class="view"&gt;
                &lt;a :href="project.html_url"&gt;View&lt;/a&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;`</span>,
    data(){
        <span class="hljs-keyword">return</span> {
            <span class="hljs-attr">projects</span>: [],
            <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
            <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>
        }
    }, 
    mounted(){

         axios
         .get(<span class="hljs-string">`https://api.github.com/users/fabiopacifici/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
         .then(
            <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                <span class="hljs-comment">//console.log(response);</span>
                <span class="hljs-built_in">this</span>.projects = response.data;
            }
        )
        .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {<span class="hljs-built_in">console</span>.log(error);})
    }
} 

<span class="hljs-comment">// Define some routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects}
];
<span class="hljs-comment">// Create the router instance and pass the routes to it</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
<span class="hljs-attr">routes</span>: routes
});
<span class="hljs-comment">// Create and mount the root instance.</span>

<span class="hljs-keyword">let</span> app = <span class="hljs-keyword">new</span> Vue({
    router 
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>The HTML file remains the same:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'app'</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Projects<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Now that we have a base to work with, let's improve it. We will use a design prototype I made using Figma and add some functionalities to our portfolio to make it look nice.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/T_avTRFAEAg" 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-final-project-how-to-build-a-portfolio-with-vuejs-vuerouter-axios-github-api-and-deploy-to-netlify">Final Project – How to Build a Portfolio with VueJS, VueRouter, Axios, GitHub API and deploy to Netlify</h2>
<p>We are ready to build our final project! For our Vue-folio, we will start from where we left off in the previous section.</p>
<p>We will build a single-page application that has two routes, one for the home page and one for the projects page. </p>
<p>Below are the building blocks:</p>
<ul>
<li>Vuejs </li>
<li>Vue router</li>
<li>Axios</li>
<li>GitHub rest API</li>
<li>portfolio design</li>
</ul>
<p>You can watch this tutorial on <a target="_blank" href="https://youtu.be/I6hQnWQU4rQ">YouTube here</a> and check out the repository on <a target="_blank" href="https://bitbucket.org/fbhood/how-to-vuejs/src/master/16-final-project-portfolio/">BitBucket here</a>.</p>
<h3 id="heading-project-structure">Project structure</h3>
<p>To speed things up we will just copy the code we wrote in the previous section.</p>
<p>The project structure will be the following:</p>
<pre><code>|-- index.html
|-- assets/
    |-- css/
        |-- style.css
    |-- js/
        |-- main.js
    |-- img/
</code></pre><h3 id="heading-indexhtml-file">index.html file</h3>
<p>The index.html file is a little different from what we had in the previous section. Here, we will place only the router-view component that is responsible for showing the component matching a given route. </p>
<p>Then we will place the actual <code>route-links</code> inside each component to make sure we have the desired result as per the design.</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</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.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vuefolio<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Raleway:wght@100;300;400;900&amp;display=swap"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./assets/css/style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

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

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</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> © Developed by <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fabiopacifici.com"</span>&gt;</span>Fabio Pacific<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Main Js file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/js/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>
<h3 id="heading-stylecss-file">style.css file</h3>
<p>Since this is not going to be a CSS tutorial, for the CSS part you can simply copy the code from the repository file if you are following along.</p>
<pre><code class="lang-css">
    <span class="hljs-comment">/* Utility Classes */</span>
    <span class="hljs-selector-class">.d_none</span> {
        <span class="hljs-attribute">display</span>: none;
    }
    <span class="hljs-selector-class">.d_flex</span> {
        <span class="hljs-attribute">display</span>: flex;
    }
    <span class="hljs-selector-class">.container</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1170px</span>;
        <span class="hljs-attribute">margin</span>: auto;
    }
    <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: white;
    } 
    <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>:<span class="hljs-number">#DB5461</span>;

    }
    <span class="hljs-selector-class">.loading</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
    }
    <span class="hljs-comment">/* END Utility Classes */</span>
    <span class="hljs-comment">/* Components */</span>
    <span class="hljs-selector-class">.bio__media</span> {
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">justify-content</span>: flex-start;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">text-align</span>: left;
    }
    <span class="hljs-selector-class">.bio__media</span> <span class="hljs-selector-tag">img</span> {
        <span class="hljs-attribute">height</span>: <span class="hljs-number">120px</span>;
    }
    <span class="hljs-selector-class">.bio__media__text</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    }
    <span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">h1</span>{
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;

    }
    <span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">p</span> {
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;

    }

    <span class="hljs-selector-class">.card__custom</span> {
        <span class="hljs-attribute">position</span>: relative;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
        <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> /<span class="hljs-number">2</span>);
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">justify-content</span>: space-between;
    }
    <span class="hljs-selector-class">.card__custom</span> &gt; <span class="hljs-selector-class">.card__custom__text</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-built_in">calc</span>((<span class="hljs-number">100%</span> / <span class="hljs-number">3</span>) *<span class="hljs-number">2</span>);
        <span class="hljs-attribute">text-align</span>: right;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">80%</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">justify-content</span>: space-around;
        <span class="hljs-attribute">overflow</span>: hidden;

    }
    <span class="hljs-selector-class">.card__custom__img</span> {

        <span class="hljs-attribute">position</span>: absolute;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">70%</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/cards_bg_img.svg);
        <span class="hljs-attribute">background-position</span>: center;
        <span class="hljs-attribute">background-repeat</span>: no-repeat;
        <span class="hljs-attribute">background-size</span>: contain;
        <span class="hljs-attribute">display</span>: inline-block;
        <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
        <span class="hljs-attribute">left</span>: <span class="hljs-number">60%</span>;
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">85px</span> <span class="hljs-number">0</span> <span class="hljs-number">100px</span> <span class="hljs-number">25px</span>;

    }
    <span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span>, <span class="hljs-selector-class">.btn_load_more</span> {
        <span class="hljs-attribute">background</span>: <span class="hljs-number">#F1EDEE</span>;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
        <span class="hljs-attribute">box-sizing</span>: border-box;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">54px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#3D5467</span>;
    }
    <span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.btn_load_more</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">cursor</span>: pointer;
        <span class="hljs-attribute">background</span>: <span class="hljs-number">#324555</span>;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: <span class="hljs-number">1s</span>;
    }
    <span class="hljs-selector-class">.card__custom__text</span> <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
    }
    <span class="hljs-comment">/* END Componenet */</span>
    * {
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
        <span class="hljs-attribute">box-sizing</span>: border-box;
    }

    <span class="hljs-selector-tag">body</span>{
        <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Raleway'</span>, Arial, Helvetica, sans-serif;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(<span class="hljs-number">116.82deg</span>, #<span class="hljs-number">3</span>D5467 <span class="hljs-number">0%</span>, #<span class="hljs-number">1</span>A232B <span class="hljs-number">99.99%</span>, #<span class="hljs-number">333333</span> <span class="hljs-number">100%</span>);

    }
    <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">text-decoration</span>: none;   
    }

    <span class="hljs-comment">/* Home Page */</span>
    <span class="hljs-selector-tag">main</span><span class="hljs-selector-id">#home</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">600px</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">justify-content</span>: center;
        <span class="hljs-attribute">align-items</span>: center;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">28px</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;

    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span>  {
        <span class="hljs-attribute">font-style</span>: normal;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">42px</span>;
        <span class="hljs-attribute">letter-spacing</span>: <span class="hljs-number">0.115em</span>;

    }
    <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> <span class="hljs-selector-tag">p</span> {
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">22px</span>;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
    }
    <span class="hljs-selector-class">.skills_projects_link</span> {
        <span class="hljs-attribute">position</span>: relative;
    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">color</span>: white;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">21px</span>;

    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease-in-out;

    }
    <span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">::after</span> {
        <span class="hljs-attribute">position</span>: absolute;
        <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">margin</span>: auto;
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">2px</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.5s</span> ease-in-out;

    }

    <span class="hljs-comment">/* Header */</span>
    <span class="hljs-selector-id">#site_header</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
        <span class="hljs-attribute">justify-content</span>: space-between;
        <span class="hljs-attribute">align-items</span>: center;
    }
    <span class="hljs-selector-id">#site_header</span> &gt; <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">text-transform</span>: uppercase;
    }
    <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span> {
            <span class="hljs-attribute">color</span>: <span class="hljs-number">#e2e2e2</span>;
        <span class="hljs-attribute">text-transform</span>: uppercase;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    }
    <span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    }
    <span class="hljs-comment">/* Portfolio Page Section */</span>

    <span class="hljs-selector-id">#portfolio</span> {
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-wrap</span>: wrap;
        <span class="hljs-attribute">align-items</span>: center;
        <span class="hljs-attribute">justify-content</span>: space-around;
    }
    <span class="hljs-selector-class">.btn_load_more</span> {

    }
    <span class="hljs-comment">/* Skills */</span>

    <span class="hljs-selector-id">#skills_section</span> {
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
        <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/skills_bg.svg);
        <span class="hljs-attribute">background-repeat</span>: no-repeat;
        <span class="hljs-attribute">background-size</span>: contain;
        <span class="hljs-attribute">background-position</span>: top left;
    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">h2</span> {
        <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">180px</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">44px</span>;
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#F1EDEE</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">2rem</span>;

    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span> {
        <span class="hljs-attribute">list-style</span>: none;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">120px</span>;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-wrap</span>: wrap;

    }
    <span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span>  <span class="hljs-selector-tag">li</span> {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.5rem</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">35px</span>;
    }



    <span class="hljs-selector-class">.avatar</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
        <span class="hljs-attribute">height</span>: auto;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;

    }




    <span class="hljs-selector-class">.card__back</span> {
        <span class="hljs-attribute">display</span>: none;
    }
    <span class="hljs-selector-class">.rotate__card</span> {
        <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate3d</span>(<span class="hljs-number">360</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">180deg</span>);
    }
    <span class="hljs-comment">/* Site Footer */</span>

    <span class="hljs-selector-tag">footer</span> {
        <span class="hljs-attribute">text-align</span>: center;
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
    }



    <span class="hljs-comment">/* Media Query  */</span>

    <span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">475px</span>) {
        <span class="hljs-selector-class">.card</span> {
            <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">100%</span>;
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        }
    }
</code></pre>
<h2 id="heading-mainjs-file-basic-structure">Main.js file basic structure</h2>
<p>Inside the main.js file, we have the core of our single page application. 
Here we will define the route components that need to be rendered for each view/page, the homepage, and projects components. </p>
<p>Then we will define two routes, one for the homepage and one for the projects page, create a router instance, and pass it to the routes. Finally, we will create a new Vue instance and pass to it the router instance and mount the root HTML element.</p>
<p>Let's start with the route components.</p>
<h3 id="heading-how-to-define-components-for-each-view">How to define components for each view</h3>
<p>The homepage component is fairly simple. </p>
<pre><code class="lang-js"><span class="hljs-comment">// Homepage component</span>
<span class="hljs-keyword">const</span> Home = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;main id="home"&gt;
        &lt;div class="about__me"&gt;
            &lt;img src="./assets/img/avatar.svg" alt=""&gt;
            &lt;h1&gt;John Doe&lt;/h1&gt;
            &lt;h3&gt;Python Expert&lt;/h3&gt;
            &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;

            &lt;div class="skills_projects_link"&gt;
                &lt;router-link to="/projects"&gt;Projects/Skills&lt;/router-link&gt; 
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;`</span>
}
</code></pre>
<p>Let's break it down. First, we create a Home constant that will hold the router component object. </p>
<p>Inside the object, the only thing we will put is the template property with some markup to render our page. The main thing to notice here is the router-link component that will point to the /projects route from the homepage.</p>
<p>Next, let's create a route component for the projects page. We have a lot to do here so for now let's just add some boilerplate code – we will come back to it later and write out step by step all the logic. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> Projects = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;div&gt;
        &lt;h1&gt;Projects&lt;/h1&gt;
    &lt;/div&gt;`</span>,
    data() { 
        <span class="hljs-keyword">return</span> {
                <span class="hljs-comment">// Data object here</span>
            }
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-comment">// All methods here</span>
    },
    mounted(){  
        <span class="hljs-comment">// Lifecycle hook      </span>

    }
}
</code></pre>
<p>The Projects route components have a <code>template</code> property that so far holds a basic markup that only spit out an <code>h1</code> title.</p>
<p>After that there is the component's data method that returns an empty object, then an empty methods object and an empty lifecycle hook.</p>
<p>That's all we need, for now, so let's move on and define the rest of the building blocks, the routes, the router and the Vue instances.</p>
<h3 id="heading-how-to-define-the-routes-router-and-vue-instance">How to define the routes, router, and Vue instance</h3>
<p>Now that we have two components to render on our main pages we can move forward to the next steps:</p>
<ul>
<li>define routes</li>
<li>create the router instance</li>
<li>create and mount the Vue instance</li>
</ul>
<p>First, let's define our two routes and link the components.</p>
<pre><code class="lang-js">
<span class="hljs-comment">// Define routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects},
];
</code></pre>
<p>In the code, we have defined a new constant called routes. In it, we defined two routes as an array of objects. </p>
<p>Each object has two properties:</p>
<ul>
<li>path</li>
<li>component</li>
</ul>
<p>The first object is for the homepage. Its path will respond to requests made to our website base URL, like https://fabiopacifici.com/.</p>
<p>Then the component property links this page to the route's component called <code>Home</code> that we defined in the previous step. </p>
<p>The second object is for the projects page. The path responds to requests made to <code>/projects</code> and it's linked to the <code>Projects</code> route component.</p>
<p>Now that we have our routes:</p>
<pre><code class="lang-js">
<span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
    routes
})
</code></pre>
<p>Above we used the ES6 syntax that allows us to just put the name of the variable holding the routes since it is equal to the name of the property that we needed to use. It's actually the same as writing <code>routes: routes</code>.</p>
<p>Now, we create a Vue instance. Inject the router instance inside it and finally mount the root element. </p>
<pre><code class="lang-js">
<span class="hljs-comment">// create and mount the vue instance</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    router
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>Done! We now have everything in place to start building our portfolio and complete the Projects route component.</p>
<h2 id="heading-how-to-build-the-main-projects-route-component">How to Build the Main Projects Route Component</h2>
<p>We will start working on the data object. Here we need to define properties that will hold all our projects once we fetch data from the git hub API.</p>
<h3 id="heading-the-data-object">The data object</h3>
<p>To keep things easier I have intentionally limited results to 20. If you feel this isn't enough you can change the code as you like. </p>
<p>You can implement pagination for your results by increasing the page property that will be passed to the query string or return more results per page by increasing the value of the <code>perPage</code> property.</p>
<pre><code class="lang-js">data() { 
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">projectsList</span>: <span class="hljs-literal">null</span>,
        <span class="hljs-attr">skills</span>: [],
        <span class="hljs-attr">projectsCount</span>: <span class="hljs-number">5</span>,
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">errors</span>: <span class="hljs-literal">false</span>,
        }
    },
</code></pre>
<p>As we learned in the section where we used Axios to fetch data from the GitHub REST API, there are a few properties we need to define. </p>
<p>The component's data function returns an object with a <code>projects</code> property where we will store all projects we fetch from GitHub.</p>
<p>Then we add a <code>projectsList</code> property that holds only a few projects at a time. We will use this property later to implement a very simple
load more feature in combination with the <code>projectsCount</code> property.</p>
<p>Then we have a <code>skills</code> property where we will store all languages used to build our projects. </p>
<p>We'll use the <code>perPage: 20</code> and <code>page: 1</code> properties to build the query string used to fetch data from GitHub. It will take 20 projects and 
return only the first page of results unless we change these values.</p>
<p>Finally, we have a <code>loading: true</code> property that we will use to check if the page is fetching data and an <code>errors: false</code> property that shows an error message in case we are unable to connect to the GitHub server.</p>
<p>In the next step, we will start working on all methods required to make our application work.</p>
<h3 id="heading-the-fetch-all-data-method">The fetch all data method</h3>
<p>The first method is the one we will use to fetch data from GitHub.</p>
<p>This method will make the Ajax call to the GitHub rest API using Axios and store the response in a property of the Vue instance:</p>
<pre><code class="lang-js">
 <span class="hljs-attr">fetchData</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            axios
            .get(<span class="hljs-string">`https://api.github.com/users/fbhood/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
            .then(
                <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {

                    <span class="hljs-built_in">this</span>.projects = response.data;
                    <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
                        <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
                            <span class="hljs-built_in">this</span>.skills.push(project.language)
                        };
                    });
                }
            )
            .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {
                <span class="hljs-built_in">console</span>.log(error);
                <span class="hljs-built_in">this</span>.errors = <span class="hljs-literal">true</span>;
            })
            .finally(<span class="hljs-function">() =&gt;</span> { 
                <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>
                <span class="hljs-built_in">this</span>.getProjects();
            })
        },
</code></pre>
<p>Let's break this down. First, we defined a method called <code>fetchData: function(){}</code>. This method uses Axios to make an API call to the
REST API. </p>
<p>In the <code>.get()</code> method we have built the URL also using the properties <code>perPage</code> and <code>page</code> as part of the query string.</p>
<p>The get method returns a promise so we used the <code>.then()</code> method on the promise to handle the response using an arrow function <code>response =&gt; {}</code>.</p>
<p>Inside the arrow function, we stored the response data inside the projects property of the Vue instance using <code>this.projects = response.data;</code>.</p>
<p>Next, we used a <code>forEach</code> loop to iterate over each project and store the language used in the repository as a skill using the code below:</p>
<pre><code class="lang-js"><span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
    <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
        <span class="hljs-built_in">this</span>.skills.push(project.language)
    };
});
</code></pre>
<p>We chained a <code>.catch</code> method to handle an error in case we are unable to connect to the rest API and fetch data. We will log the error to the
console and update the value of the <code>errors</code> property to true so that we can show a custom error message to the user later on. </p>
<p>Finally, we chained the <code>.finally()</code> method that will be executed after the response has been handled. We also updated the <code>loading</code> property and set it to false so that we can show the results to the user. </p>
<p>Inside the <code>finally</code> method we can also call a method (that we still have to create) and that we will use to slice the results later. </p>
<p>Let's build it.</p>
<h3 id="heading-the-get-projects-method">The get projects method</h3>
<p>This method takes a portion of the projects we actually stored in the <code>projects</code> property. We can use the <code>projectsList</code> property to store the slice and later implement a method to increment them with a show more button.</p>
<pre><code class="lang-js">
<span class="hljs-attr">getProjects</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

    <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.projectsList;

},
</code></pre>
<p>The getProjects method takes a portion of all projects stored in the <code>projects</code> property using the array slice method in conjunction with
the property <code>projectsCount</code> that is set to five. So it will store in there only the first five results and return them.</p>
<p>To add five more projects to the <code>projectsList</code> property we will also need a method that the user can call when he clicks on the load more button. Let's create it.</p>
<h3 id="heading-the-load-more-projects-method">The load more projects method</h3>
<p>The load more method will first check if the length of the <code>projects</code> array is less than or equal to the length of the <code>projectsList</code> array. Then, if not, it will increment the value of the <code>projectsCount</code> property by five and then take a bigger slice from the <code>projects</code> property.</p>
<pre><code class="lang-js">
loadMore(){

    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.projectsList.length &lt;= <span class="hljs-built_in">this</span>.projects.length){
        <span class="hljs-built_in">this</span>.projectsCount += <span class="hljs-number">5</span>;
        <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount)
    }


}
</code></pre>
<h3 id="heading-build-the-template">Build the template</h3>
<p>In the template property of the <code>Projects</code> component, we can start with the header section. We'll also put in there two <code>router-link</code> components for the pages navigation:</p>
<pre><code class="lang-html">`<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"site_header"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container d_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">"bio__media"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/img/avatar.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media__text"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Python Expert<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. <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">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">'/'</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Project<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</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://"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-github fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Next, we can continue working on the template and create the main section.
We will put the following markup always in the main template div, right under the header closing tag.</p>
<p>Let's start by placing the main container:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Show an error message if the REST API doensn't work --&gt;</span>
    <span class="hljs-comment">&lt;!-- Otherwise show  a section for our portfolio projects and skills section--&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
</code></pre>
<p>Inside the container, let's use the v-if-else directives to show an error message or the projects section:</p>
<pre><code class="lang-html"> <span class="hljs-comment">&lt;!-- Show Errors if the rest api doesn't work --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"errors"</span>&gt;</span> 
        Sorry! It seems we can't fetch data righ now 😥
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Else show the portfolio section --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>To make the code work, we used the v-if directive and passed to it the <code>errors</code> property. This property will be set to <code>true</code> if there is an error while we fetch data from GitHub or will be set to <code>false</code> if everything is ok. So the v-else directive will render the portfolio section.</p>
<p>Next, we need to show a 'loading...' message while we fetch data. When done we can use the v-for directive to loop over the results. So right in the portfolio section, we will write another v-if-else directive.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span>
 <span class="hljs-comment">&lt;!-- Use a v-if directive to show the loading message --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"loading"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"loading"</span>&gt;</span>😴 Loading ... <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- use a v-for directive to loop over the projectsList array --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> <span class="hljs-attr">v-else</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre>
<p>Here we use the v-if directive <code>&lt;div class="loading" v-if="loading"&gt;😴 Loading ... &lt;/div&gt;</code> to render a loading message. After that the <code>&lt;div v-for="project in projectsList" class="card__custom" v-else&gt;&lt;/div&gt;</code>
has two directives, the v-for directive that we use to loop over the <code>projectsList</code> property and a v-else directive that will show this element when we are done fetching data from GitHub.</p>
<p>Now we can use the <code>project</code> variable to render all project details in our markup:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- use a v-for directive to loop over the projectsList array --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</span> <span class="hljs-attr">v-else</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__custom__text"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Create a custom method to trim the project name so that it doesn't break the design --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{project.name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Create a custom trimmedText to trim the description --&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{project.description}}<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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"meta__data d_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">"date"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Updated at<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{new Date(project.updated_at).toDateString()}}<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">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</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">"card__custom__img"</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">"card_custom__button"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
            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">div</span>&gt;</span>
</code></pre>
<p>To render the project title and desciption we used the propeties <code>poject.name</code> and <code>project.description</code>. But the description and the title will break our design unless we trim them at some point. </p>
<p>Next in the element with class <code>date</code> we rendered the poject data in a readable fomat using the <code>new Data().toDateString()</code> method. </p>
<p>To render the user avatar <code>&lt;img class="avatar" :src="project.owner.avatar_url"&gt;</code> we used the shortcut for the v-bind diective so that we could use the property <code>project.owner.avatar_url</code> to grab the avatar URL. </p>
<p>Finally, to render a button that once clicked redirects the user to the repository page we bound the <code>href</code> attribute to the <code>project.html_url</code> property <code>&lt;a :href="project.html_url" target="_blank"&gt;Code&lt;/a&gt;</code>.</p>
<p>Our project card is complete. The next thing we need to do is render a load more button to show more projects. </p>
<p>We are still working inside the <code>projects</code> section. Right after the project card we can write the following markup</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Render a load more button --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"text-align: center; width:100%"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!loading"</span> &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"projectsList.length &lt; projects.length"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn_load_more"</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"loadMore()"</span>&gt;</span>Load More<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">""</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>&gt;</span>Visit My GitHub<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>
</code></pre>
<p>The v-if directive first checks if the loading property is set to false. 
If so, we'll use another v-if directive to check if the length of property <code>projectsList</code> is less than the length of the property <code>projects</code>. </p>
<p>If so, it will show a button that uses a v-on directive to listen for clicks 
<code>&lt;button class="btn_load_more" v-on:click="loadMore()"&gt;Load More&lt;/button&gt;</code> and trigger a <code>loadMore()</code> method. Otherwise, we show a link to the GitHub account. </p>
<p>After this, we can show a list of skills related to all the projects:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Show a skills section --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"skills_section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Development Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- Loop over the skills property --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"skill in skills"</span>&gt;</span>{{skill}}<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 class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Our markup is complete, but we need to improve our code a little as the project title and its description are breaking our design! </p>
<p>Let's create two methods, one to trim the title and one for the description text.</p>
<h3 id="heading-the-trimtext-and-trimtitle-methods">The trimText and trimTitle methods</h3>
<p>The <code>trimTitle</code> method will replace all <code>-</code> and <code>_</code> with a space, and restrict the number of characters to 12. The <code>trimText</code> method instead only reduces the number of characters of the description in excess of 100 characters. </p>
<pre><code class="lang-js">trimTitle: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
    <span class="hljs-keyword">let</span> title = text.replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">" "</span>).replace(<span class="hljs-string">"_"</span>, <span class="hljs-string">" "</span>)
    <span class="hljs-keyword">if</span>(title.length &gt; <span class="hljs-number">15</span>) {
        <span class="hljs-keyword">return</span> title.slice(<span class="hljs-number">0</span>, <span class="hljs-number">12</span>) + <span class="hljs-string">' ...'</span>
    } <span class="hljs-keyword">return</span> title;

},
<span class="hljs-attr">trimText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
    <span class="hljs-comment">//console.log(text.slice(0, 100));</span>
    <span class="hljs-keyword">if</span>(text.length &gt; <span class="hljs-number">100</span>) {
        <span class="hljs-keyword">return</span> text.slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>) + <span class="hljs-string">' ...'</span>
    } <span class="hljs-keyword">return</span> text;
},
</code></pre>
<p>With these two methods, now we can update the markup and use them to make sure nothing breaks the design.</p>
<p>Let's update these two lines that will be changed from this:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Create a custom method to trim the project name so that it doesn't break the design --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{project.name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
<span class="hljs-comment">&lt;!-- Create a custom trimmedText to trim the description --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{project.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>To this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{{trimedTitle(project.name)}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{trimedText(project.description)}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>Let's put eveything together. The final markup will be the following:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"site_header"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container d_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">"bio__media"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/img/avatar.svg"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bio__media__text"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Python Expert<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem ipsum dolor sit amet consectetur adipisicing elit. <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">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">'/'</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>&gt;</span>Project<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</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://"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"fab fa-github fa-lg fa-fw"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</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">nav</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</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"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"errors"</span>&gt;</span> 
            Sorry! It seems we can't fetch data righ now 😥
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"portfolio"</span> <span class="hljs-attr">v-else</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"loading"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"loading"</span>&gt;</span>😴 Loading ... <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">"projects"</span> <span class="hljs-attr">v-else</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"project in projectsList"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"card__custom"</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__custom__text"</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">h3</span>&gt;</span>{{trimedTitle(project.name)}}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{trimedText(project.description)}}<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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"meta__data d_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">"date"</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Updated at<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{{new Date(project.updated_at).toDateString()}}<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">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"avatar"</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"project.owner.avatar_url"</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">"card__custom__img"</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">"card_custom__button"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">:href</span>=<span class="hljs-string">"project.html_url"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
                            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">div</span>&gt;</span>


                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"text-align: center; width:100%"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!loading"</span> &gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"projectsList.length &lt; projects.length"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn_load_more"</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"loadMore()"</span>&gt;</span>Load More<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">""</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>&gt;</span>Visit My GitHub<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">id</span>=<span class="hljs-string">"skills_section"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Development Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"skills"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"skill in skills"</span>&gt;</span>{{skill}}<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 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">section</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">div</span>&gt;</span>
</code></pre>
<p>There is one last thing to do. Since fetching data from GitHub is very fast we don't really see the loading message. Let's set a timeout and delay it by a few seconds – then you can tune it as you like.</p>
<h3 id="heading-the-mounted-lifecycle-hook">The mounted lifecycle hook</h3>
<pre><code> mounted(){  

        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-built_in">this</span>.fetchData, <span class="hljs-number">3000</span> );

    }
</code></pre><p>Inside the mounted lifecycle hook we used <code>setTimeout()</code> and called the <code>fetchData</code> method as the first parameter. Then for the second parameter we specified that this method should be executed after 3000 milliseconds (3seconds). </p>
<h2 id="heading-lets-see-our-final-code-all-toghether">Let's see our final code all toghether</h2>
<p>Index.html file looks like the following:</p>
<pre><code class="lang-html">
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</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.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Vuefolio<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"preconnect"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.gstatic.com"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?family=Raleway:wght@100;300;400;900&amp;display=swap"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://use.fontawesome.com/releases/v5.1.1/css/all.css"</span>
        <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./assets/css/style.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>

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

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>

        <span class="hljs-comment">&lt;!-- Render the component for the corresponding route --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">router-view</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> © Developed by <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fabiopacifici.com"</span>&gt;</span>Fabio Pacific<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Axios --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/axios/dist/axios.min.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- VueJS development version --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Vue Router --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/vue-router@2.0.0/dist/vue-router.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Main Js file --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/js/main.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</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>And this is the main.js file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Create route components</span>
<span class="hljs-keyword">const</span> Home = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;main id="home"&gt;
        &lt;div class="about__me"&gt;
            &lt;img src="./assets/img/avatar.svg" alt=""&gt;
            &lt;h1&gt;John Doe&lt;/h1&gt;
            &lt;h3&gt;Python Expert&lt;/h3&gt;
            &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;

            &lt;div class="skills_projects_link"&gt;&lt;router-link to="/projects"&gt;Projects/Skills&lt;/router-link&gt; &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;`</span>
}
<span class="hljs-keyword">const</span> Projects = {
    <span class="hljs-attr">template</span>: 
    <span class="hljs-string">`&lt;div&gt;
        &lt;header id="site_header" class="container d_flex"&gt;
            &lt;div class="bio__media"&gt;
                &lt;img src="./assets/img/avatar.svg" alt=""&gt;
                &lt;div class="bio__media__text"&gt;
                    &lt;h1&gt;John Doe&lt;/h1&gt;
                    &lt;h3&gt;Python Expert&lt;/h3&gt;
                    &lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. &lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;nav&gt;
                &lt;router-link to='/'&gt;Home&lt;/router-link&gt;
                &lt;router-link to="/projects"&gt;Project&lt;/router-link&gt;
                &lt;a href="https://"&gt;
                    &lt;i class="fab fa-github fa-lg fa-fw"&gt;&lt;/i&gt;
                &lt;/a&gt;
            &lt;/nav&gt;
        &lt;/header&gt;

         &lt;main class="container"&gt;
            &lt;div class="error" v-if="errors"&gt; 
                Sorry! It seems we can't fetch data righ now 😥
            &lt;/div&gt;

            &lt;section id="portfolio" v-else&gt;
                &lt;div class="loading" v-if="loading"&gt;😴 Loading ... &lt;/div&gt;
                &lt;div class="projects" v-else&gt;
                     &lt;div v-for="project in projectsList" class="card__custom" &gt;
                        &lt;div class="card__custom__text"&gt;
                            &lt;div&gt;
                                &lt;h3&gt;{{trimedTitle(project.name)}}&lt;/h3&gt;
                                &lt;p&gt;{{trimedText(project.description)}}&lt;/p&gt;                        
                            &lt;/div&gt;

                            &lt;div class="meta__data d_flex"&gt;
                                &lt;div class="date"&gt;
                                    &lt;h5&gt;Updated at&lt;/h5&gt;
                                    &lt;div&gt;{{new Date(project.updated_at).toDateString()}}&lt;/div&gt;
                                &lt;/div&gt;
                                &lt;img class="avatar" :src="project.owner.avatar_url"&gt;

                            &lt;/div&gt;
                        &lt;/div&gt;
                        &lt;div class="card__custom__img"&gt;&lt;/div&gt;
                        &lt;div class="card_custom__button"&gt;
                            &lt;a :href="project.html_url" target="_blank"&gt;
                                Code
                            &lt;/a&gt;
                        &lt;/div&gt;


                    &lt;/div&gt;


                    &lt;div style="text-align: center; width:100%" v-if="!loading" &gt;
                        &lt;div v-if="projectsList.length &lt; projects.length"&gt;
                            &lt;button class="btn_load_more" v-on:click="loadMore()"&gt;Load More&lt;/button&gt;
                        &lt;/div&gt;
                        &lt;div v-else&gt;
                            &lt;a href="" target="_blank" rel="noopener noreferrer"&gt;Visit My GitHub&lt;/a&gt;
                        &lt;/div&gt;

                    &lt;/div&gt;

                    &lt;div id="skills_section"&gt;
                        &lt;h2&gt;Development Skills&lt;/h2&gt;
                        &lt;ul class="skills"&gt;
                            &lt;li v-for="skill in skills"&gt;{{skill}}&lt;/li&gt;
                        &lt;/ul&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/section&gt;


        &lt;/main&gt;
    &lt;/div&gt;`</span>,
data() { 
    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">data</span>: [],
        <span class="hljs-attr">projects</span>: [],
        <span class="hljs-attr">projectsList</span>: <span class="hljs-literal">null</span>,
        <span class="hljs-attr">skills</span>: [],
        <span class="hljs-attr">projectsCount</span>: <span class="hljs-number">5</span>,
        <span class="hljs-attr">perPage</span>: <span class="hljs-number">20</span>,
        <span class="hljs-attr">page</span>: <span class="hljs-number">1</span>,
        <span class="hljs-attr">loading</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">errors</span>: <span class="hljs-literal">false</span>,
        }
    },
    <span class="hljs-attr">methods</span>: {
        <span class="hljs-attr">trimedTitle</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
            <span class="hljs-keyword">let</span> title = text.replaceAll(<span class="hljs-string">"-"</span>, <span class="hljs-string">" "</span>).replace(<span class="hljs-string">"_"</span>, <span class="hljs-string">" "</span>)
            <span class="hljs-keyword">if</span>(title.length &gt; <span class="hljs-number">15</span>) {
                <span class="hljs-keyword">return</span> title.slice(<span class="hljs-number">0</span>, <span class="hljs-number">12</span>) + <span class="hljs-string">' ...'</span>
            } <span class="hljs-keyword">return</span> title;

        },
        <span class="hljs-attr">trimedText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">text</span>)</span>{
            <span class="hljs-comment">//console.log(text.slice(0, 100));</span>
            <span class="hljs-keyword">if</span>(text === <span class="hljs-literal">null</span>) {
                <span class="hljs-keyword">return</span> <span class="hljs-string">'This project has no description yet!'</span>;
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(text.length &gt; <span class="hljs-number">100</span>) {
                <span class="hljs-keyword">return</span> text.slice(<span class="hljs-number">0</span>, <span class="hljs-number">100</span>) + <span class="hljs-string">' ...'</span>
            } 
            <span class="hljs-keyword">return</span> text;

        },
        <span class="hljs-attr">getProjects</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

            <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount);
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.projectsList;

        },
        <span class="hljs-attr">fetchData</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
            axios
            .get(<span class="hljs-string">`https://api.github.com/users/fbhood/repos?per_page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.perPage}</span>&amp;page=<span class="hljs-subst">${<span class="hljs-built_in">this</span>.page}</span>`</span>)
            .then(
                <span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
                    <span class="hljs-built_in">this</span>.projects = response.data;
                    <span class="hljs-built_in">this</span>.projects.forEach(<span class="hljs-function"><span class="hljs-params">project</span> =&gt;</span>{
                        <span class="hljs-keyword">if</span> (project.language !== <span class="hljs-literal">null</span> &amp;&amp; ! <span class="hljs-built_in">this</span>.skills.includes(project.language)) { 
                            <span class="hljs-built_in">this</span>.skills.push(project.language)
                        };
                    });
                }
            )
            .catch(<span class="hljs-function"><span class="hljs-params">error</span>=&gt;</span> {
                <span class="hljs-built_in">console</span>.log(error);
                <span class="hljs-built_in">this</span>.errors = <span class="hljs-literal">true</span>;
            })
            .finally(<span class="hljs-function">() =&gt;</span> { 
                <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>
                <span class="hljs-built_in">this</span>.getProjects();
            })
        }, 
        loadMore(){

            <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.projectsList.length &lt;= <span class="hljs-built_in">this</span>.projects.length){
                <span class="hljs-built_in">this</span>.projectsCount += <span class="hljs-number">5</span>;
                <span class="hljs-built_in">this</span>.projectsList = <span class="hljs-built_in">this</span>.projects.slice(<span class="hljs-number">0</span>, <span class="hljs-built_in">this</span>.projectsCount)
            }


        }

    },
    mounted(){  

        <span class="hljs-built_in">setTimeout</span>(<span class="hljs-built_in">this</span>.fetchData, <span class="hljs-number">3000</span> );

    }
}

<span class="hljs-comment">// Define routes</span>
<span class="hljs-keyword">const</span> routes = [
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>, <span class="hljs-attr">component</span>: Home},
    {<span class="hljs-attr">path</span>: <span class="hljs-string">'/projects'</span>, <span class="hljs-attr">component</span>: Projects},
];


<span class="hljs-comment">// create the router instance</span>
<span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({
    routes
})

<span class="hljs-comment">// create and mount the vue instance</span>
<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> Vue({
    router
}).$mount(<span class="hljs-string">'#app'</span>);
</code></pre>
<p>On the CSS side this is what we have:</p>
<pre><code class="lang-css">
<span class="hljs-comment">/* Utility Classes */</span>
<span class="hljs-selector-class">.d_none</span> {
    <span class="hljs-attribute">display</span>: none;
}
<span class="hljs-selector-class">.d_flex</span> {
    <span class="hljs-attribute">display</span>: flex;
}
<span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1170px</span>;
    <span class="hljs-attribute">margin</span>: auto;
}
<span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">color</span>: white;
} 
<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>:<span class="hljs-number">#DB5461</span>;

}
<span class="hljs-selector-class">.loading</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-comment">/* END Utility Classes */</span>
<span class="hljs-comment">/* Components */</span>
<span class="hljs-selector-class">.bio__media</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: flex-start;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">text-align</span>: left;
}
<span class="hljs-selector-class">.bio__media</span> <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">120px</span>;
}
<span class="hljs-selector-class">.bio__media__text</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
}
<span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">h1</span>{
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;

}
<span class="hljs-selector-class">.bio__media__text</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;

}

<span class="hljs-selector-class">.card__custom</span> {
    <span class="hljs-attribute">position</span>: relative;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">400px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">3rem</span>;
    <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;
    <span class="hljs-attribute">flex-basis</span>: <span class="hljs-built_in">calc</span>(<span class="hljs-number">100%</span> /<span class="hljs-number">2</span>);
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: space-between;
}
<span class="hljs-selector-class">.card__custom</span> &gt; <span class="hljs-selector-class">.card__custom__text</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-built_in">calc</span>((<span class="hljs-number">100%</span> / <span class="hljs-number">3</span>) *<span class="hljs-number">2</span>);
    <span class="hljs-attribute">text-align</span>: right;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">80%</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;
    <span class="hljs-attribute">justify-content</span>: space-around;
    <span class="hljs-attribute">overflow</span>: hidden;

}
<span class="hljs-selector-class">.card__custom__img</span> {

    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">70%</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/cards_bg_img.svg);
    <span class="hljs-attribute">background-position</span>: center;
    <span class="hljs-attribute">background-repeat</span>: no-repeat;
    <span class="hljs-attribute">background-size</span>: contain;
    <span class="hljs-attribute">display</span>: inline-block;
    <span class="hljs-attribute">z-index</span>: -<span class="hljs-number">1</span>;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">60%</span>;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">85px</span> <span class="hljs-number">0</span> <span class="hljs-number">100px</span> <span class="hljs-number">25px</span>;

}
<span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span>, <span class="hljs-selector-class">.btn_load_more</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#F1EDEE</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">54px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#3D5467</span>;
}
<span class="hljs-selector-class">.card_custom__button</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-class">.btn_load_more</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">cursor</span>: pointer;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#324555</span>;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: <span class="hljs-number">1s</span>;
}
<span class="hljs-selector-class">.card__custom__text</span> <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.5rem</span>;
}
<span class="hljs-comment">/* END Componenet */</span>
* {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">body</span>{
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Raleway'</span>, Arial, Helvetica, sans-serif;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-built_in">linear-gradient</span>(<span class="hljs-number">116.82deg</span>, #<span class="hljs-number">3</span>D5467 <span class="hljs-number">0%</span>, #<span class="hljs-number">1</span>A232B <span class="hljs-number">99.99%</span>, #<span class="hljs-number">333333</span> <span class="hljs-number">100%</span>);

}
<span class="hljs-selector-tag">a</span> {
 <span class="hljs-attribute">text-decoration</span>: none;   
}

<span class="hljs-comment">/* Home Page */</span>
<span class="hljs-selector-tag">main</span><span class="hljs-selector-id">#home</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">600px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">justify-content</span>: center;
    <span class="hljs-attribute">align-items</span>: center;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5rem</span>;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">36px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span> {
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">28px</span>;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">500</span>;

}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h1</span>, <span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> &gt; <span class="hljs-selector-tag">h3</span>  {
    <span class="hljs-attribute">font-style</span>: normal;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">42px</span>;
    <span class="hljs-attribute">letter-spacing</span>: <span class="hljs-number">0.115em</span>;

}
<span class="hljs-selector-id">#home</span> &gt; <span class="hljs-selector-class">.about__me</span> <span class="hljs-selector-tag">p</span> {
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">22px</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span>;
}
<span class="hljs-selector-class">.skills_projects_link</span> {
    <span class="hljs-attribute">position</span>: relative;
}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">21px</span>;

}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.5s</span> ease-in-out;

}
<span class="hljs-selector-class">.skills_projects_link</span> &gt; <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">::after</span> {
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">50%</span>;
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateX</span>(-<span class="hljs-number">50%</span>);
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">margin</span>: auto;
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">content</span>: <span class="hljs-string">""</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">2px</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">transition</span>: background-color <span class="hljs-number">0.5s</span> ease-in-out;

}

<span class="hljs-comment">/* Header */</span>
<span class="hljs-selector-id">#site_header</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">justify-content</span>: space-between;
    <span class="hljs-attribute">align-items</span>: center;
}
<span class="hljs-selector-id">#site_header</span> &gt; <span class="hljs-selector-tag">h1</span> {
    <span class="hljs-attribute">text-transform</span>: uppercase;
}
<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#e2e2e2</span>;
    <span class="hljs-attribute">text-transform</span>: uppercase;
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
}
<span class="hljs-selector-tag">nav</span> <span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#DB5461</span>;
}
<span class="hljs-comment">/* Portfolio Page Section */</span>

<span class="hljs-selector-id">#portfolio</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;

}


<span class="hljs-selector-id">#portfolio</span> <span class="hljs-selector-class">.projects</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-wrap</span>: wrap;
    <span class="hljs-attribute">align-items</span>: center;
    <span class="hljs-attribute">justify-content</span>: space-around;
}
<span class="hljs-comment">/* Skills */</span>

<span class="hljs-selector-id">#skills_section</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">4rem</span>;
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">300px</span>;
    <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">url</span>(../img/skills_bg.svg);
    <span class="hljs-attribute">background-repeat</span>: no-repeat;
    <span class="hljs-attribute">background-size</span>: contain;
    <span class="hljs-attribute">background-position</span>: top left;
}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">h2</span> {
    <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">180px</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">44px</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#F1EDEE</span>;
    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">2rem</span>;

}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span> {
    <span class="hljs-attribute">list-style</span>: none;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> <span class="hljs-number">120px</span>;
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-wrap</span>: wrap;

}
<span class="hljs-selector-id">#skills_section</span> <span class="hljs-selector-tag">ul</span>  <span class="hljs-selector-tag">li</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">0.5rem</span>;
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#DB5461</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">5px</span> solid <span class="hljs-number">#3D5467</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">35px</span>;
}



<span class="hljs-selector-class">.avatar</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">30px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;

}


<span class="hljs-selector-class">.card__back</span> {
    <span class="hljs-attribute">display</span>: none;
}
<span class="hljs-selector-class">.rotate__card</span> {
    <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate3d</span>(<span class="hljs-number">360</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">180deg</span>);
}
<span class="hljs-comment">/* Site Footer */</span>

<span class="hljs-selector-tag">footer</span> {
    <span class="hljs-attribute">text-align</span>: center;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">2rem</span> <span class="hljs-number">0</span>;
}



<span class="hljs-comment">/* Media Query  */</span>

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">475px</span>) {
    <span class="hljs-selector-class">.card</span> {
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">100%</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    }
}
</code></pre>
<p>That's it! We are ready to deply our code to production.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/I6hQnWQU4rQ" 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-continuos-deployment-with-bitbucket-and-netlify">Continuos Deployment with BitBucket and Netlify</h2>
<p>The final step is to deploy our projects so that others can see them. To do that we will use two services:</p>
<ul>
<li>BitBucket, a git-based source code repository for hosting (you can use GitHub if you prefer)</li>
<li>Netlify, a web hosting company that provides hosting for websites that have source code files stored in a Git version control system.</li>
</ul>
<p>You can watch this final video on <a target="_blank" href="https://youtu.be/BH5I68DzcYQ">YouTube here</a>.</p>
<p>You can also checkout the final repositories on BitBucket: </p>
<ul>
<li><a target="_blank" href="https://bitbucket.org/fbhood/simple-twitter/src/master/">SimpleTwitter</a></li>
<li><a target="_blank" href="https://bitbucket.org/fbhood/vue-folio/src">VuePortfolio</a></li>
</ul>
<h3 id="heading-create-the-project-folders-and-copy-all-files">Create the project folders and copy all files</h3>
<p>First, create two folders – one for each project:</p>
<ul>
<li>vue-folio</li>
<li>simple-twitter
then copy all projects files in the related folder.</li>
</ul>
<h3 id="heading-initialize-a-git-repository">Initialize a git repository</h3>
<p>Next, we need to initialise the git repository locally.</p>
<pre><code>cd vue-folio 
git init
git add .
git commit -m<span class="hljs-string">"Initial Commit"</span>
</code></pre><p>You need to execute the commands above in a termial, so you need to have git installed on you system. If you don't, you can read about how to do that <a target="_blank" href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">here</a>.</p>
<p>With the first command, we navigate to the project folder called <code>vue-folio</code>. Then we initialise a git repository, add all files to the staging area, and commit the files.</p>
<p>Repeat the steps above for both projects folders.</p>
<h3 id="heading-create-a-bitbucket-or-github-repository">Create a BitBucket or GitHub repository</h3>
<p>I assume you already have an account with GitHub o BitBucket. But if you don't, then go over and create one.</p>
<p>Follow the steps in the video to create the repositories and connect them with you local repositories.</p>
<h3 id="heading-create-an-account-with-netlify-and-create-a-site">Create an account with Netlify and create a site</h3>
<p>You can use Netlify's free plan for private projects, hobby websites, and experiments. It's a perfect fit for our tutorial. </p>
<p>Follow the steps in the video to deploy your projects there.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/BH5I68DzcYQ" 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-whats-next">What's Next?</h2>
<p>In an upcoming tutorial I'll show you also how to test your code, upgrade to Vue3, and more.</p>
<h3 id="heading-thank-you-for-reading">Thank you for reading!</h3>
<p>I hope you enjoyed this tutorial and the accompanying videos. If so, please share the article and hit the like button on the videos. You can also enable notifications by clicking on the bell icon to know when my next video is online. </p>
<p>If you have any questions please just reach out to me. I reply to all YouTube comments. </p>
<p>Dont forget to subscribe to my YouTube Channel <a target="_blank" href="https://youtube.com/channel/UCTuFYi0pTsR9tOaO4qjV_pQ">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
