Blog

Effortlessly build blogs, product guides, and API docs with Hashnode.

Create a Standout Developer Portfolio: Build Your Personal Brand with HTML, Tailwind, Hashnode, and Vercel.

9 min read

Cover Image for Create a Standout Developer Portfolio: Build Your Personal Brand with HTML, Tailwind, Hashnode, and Vercel.

The job market is tough, and if you are looking for opportunities, you need to stand out. A good way to do that, is by building a developer portfolio. It can not only help you have a proof of work, but also help create your personal brand.

Even if we all know the importance of having a portfolio, we often end up not building at all, because of contemplation or wanting to over-complicate it.

In this article, I will show you how you can build a simple yet effective portfolio using:

  • HTML

  • Tailwind CSS

  • Hashnode and

  • Vercel for deployment

You can either follow this guide or watch me build it live here:

Getting started

The first step in building a portfolio or any project is to have a clear design or structure in mind. For a developer portfolio, we need to include a homepage that introduces us and highlights our skills, a section showcasing our projects, a list of articles that we have written, and links to our social media profiles.

Here's how I envisioned it to be ๐Ÿ‘‡

Let us divide it into three smaller tasks:

  • Building the homepage that includes our bio, and projects.

  • Using Hashnode to build our blog section

  • Deploying it using Vercel.

Part 1: Building the homepage

To get started, make sure you have a repository setup on GitHub, and have cloned it on an IDE of your choice.

The next step is to install Tailwind:

  1. On your terminal type npm install -D tailwindcss followed by npx tailwindcss init.

  2. Add the paths to all of your template files in your tailwind.config.js file.

     /** @type {import('tailwindcss').Config} */
     module.exports = {
       content: ["./src/**/*.{html,js}"],
       theme: {
         extend: {},
       },
       plugins: [],
     }
    
  3. Add the @tailwind directives for each of Tailwindโ€™s layers to your main CSS file.

     @tailwind base; 
     @tailwind components; 
     @tailwind utilities;
    
  4. Run the CLI tool to scan your template files for classes and build your CSS npx tailwindcss -i ./src/input.css -o ./src/output.css --watch

  5. On your index.html file, add <link href="./output.css" rel="stylesheet"\>

For this demo, we will be using Flowbite components to design the portfolio.
Here are the commands to get started:

  • npm install flowbite

  • On you tailwind.config.js file add flowbite as the plugin:

      module.exports = {
    
          plugins: [
              require('flowbite/plugin')
          ]
    
      }
    
  • Before the end of the body tag, add the JavaScript code that powers the interactive elements: <script src="../path/to/flowbite/dist/flowbite.min.js"></script>

Building the header and the bio section:

<nav class="bg-white border-gray-200 dark:bg-gray-900">
    <div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
        <a href="https://www.linkedin.com/in/haimantika-mitra/" class="flex items-center space-x-3 rtl:space-x-reverse">
            <img class="w-10 h-10 rounded-full" src="img/hm.jpg" alt="Rounded avatar">
            <span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">Haimantika Mitra</span>
        </a>
      <button data-collapse-toggle="navbar-default" type="button" class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="navbar-default" aria-expanded="false">
          <span class="sr-only">Open main menu</span>
          <svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
              <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
          </svg>
      </button>
      <div class="hidden w-full md:block md:w-auto" id="navbar-default">
        <ul class="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 rtl:space-x-reverse md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700">
          <li>
            <a href="#home" class="block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500" aria-current="page">Home</a>
          </li>
          <li>
            <a href="https://starter-kit-ophg-git-new-portfolio-haimantikas-projects.vercel.app/blog" class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Blog</a>
          </li>
          <li>
            <a href="#projects" class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:border-0 md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent">Projects</a>
          </li>

        </ul>
      </div>
    </div>
  </nav>
    <main class="max-w-4xl mx-auto flex flex-col">
    <section id="home" class="flex flex-col sm:flex-row p-20 items-center gap-8 mb-12">

    <h1 class="mb-4 text-3xl font-extrabold text-gray-900 dark:text-white md:text-5xl lg:text-6xl"><span class="text-transparent bg-clip-text bg-gradient-to-r to-emerald-600 from-sky-400">Generalist & </span> Developer Advocate.</h1>
    <p class="text-lg font-normal text-gray-500 lg:text-xl dark:text-gray-400">Hello, I am Haimantika. Currently working as a Developer Advocate at Hashnode. I help B2D+ companies bridge the gap between developers and the product with code, content and community. As a community builder and a product of the community myself, I strongly believe that technology is for everyone. I am passionate about learning, sharing, and building with the community.</p>

    </section>

Building the projects section using Flowbite's card component:

 <main class="max-w-4xl mx-auto flex flex-col">
    <section id="home" class="flex flex-col sm:flex-row p-20 items-center gap-8 mb-12">

    <h1 class="mb-4 text-3xl font-extrabold text-gray-900 dark:text-white md:text-5xl lg:text-6xl"><span class="text-transparent bg-clip-text bg-gradient-to-r to-emerald-600 from-sky-400">Generalist & </span> Developer Advocate.</h1>
    <p class="text-lg font-normal text-gray-500 lg:text-xl dark:text-gray-400">Hello, I am Haimantika. Currently working as a Developer Advocate at Hashnode. I help B2D+ companies bridge the gap between developers and the product with code, content and community. As a community builder and a product of the community myself, I strongly believe that technology is for everyone. I am passionate about learning, sharing, and building with the community.</p>

    </section>
    <main class="max-w-4xl mx-auto flex flex-col">
        <section id="projects" class="flex flex-col sm:flex-row p-20 items-center gap-8 mb-12">


<div class="max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700">
    <a href="#">
        <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Melody Matcher</h5>
    </a>
    <p class="mb-3 font-normal text-gray-700 dark:text-gray-400">Happy? Sad? Angry? Want a music blend made just for you? Try out the soul music generator. Built using: Next.JS, and QuickBlox API.</p>
    <a href="https://music-suggester.vercel.app/" class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
        Try it here
        <svg class="rtl:rotate-180 w-3.5 h-3.5 ms-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
            <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
        </svg>
    </a>
</div>



<div class="max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700">
    <a href="#">
        <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Diss Track</h5>
    </a>
    <p class="mb-3 font-normal text-gray-700 dark:text-gray-400">Got hate comments? Time to turn it into a rap! Try out Diss Track. Built using: TailwindCSS (DaisyUI components), and QuickBlox API.</p>
    <a href="https://disstrack.vercel.app/" class="inline-flex items-center px-3 py-2 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
        Try it here
        <svg class="rtl:rotate-180 w-3.5 h-3.5 ms-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 10">
            <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 5h12m0 0L9 1m4 4L9 9"/>
        </svg>
    </a>
</div>
</main>

Building the footer:

<footer class="bg-white rounded-lg shadow dark:bg-gray-900 m-4">
    <div class="w-full max-w-screen-xl mx-auto p-4 md:py-8">
        <div class="sm:flex sm:items-center sm:justify-between">

            <ul class="flex flex-wrap items-center mb-6 text-sm font-medium text-gray-500 sm:mb-0 dark:text-gray-400">
                <li>
                    <a href="https://www.linkedin.com/in/haimantika-mitra/" class="hover:underline me-4 md:me-6">LinkedIn</a>
                </li>
                <li>
                    <a href="#" class="hover:underline me-4 md:me-6">GitHub</a>
                </li>
                <li>
                    <a href="#" class="hover:underline me-4 md:me-6">Twitter</a>
                </li>
                <li>
                    <a href="#" class="hover:underline">Resume</a>
                </li>
            </ul>
        </div>

</footer>

Part 2: Building the blog section

To build the blog section, we will make use of Hashnode in Headless mode and it's open-source starter kit.

  1. The first step is to fork it and create your own copy.

  2. Clone it locally using any IDE of your choice.

  3. Currently, Hashnode has three themes: personal, hashnode and enterprise. You can use any, but in this demo, we will use the enterprise theme.

  4. It's a monorepo, so cd into packages/blog-starter-kit/themes/enterprise, and then type pnpm install to install all the necessary packages.

  5. The repository, will include a .env.example file. Copy the contents and create a .env file that includes your blog publication details. You will only need to update NEXT_PUBLIC_HASHNODE_PUBLICATION_HOST, rest everything remains the same.

This is where you will find your Hashnode publication name, for me, it is haimantika.dev ๐Ÿ‘‡

  1. Once all of it is done, use the command npm run dev to run it locally.

  2. The starter kit uses Next.js and Tailwind, allowing you to customize the styling. In this demo, we modify the footer to match our homepage footer. The code for that looks like:

     import Link from 'next/link';
     import { Container } from './container';
     import { useAppContext } from './contexts/appContext';
     import { SocialLinks } from './social-links';
    
     export const Footer = () => {
    
         return (
             <footer className="bg-white rounded-lg shadow dark:bg-gray-900 m-4">
         <div className="w-full max-w-screen-xl mx-auto p-4 md:py-8">
             <div className="sm:flex sm:items-center sm:justify-between">
                 <ul className="flex flex-wrap items-center mb-6 text-sm font-medium text-gray-500 sm:mb-0 dark:text-gray-400">
                     <li>
                         <a href="https://www.linkedin.com/in/haimantika-mitra/" className="hover:underline me-4 md:me-6">LinkedIn</a>
                     </li>
                     <li>
                         <a href="#" className="hover:underline me-4 md:me-6">GitHub</a>
                     </li>
                     <li>
                         <a href="#" className="hover:underline me-4 md:me-6">Twitter</a>
                     </li>
                     <li>
                         <a href="#" className="hover:underline">Resume</a>
                     </li>
                 </ul>
             </div>
         </div>
     </footer>
    
         );
     }
    

If you want to make other changes, check out this video:

Part 3: Deploying it using Vercel

We will need to deploy the homepage and the blog separately. To deploy the homepage, you can follow Vercel's documentation.

Let us learn how to deploy the blog section:

  • Create a new project on Vercel and connect this starter-kit repo.

  • Since it's a monorepo, and you choose the enterprise theme, you need to choose packages/blog-starter-kit/themes/enterpriseas the root directory while importing on Vercel.

  • Choose Next.js as framework preset.

  • Set the following environment variables

      NEXT_PUBLIC_HASHNODE_GQL_ENDPOINT=https://gql.hashnode.com 
      NEXT_PUBLIC_HASHNODE_PUBLICATION_HOST=engineering.hashnode.com -> Change this to your Hashnode blog URL i.e. handle.hashnode.dev 
      NEXT_PUBLIC_BASE_URL=/blog -> This could be /blog if you are installing on subpath; otherwise remove this var 
      NEXT_PUBLIC_MODE=production
    
  • Then click on deploy. If you see a 404 error, do not worry, as you set the subpath as /blog you need to add it to Vercel's auto generated domain to see your blog.

Hosting your blog to your domains subpath

The next step is to map your blog to yourdomain.com/blog. Currently, the homepage and blog is hosted in two different domains, but we want it to be on the same domain with the /blog subpath. Something like ๐Ÿ‘‰ https://haimantika.dev/blog

Since, we have used HTML and TailwindCSS to build the portfolio, we will need to work with Cloudflare workers.

Note: If you decide to build your portfolio with Next.js at any point in the future, you do not need to go through the Cloudflare route and you can simply follow the steps mentioned here.

Deploy the Worker, and updating the script and map it to yourdomain.com/blog:

  • Login/Signup on Cloudflare.

  • Create and deploy worker as shown in the image below.

  • Once deployed, click on Edit code to update the worker script.

  • Add the following code and save it.

      const subpath = '/blog'; // Replace with your subpath
      const blogBaseUrl = 'https://yourdomain.com'; // Replace with your blog URL
    
      addEventListener('fetch', event => {
        event.respondWith(handleRequest(event.request))
      })
    
      /**
       * Respond to the request
       * @param {Request} request
       */
      async function handleRequest(request) {
        const url = new URL(request.url)
    
        if (url.pathname.startsWith(subpath)) {
          // Proxy blog requests
          return proxyBlog(request)
        } else {
          // Passthrough everything else
          return fetch(request)
        }
      }
    
      /**
       * Proxy blog requests
       * @param {Request} request
       */
      async function proxyBlog(request) {
        const path = new URL(request.url).pathname;
        return fetch(`${blogBaseUrl}${path}`, request)
      }
    

    Once you've deployed the worker, the next step is to add the worker to your website domain on cloudfare.

    For the domain you'll be using on your Cloudflare account, you need to add a worker route. The route should be *, and the worker should be the script you created in the steps above.

    Note: Make sure that your domain - yourdomain.com is on Cloudflare. If not, you should transfer it to make it work!

    For the final step, go to the Hashnode blog dashboard, and under the Domain section, make sure to turn on the Headless mode and mention the blog base URL.

    And now you are done! When you browse to yourdomain.com/blog you will see your blog live on the subpath!


Resources

Thank you for reading to the end. If you have followed the tutorial or the webinar and created a portfolio, feel free to tag us on X (Twitter) @Hashnode, and we will retweet it!

Here are some resources to help you learn more: