Let's Build Github User Finder App with Next.js & Tailwind CSS.
Tags: tailwindcss, nextjs
Published: 2022-03-14 05:07:57

---
title: Let's Build Github User Finder App with Next.js & Tailwind CSS.
published: true
date: 2022-02-28 07:04:00 UTC
tags: TailwindCSS,Nextjs
canonical_url: https://nextjsdev.com/lets-build-github-user-finder-app-with-next-js-tailwind-css/
---

 ![Let's Build Github User Finder App with Next.js & Tailwind CSS.](https://nextjsdev.com/content/images/2022/03/tailwindcss-3.PNG)

Hello everyone, In this project, we are going to build GitHub user Search App using Github API. We will design the UI of the app using Tailwind CSS  with Next.js as a framework.

We will use Github API to pull profile data and display it.

Here is the image of the application that we are going to build:

 ![Let's Build Github User Finder App with Next.js & Tailwind CSS.](https://nextjsdev.com/content/images/2022/03/github-user-finder-1.PNG)

 ![Let's Build Github User Finder App with Next.js & Tailwind CSS.](https://nextjsdev.com/content/images/2022/03/github-user-finder-light-1.PNG)

This is a [frontendmentor.io](https://www.frontendmentor.io/challenges/github-user-search-app-Q09YOgaH6)challenge, and our goal is to make this design as close as possible to the given design.

It will be fully responsive and also have [**dark mode**](https://dev.to/raivikas/add-dark-mode-in-nextjs-app-using-tailwind-css-dark-mode-51j-temp-slug-3122556) functionality.

> [Demo Link of the Project](https://github-user-finder-by-raivikas.vercel.app/)

> [Github Link of the Project](https://github.com/raiv200/github-user-finder)

So let's start building 🚀 :

##   
Create  Next.js app  with tailwind CSS
<!--kg-card-begin: markdown-->
```bash
npx create-next-app my-project // without Tailwind CSS installed
or
npx create-next-app -e with-tailwindcss my-project //with Tailwind CSS

```
<!--kg-card-end: markdown-->

Install the necessary npm packages:

1. Install  @heroicons/react
2. Install next-themes
<!--kg-card-begin: markdown-->
```bash
npm install next-themes @heroicons/react

```
<!--kg-card-end: markdown-->
## To Create the Components and add the dark mode Functionality

First of all, create a **components** folder in the root of the directory and add these files:

1. **Avatar.js**
2. **Loading.js**
3. **Logo.js**
4. **Navbar.js**
5. **GithubUser.js**
6. **SearchBar.js**
7. **UserBio.js**
8. **UserData.js**
9. **UserProfile.js**
10. **UserStats.js**

Don,t get worried by seeing so many components, I have purposely created these components so that it will be easy for you to manage code.

One important point before that,  open the \_app.js file inside the pages directory and add the following code:

**Inside \_app.js :**

<!--kg-card-begin: markdown-->
```jsx
import 'tailwindcss/tailwind.css'
import '../styles/globals.css'
import { ThemeProvider } from 'next-themes'

function MyApp({ Component, pageProps }) {
  return (
    <ThemeProvider attribute="class">
      <Component {...pageProps} />
    </ThemeProvider>
  )
}

export default MyApp

```
<!--kg-card-end: markdown-->

Then open the **tailwind.config.js** file and add the **darkMode** Key with the value of **class** like this:

**Inside tailwind.config.js :**

<!--kg-card-begin: markdown-->
```jsx
module.exports = {
  content: [
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      animation: {
        wiggle: 'wiggle 1s ease-in-out ',
        wiggle_reverse:'wiggle_reverse 0.3s ease-in'
      },
      
      keyframes: {
        wiggle: {
          '0%, 100%': { transform: 'rotate(-60deg)' },
          '50%': { transform: 'rotate(60deg)' },
        },
        wiggle_reverse:{
          '0%': { transform: 'rotate(90deg)' },
          '100%': { transform: 'rotate(0deg)' },
        }
      }
    },
  },
  darkMode:"class",
  plugins: [],
}

```
<!--kg-card-end: markdown-->

and then **next.config.js** file and add  the domains for the images like this:

**Inside next.config.js :**

<!--kg-card-begin: markdown-->
```js
/** @type {import('next').NextConfig} */
module.exports = {
  images: {
    domains:["nextjsdev.com","avatars.githubusercontent.com"]
},
  reactStrictMode: true,
}

```
<!--kg-card-end: markdown-->

I have added two custom animations also which  I am using in this application.

Now paste this code one by one in each component:

**Inside Avatar.js:**

<!--kg-card-begin: markdown-->
```jsx
import Image from "next/image";
import Vercel from "../public/vercel.svg"
const Avatar = ({imageURL}) => {
  return (
    <div className=" w-[120px] h-[120px] ml-8 ring-[5px] ring-[#3b52d4] dark:ring-[#053bff] rounded-full ">
    {imageURL ? (
      <Image 
        src={imageURL ? imageURL : Vercel}
        width="120"
        height="120"
        objectFit="cover"
        className="rounded-full "
    />
    ): (
      <p className="text-lg font-bold font-mono text-center pt-8 text-gray-800 dark:text-gray-200">No Image Found</p>
    )
    
    }
    </div>
  )
}

export default Avatar

```
<!--kg-card-end: markdown-->

**Inside Loading.js:**

<!--kg-card-begin: markdown-->
```jsx
export const Loading = () => {
  return (
    <div className=" w-28 mx-auto mt-40">
     <svg className="animate-spin -ml-1 mr-3 h-10 w-10 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
        <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
        <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
      </svg>
    </div>
  )
}

```
<!--kg-card-end: markdown-->

**Inside Logo.js:**

<!--kg-card-begin: markdown-->
```jsx
import Link from 'next/link'

const Logo = () => {
  return (
    <Link href="/">
      <div className="p-3 rounded-xl cursor-pointer">
        <p className="font-mono text-xl font-semibold text-gray-800 dark:text-gray-50 md:text-xl lg:text-2xl">
          devfinder
        </p>
      </div>
    </Link>
  )
}

export default Logo

```
<!--kg-card-end: markdown-->

**Inside Navbar.js:**

<!--kg-card-begin: markdown-->
```jsx
import { SunIcon } from '@heroicons/react/outline'
import { MoonIcon } from '@heroicons/react/solid'
import Logo from './Logo'
import { useTheme } from 'next-themes'
import { useState, useEffect } from 'react'

const Navbar = () => {
  const { systemTheme, theme, setTheme } = useTheme();

  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, [])

  const renderThemeChanger = () => {
    const currentTheme = theme === 'system' ? systemTheme : theme
    if (!mounted) return null;

    if (currentTheme === 'dark') {
      return (
        <div className="flex cursor-pointer items-center ">
          <h2 className="text-md font-mono font-semibold uppercase tracking-wider dark:text-gray-50">
            Light
            <SunIcon
              className="ml-1 inline-block h-8 w-8 text-amber-400 animate-wiggle "
              onClick={() => setTheme('light')}
            />
          </h2>
        </div>
      )
    } else {
      return (
        <div className="flex cursor-pointer items-center">
          <h2 className="text-md font-mono font-semibold uppercase tracking-wider text-slate-500 ">
            Dark
            <MoonIcon
              className="ml-1 inline-block h-8 w-8 text-gray-600 animate-wiggle_reverse"
              onClick={() => setTheme('dark')}
            />
          </h2>
        </div>
      )
    }
  }

  return (
    <header className="align-items mx-auto mt-4 flex max-w-md justify-between space-x-4 rounded-md p-2 md:max-w-2xl">
      <div>
        <Logo />
      </div>

      {renderThemeChanger()}
    </header>
  )
}

export default Navbar

```
<!--kg-card-end: markdown-->

**Inside GithubUser.js:**

<!--kg-card-begin: markdown-->
```jsx
import UserProfile from './UserProfile'
import UserBio from './UserBio'
import UserStats from './UserStats'
import UserData from './UserData'

const GithubUser = (props) => {
  const date = new Date(props.data.created_at)
  const newDate = date.toDateString(4, 10).slice(4, 15)

  return (
    <div className="mx-auto mt-6 flex max-w-md min-h-[470px] flex-col items-end justify-between space-y-4 rounded-lg bg-gray-200 py-6 transition duration-300 ease-in dark:bg-[#2b365e] md:min-h-fit md:max-w-2xl">
       <UserProfile
        name={props.data.name}
        date={newDate}
        username={props.data.login}
        imageURL={props.data.avatar_url}
      />

      <div className=" flex w-full md:max-w-lg flex-col space-y-6 px-6 py-3">
        <UserBio bio={props.data.bio} />
        <UserStats
          repos={props.data.public_repos}
          followers={props.data.followers}
          following={props.data.following}
        />

        <UserData
          location={props.data.location}
          twitterUsername={props.data.twitter_username}
          blog={props.data.blog}
          company={props.data.company}
        />
      </div> 
    </div>
  )
}

export default GithubUser

```
<!--kg-card-end: markdown-->

**Inside SearchBar.js:**

<!--kg-card-begin: markdown-->
```jsx
import { SearchIcon } from '@heroicons/react/outline'

const SearchBar = ({userName, handleClick , userRef}) => {
 
  return (
    <div className=" align-items mx-auto mt-4 flex max-w-md justify-between space-x-2 rounded-lg bg-gray-200 p-2 pb-2 transition duration-300 ease-in dark:bg-[#2b365e] md:max-w-2xl">
      <SearchIcon className="mt-3 ml-2 h-6 w-6 text-[#5176ff] dark:text-blue-600" />
      <input
        name="search"
        ref={userRef}
        placeholder="Search GitHub username....."
        className="text-md mt-1 w-[400px] rounded-md bg-gray-200 px-2 py-2 font-mono leading-6 text-slate-500 placeholder-neutral-400 transition duration-300 ease-in focus:outline-none dark:bg-[#2b365e] dark:text-gray-50 dark:placeholder-slate-500"
      />
      <button
        onClick={handleClick}
        className=" text-md mx-auto h-10 rounded-md bg-gray-50 px-4 font-mono font-medium text-blue-600 shadow-xl transition duration-300 ease-in hover:bg-blue-500 hover:text-blue-100 dark:bg-[#5176ff] dark:text-white dark:hover:bg-blue-600 "
      >
        Search
      </button>
    </div>
  )
}

export default SearchBar

```
<!--kg-card-end: markdown-->

**Inside UserBio.js:**

<!--kg-card-begin: markdown-->
```jsx
import React from 'react'

const UserBio = ({bio}) => {
  return (
    <p className="font-mono text-sm font-medium text-gray-800 dark:text-gray-300 text-center ">
          Bio-{bio === null ? 'Not Available' :bio}
    </p>
  )
}

export default UserBio

```
<!--kg-card-end: markdown-->

**Inside UserData.js:**

<!--kg-card-begin: markdown-->
```jsx
import {
    LocationMarkerIcon,
    LinkIcon,
    OfficeBuildingIcon,
  } from '@heroicons/react/solid'

const UserData = ({location, twitterUsername,blog, company}) => {
  return (
    <div className="grid grid-cols-1 gap-6 px-2 py-4 md:grid-cols-2 md:gap-x-10">
          <div className="flex items-center space-x-2 font-semibold text-white transition-colors duration-150 hover:text-blue-400">
            <LocationMarkerIcon className="h-5 w-5 text-slate-500 dark:text-gray-100" />
            <p className="font-mono text-sm font-medium text-gray-900 dark:text-gray-300">
              {location ? location : "Not Available"}
            </p>
          </div>

          <div className="flex items-center space-x-2 font-semibold text-white transition-colors duration-150 hover:text-blue-400">
            <svg
              width="20"
              height="20"
              fill="currentColor"
              className="text-sky-400 opacity-100"
            >
              <path d="M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84"></path>
            </svg>
            <p className="font-mono text-sm font-medium text-gray-900 dark:text-gray-300">
              <a
                href={`https://twitter.com/${twitterUsername}`}
                target="_blank"
              >
                {twitterUsername ? twitterUsername : "Not Available"}
              </a>
            </p>
          </div>

          <div className="flex items-center space-x-2 font-semibold text-white transition-colors duration-150 hover:text-blue-400">
            <LinkIcon className="h-5 w-5 text-slate-500 dark:text-gray-100 " />
            <p className="decoration-3 font-sm font-mono text-sm font-medium text-gray-900 underline dark:text-gray-300">
              <a href={`https://${blog}`} target="_blank">
                {blog ? blog :"Not Available"}
              </a>
            </p>
          </div>

          <div className="flex items-center space-x-2 font-semibold text-white transition-colors duration-150 hover:text-blue-400">
            <OfficeBuildingIcon className="h-5 w-5 text-slate-500 dark:text-gray-100 " />
            <p className="font-sm font-mono text-sm font-medium text-gray-900 dark:text-gray-300">
              {company ? company : "Not Available"}
            </p>
          </div>
        </div>
  )
}

export default UserData

```
<!--kg-card-end: markdown-->

**Inside UserProfile.js:**

<!--kg-card-begin: markdown-->
```jsx
import Avatar from './Avatar'

const UserProfile = ({ name, date, username ,imageURL}) => {
  return (
    <div className=" flex w-full items-center space-x-4 md:justify-evenly md:space-x-6">
      <Avatar imageURL={imageURL} />
      <div className="flex flex-1 items-center space-x-6 px-2 md:flex-1 md:items-start md:justify-between">
        <h2 className="w-32 md:w-44 font-mono text-lg font-bold text-gray-800 dark:text-gray-50 md:text-2xl">
          {name}{' '}
          <span className="inline-block font-mono text-sm text-blue-400">
            {username && `@${username ? username :'Not Available'}`}
          </span>
        </h2>
        {username && <p className=" md:text-md -mt-2 pl-6 font-mono text-sm font-[400] text-slate-600 dark:text-gray-300 md:mt-0 md:p-6 md:pt-2">
          Joined{' '}
          <span className="flex font-mono text-xs font-semibold md:inline-block md:text-sm">
            {date ? date :"Not Available"}
          </span>
        </p>}
      </div>
    </div>
  )
}

export default UserProfile

```
<!--kg-card-end: markdown-->

**Inside UserStats.js:**

<!--kg-card-begin: markdown-->
```jsx
import React from 'react'

const UserStats = ({repos,followers ,following}) => {
  return (
    <div className=" grid grid-cols-3 gap-6 divide-x divide-gray-700 rounded-xl bg-gray-50 py-4 dark:divide-gray-50 dark:bg-[#1e253f]">
    <div className="align-items flex flex-col px-4 text-center">
      <h4 className="font-mono text-xs font-semibold text-gray-700 dark:text-gray-400 ">
        Repos
      </h4>
      <p className="font-mono text-lg font-extrabold text-gray-700 dark:text-gray-50 ">
        {repos ? repos :"Not Available"}
      </p>
    </div>

    <div className="align-items flex flex-col text-center">
      <h4 className="font-mono text-xs font-semibold text-gray-700 dark:text-gray-400 ">
        Followers
      </h4>
      <p className="font-mono text-lg font-extrabold text-gray-700 dark:text-gray-50 ">
        {followers ? followers :"Not Available"}
      </p>
    </div>

    <div className="align-items flex flex-col text-center">
      <h4 className="font-mono text-xs font-semibold text-gray-700 dark:text-gray-400 ">
        Following
      </h4>
      <p className="font-mono text-lg font-extrabold text-gray-700 dark:text-gray-50 ">
        {following? following : "Not Available"}
      </p>
    </div>
  </div>
  )
}

export default UserStats

```
<!--kg-card-end: markdown-->

Now open up the terminal and start the application by running the command

<!--kg-card-begin: markdown-->
```bash
npm run dev

```
<!--kg-card-end: markdown-->

The application might be working or it may be showing some error because we haven't completed the application yet, so don't worry about it.

## To fetch the Github API and Display the data

Now the final step has come, go to **index.js** inside the pages directory and delete all the code inside it and then paste this code inside it:

**Inside the index.js :**

<!--kg-card-begin: markdown-->
```jsx
import Head from 'next/head'
import SearchBar from '../components/SearchBar'
import Navbar from '../components/Navbar'
import GithubUser from '../components/GithubUser'
import { useState, useRef, useEffect } from 'react'
import { Loading } from '../components/Loading'

export default function Home() {
  let API = 'https://api.github.com/users/octocat'

  const userRef = useRef(null)
  const [userName, setUserName] = useState('')
  const [data, setData] = useState('')
  const [isLoading, setLoading] = useState(false)

  function handleClick() {
    setUserName(userRef.current.value)
  }
  useEffect(() => {
    setLoading(true)
    if (userName) {
      API = `https://api.github.com/users/${userName}`
    }

    fetch(API)
      .then((res) => res.json())
      .then((data) => {
        setData(data)
        setLoading(false)
      })
  }, [userName]);

  if(!data) (
  <p>No Profile data.</p>
  )

  return (
    <div className="min-h-screen bg-gray-50 py-7 dark:bg-[#1e253f]">
      <Head>
        <title>GitHub User Finder App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Navbar />
      
      {isLoading ? <Loading /> :
      <>
      <SearchBar
        userName={userName}
        handleClick={handleClick}
        userRef={userRef}
      />
      <GithubUser data={data} />
      </>
      }
    </div>
  )
}

```
<!--kg-card-end: markdown-->

After pasting the code save the application and restart the server and visit **localhost:3000** and you will the application working.

You can type any valid GitHub username and you will see the data get displayed on the front-end.

## Conclusion

Hope you were able to build this amazing Github User finder App for your next project. Feel free to follow me on [Twitter](https://twitter.com/raivikas200) and share this if you like this project 😉.

It took me 4-5 days to build this project and I would appreciate ✌️ it if you could share this blog post.

If you think that this was helpful and then please do consider visiting my blog website [nextjsdev.com](https://nextjsdev.com/) and do follow me on **Twitter** and connect with me on **LinkedIn**.

If you were stuck somewhere and not able to find the solution you can check out my completed [Github Repo](https://github.com/raiv200/github-user-finder) here.

Thanks for your time to read this project, if you like this please share it on Twitter and Facebook or any other social media and tag me there.

I will see you in my next blog ✌️. Till then take care and keep building projects.

Some Useful Link:

1. [Next.js and Tailwind Installation Docs](https://tailwindcss.com/docs/guides/nextjs)
2. [Github link for project](https://github.com/raiv200/github-user-finder)

Connect with me:

1. [Twitter Link](https://twitter.com/raivikas200)
2. [LinkedIn link](https://www.linkedin.com/in/raivikas200/)
3. [Facebook Link](https://www.facebook.com/raiv200/)
4. [Github Link](https://github.com/raiv200)