How to Create a GitHub Profile Application With React and GitHub APIs
Tags: react, github, chakraui, javascript
Published: 2023-01-09 23:15:07

In this article, we will learn how to create an online portfolio showcasing information available in our GitHub profile by leveraging the GitHub API, React, and Chakra UI. 

## Prerequisites

Knowledge of React and how to fetch data from APIs is required to follow along in this article. Experience with Chakra UI is a plus but not required.

## What We Will Build

The GitHub profile application will have the following features:

* A `/github-profile` route containing basic information about our GitHub account, such as
    
    * The number of repositories available
        
    * The number of followers and following 
        
    * A link to our portfolio
        
    * Our location
        
    * Our username
        
    * Our profile picture
        
* A `/repos` route containing the list of repositories we have on GitHub, and pagination for easier navigation from one set of repositories to another
    
* An error boundary route
    
* A navigation bar with links to the different routes
    

The images below show a preview of the application we will build. 

![](https://paper-attachments.dropboxusercontent.com/s_E24FB436EF76382794006C074BE9E6B59F0AA2BAA5C959DFD91E28C483E6397D_1673290539466_Screenshot+3193.png align="left")

![](https://paper-attachments.dropboxusercontent.com/s_E24FB436EF76382794006C074BE9E6B59F0AA2BAA5C959DFD91E28C483E6397D_1673290539112_Screenshot+3194.png align="left")

The hosted version of the complete application is on [Vercel](https://nefe-altschool-github-exam-q1.vercel.app/github-profile), and the source code is available on [GitHub](https://github.com/nefejames/altschool-github-exam-app).

## Getting Started

Run the command below in the terminal to generate a new React app with create-react-app.

```bash
npx create-react-app github-profile-app
```

Next, navigate to the directory and run the server. 

```bash
cd github-profile-app

npm start
```

Finally, [install Chakra UI](https://chakra-ui.com/getting-started) into the React app.

Now that the app is ready let’s start building. We will begin with creating the different routes.

## Setting up the GitHubUser Context

The GitHubUser context will contain details of our GitHub account that we fetch from the GitHub API.

```javascript

import { createContext, useContext, useEffect, useState } from "react";
const GitHubUserContext = createContext();
export const useGitHubUserContext = () => useContext(GitHubUserContext);

export default function GitHubUserContextProvider({ children }) {
  const [GitHubUserData, setGitHubUserData] = useState(false);
  
  async function fetchGitHubUserData() {
    const response = await fetch(`https://api.github.com/users/nefejames`);
    const data = await response.json();
    setGitHubUserData(data);
  }
  
  useEffect(() => {
    fetchGitHubUserData();
  }, []);
  
  const data = GitHubUserData;
  
  return (
    <GitHubUserContext.Provider value={data}>
      {children}
    </GitHubUserContext.Provider>
  );
}
```

Here, we did the following:

* Imported the necessary dependencies from `react`
    
* Created the `GitHubUserData` state
    
* Created the `fetchGitHubUserData` function that fetches the user data from the GitHub API
    
* Set up the fetch functionality in the `useEffect`
    
* Passed the fetched data as the value of the Context API
    

## Setting up the GitHubRepos Context and Pagination Functionality 

The GitHubRepos context will contain information on our GitHub repos and their respective details. We will also set up the pagination functionality here. 

```javascript

import { createContext, useContext, useEffect, useState } from "react";
const GitHubReposContext = createContext();

export const useGitHubReposContext = () => useContext(GitHubReposContext);
export default function GitHubReposContextProvider({ children }) {
  const [GitHubReposData, setGitHubReposData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [currentPage, setCurrentPage] = useState(1);
  const totalNoOfPages = 5;
  
  async function fetchGitHubReposData() {
    setLoading(true);
    const response = await fetch(
      `https://api.github.com/users/nefejames/repos`
    );
    const data = await response.json();
    setGitHubReposData(data);
    setLoading(false);
  }
  
  useEffect(() => {
    fetchGitHubReposData();
  }, []);
  
  //start pagination functionality setup
  const getPaginationData = (arr, currentPage = 1, reposPerPage) => {
    const start = currentPage * reposPerPage - reposPerPage;
    const data = [...arr].splice(start, reposPerPage);
    return data;
  };
  
  const handlePageChange = (page) => {
    setCurrentPage(page);
  };
  
  const prevPage = () => {
    if (currentPage === 1) {
      return;
    }
    setCurrentPage((prev) => prev - 1);
  };
  
  const nextPage = () => {
    if (currentPage >= totalNoOfPages) {
      return;
    } else {
      setCurrentPage((prev) => prev + 1);
    }
  };
  
  const filteredRepos = getPaginationData(GitHubReposData, currentPage, 6);
  //end pagination functionality setup
  
  const data = {
    filteredRepos,
    prevPage,
    nextPage,
    handlePageChange,
    totalNoOfPages,
    currentPage,
    loading,
  };
  return (
    <GitHubReposContext.Provider value={data}>
      {children}
    </GitHubReposContext.Provider>
  );
}
```

We did the following to set up the GitHubRepos context:

* Imported the necessary dependencies from react
    
* Created the `GitHubReposData` and the `loading` states
    
* Created the `fetchGitHubReposData` function that fetches the repos data from the GitHub API
    
* Set up the fetch functionality in the `useEffect`
    
* Passed the fetched data as the value of the Context API
    

Let’s break down the functionality for the pagination:

* We created the `currentPage` state and set its initial value to 1 because that is what we want the first pagination step to be
    
* We set up the `totalNoOfPages` and set it to 5 because that is the number of steps we want 
    
* The `prevPage` function only reduces the step if the value of `currentPage` is more than 1
    
* The `nextPage` function only increases the step if `currentPage` is greater than or equal to `totalNoOfPages` 
    
* The `handlePageChange` function updates the `currentPage` when anyone clicks on the pagination buttons
    

## Creating the Pagination Component

The `Pagination` component will hold the pagination buttons. To set it up, create a `Pagination.js` file in the components folder and paste the following code.

```javascript

import { Button, HStack } from "@chakra-ui/react";

export default function Pagination({
  onPageChange,
  currentPage,
  prevPage,
  nextPage,
  totalNoOfPages,
}) {
  const getPages = () => {
    const pageNumber = [];
    for (let i = 1; i < totalNoOfPages + 1; i++) {
      pageNumber.push(i);
    }
    return pageNumber;
  };
  return (
    <HStack spacing={2} mt={6}>
      <Button
        disabled={currentPage === 1 ? true : false}
        onClick={prevPage}
      >
        &larr;
      </Button>
      {getPages().map((page) => (
        <Button
          key={page}
          onClick={() => onPageChange(page)}
          bg={currentPage === page ? "blue.500" : "gray.200"}
          color={currentPage === page ? "#fff" : "#111"}
          _hover={{
            bg: currentPage === page ? "blue.500" : "",
          }}
        >
          {page}
        </Button>
      ))}
      <Button
        disabled={currentPage === totalNoOfPages ? true : false}
        onClick={nextPage}
      >
        &rarr;
      </Button>
    </HStack>
  );
}
```

Let’s break down the code above:

* The Pagination component takes in `onPageChange`, `currentPage`, `prevPage`, nextPage, and `totalNoOfPages` as props
    
* The `getPages` function gets the current step and returns it in the `pageNumber` variable
    
* We use the values and properties gotten via props to get up the buttons
    

## Creating the GitHub UserProfile Route

Navigate to the `src` folder, and create a `pages` folder there. In there, create a `GitHubUser.js` file, and update it with the following code.

```javascript

import GitHubUserAnalytics from "../components/GitHubUserAnalytics";
import GitHubUserLinks from "../components/GitHubUserLinks";
import { Link } from "react-router-dom";
import { useGitHubUserContext } from "../context/GitHubUserContext";
import { LazyLoadImage } from "react-lazy-load-image-component";
import "react-lazy-load-image-component/src/effects/blur.css";
import PlaceholderImage from "../images/placeholder.jpg";
import SEO from "../components/SEO";

export default function GitHubUser() {
  const data = useGitHubUserContext();
  const ChakraLink = chakra(Link);
  const ChakraLazyLoadImage = chakra(LazyLoadImage);
  if (!data) return <Spinner size="xl" />;
  
  return (
    <>
      <SEO
        title="nefejames - GitHub profile"
        description="Front end web developer and technical writer - always learning - nefejames"
      />
      <Card>
        <Flex>
          <Box width={["full", "300px"]}>
            <ChakraLazyLoadImage
              src={data.avatar_url}
              placeholderSrc={PlaceholderImage}
            />
            <Text as="span" mt={3} display="inline-block" color="teal.900">
              @{data.login}
            </Text>
          </Box>
          <VStack spacing={7} alignItems="start">
            <VStack spacing={5} alignItems="start">
              <VStack>
                <Text>
                  {data.name}
                </Text>
              </VStack>
              <Text as="p">{data.bio}</Text>
            </VStack>
            <GitHubUserAnalytics
              noOfRepos={data.public_repos}
              noOfFollowers={data.followers}
              noOfFollowing={data.following}
            />
            <GitHubUserLinks webUrl={data.blog} location={data.location} />
            <ChakraLink to="/repos" w="full">
              <Button>View my Repos</Button>
            </ChakraLink>
          </VStack>
        </Flex>
      </Card>
    </>
  );
}
```

Here, we did the following:

* Imported the relevant dependencies and components 
    
* Passed the appropriate data to `noOfRepos`, `noOfFollowers`, and `noOfFollowing` properties of the `GitHubUserAnalytics` component
    
* Passed the appropriate data to the `webUrl` and `location` properties of the `GitHubUserLinks` component
    

The `GitHubUserAnalytics` component displays analytics on our account's number of repositories, followers, and following. 

Here’s the code snippet for it. It takes in the `noOfRepos`,  `noOfFollowers`, and `noOfFollowing` as props from its parent, the `GitHubUser` component, and displays them in the UI.

```javascript

import { Flex, Text, VStack } from "@chakra-ui/react";

export default function GitHubUserAnalytics({
  noOfRepos,
  noOfFollowers,
  noOfFollowing,
}) {
  return (
    <Flex>
      <VStack spacing={-1}>
        <Text as="span">Repos</Text>
        <Text as="span" fontWeight="bold" fontSize="xl">
          {noOfRepos}
        </Text>
      </VStack>
      <VStack spacing={-1}>
        <Text as="span">Followers</Text>
        <Text as="span" fontWeight="bold" fontSize="xl">
          {noOfFollowers}
        </Text>
      </VStack>
      <VStack spacing={-1}>
        <Text as="span">Following</Text>
        <Text as="span" fontWeight="bold" fontSize="xl">
          {noOfFollowing}
        </Text>
      </VStack>
    </Flex>
  );
}
```

The `GitHubUserLinks` component is responsible for displaying the link to our portfolio and our location. 

Here’s the code snippet for it. It takes the `webUrl` and `location` as props from `GitHubUser` and displays them in the UI.

```javascript

import { HStack, Text } from "@chakra-ui/react";
import { BiLink, BiMap } from "react-icons/bi";

export default function GitHubUserLinks({ webUrl, location }) {
  return (
    <HStack spacing={8}>
      <a href={webUrl} target="_blank" rel="noopener noreferrer">
        <HStack>
          <BiLink />
          <Text as="span">My portfolio</Text>
        </HStack>
      </a>
      <HStack>
        <BiMap />
        <Text as="span">{location}</Text>
      </HStack>
    </HStack>
  );
}
```

The `SEO` component uses [react-helmet-async](https://github.com/staylor/react-helmet-async) to set up some basic SEO optimization for the page. It accepts the `title` and `description` as props.

```javascript

import { Helmet } from "react-helmet-async";

export default function SEO({
  title,
  description = "Front end web developer and technical writer - always learning - nefejames",
}) {
  return (
    <Helmet>
      <title>{title}</title>
      <meta charset="utf-8" />
      <meta name="theme-color" content="#000000" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <meta property="og:description" content={description} />
    </Helmet>
  );
}
```

## Creating the Repos Route

The Repos route will handle the display of the list of repositories and the pagination buttons. Create a `Repos.js` file in the `pages` folder, and copy the code below to set it up. 

```javascript

import { Center, SimpleGrid, Spinner } from "@chakra-ui/react";
import Repo from "../components/Repo";
import { useGitHubReposContext } from "../context/GitHubReposContext";
import SEO from "../components/SEO";
import Pagination from "../components/Pagination";

export default function Repos() {
  const {
    filteredRepos,
    prevPage,
    nextPage,
    handlePageChange,
    totalNoOfPages,
    currentPage,
    loading,
  } = useGitHubReposContext();
  
  if (loading) return <Spinner size="xl" />;
  
  return (
    <>
      <SEO title="My Repos" />
      <SimpleGrid columns={[1, 2]} spacingX="10" spacingY="8" pt={10}>
        {filteredRepos.map((repo) => (
          <Repo
            key={repo.name}
            repoName={repo.name}
            progLang={repo.language}
            stars={repo.stargazers_count}
          />
        ))}
      </SimpleGrid>
      <Center mb={10}>
        <Pagination
          onPageChange={handlePageChange}
          currentPage={currentPage}
          prevPage={prevPage}
          nextPage={nextPage}
          totalNoOfPages={totalNoOfPages}
        />
      </Center>
    </>
  );
}
```

Here, we did the following:

* Imported the relevant dependencies and components
    
* Mapped through the `filteredRepos`, and displayed the repos in the UI
    
* Passed the appropriate properties to the `Pagination` component
    

## Creating the Error Boundary Route

Error boundaries help us catch errors that occur anywhere in our application. Create an `Error.js` file in the `pages` folder, and copy the code below to set it up.

```javascript

import { Text } from "@chakra-ui/react";

export default function Error() {
  throw new Error("error");
  return <Text as="span">An error route</Text>;
}
```

## Creating the 404 Route

404 routes are instrumental in helping us to catch user-generated navigation errors in our applications. Create a `NotFound.js` file in the `pages` folder, and copy the code below to set it up.

```javascript

import { Button, Center, Text, VStack } from "@chakra-ui/react";
import { Link } from "react-router-dom";

export default function NotFound() {
  return (
    <Center>
      <VStack spacing={5}>
        <Text as="h1">Error. Page not found</Text>
        <Button color="white" bg="green.900" _hover={{ bg: "green.800" }}>
          <Link to="/github-user">View my GitHub profile</Link>
        </Button>
      </VStack>
    </Center>
  );
}
```

## Bringing It All Together

Having created all the necessary components and their respective functionalities, let’s bring them all together in the `App.js` file.

```javascript

import { Routes, Route } from "react-router-dom";
import { Flex } from "@chakra-ui/react";
import GitHubUser from "./pages/GitHubUser";
import Home from "./pages/Home";
import NotFound from "./pages/404";
import Header from "./components/Header";
import Repos from "./pages/Repos";
import Error from "./pages/Error";
import SEO from "./components/SEO";
import RepoDetails from "./components/RepoDetails";

function App() {
  return (
    <>
      <SEO title="My GitHub App" />
      <Flex>
        <Routes>
          <Route exact path="/" element={<Home />}>
            <Route path="/github-profile" element={<GitHubUser />} />
            <Route path="repos" element={<Repos />} />
          </Route>
          <Route path="/error" element={<Error />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </Flex>
    </>
  );
}

export default App;
```

## Conclusion

With that, we have successfully created the GitHub profile application. This project was part of the second-semester assessment for [AltSchool Africa’s](https://www.altschoolafrica.com/) [Frontend Engineering](https://www.altschoolafrica.com/schools/engineering) track.