Learn to create a GitHub Profile Finder using HTML, CSS, and JavaScript. Build your own app to fetch GitHub user profiles and display details!
Table of Contents
Creating a GitHub Profile Finder is a fun way to learn HTML, CSS, and JavaScript. This project will let you input a GitHub username and fetch their profile data using GitHub’s API. With this project, you’ll get comfortable with making API requests, handling responses, and displaying the data in a clean, modern layout.
Prerequisites:
- Basic knowledge of HTML, CSS, and JavaScript
- An active internet connection (to fetch data from GitHub)
- GitHub account (optional but helpful for testing with your own profile)
Let’s dive into the steps to create your own GitHub Profile Finder!
Source Code
Step 1 (HTML Code):
Start with a basic HTML structure and add a container for the GitHub Profile Finder. This will include an input field for the username, a button to trigger the search, and an area to display the profile information.
Here’s a breakdown of each part of the HTML code:
Basic Structure
DOCTYPE Declaration:
<!DOCTYPE html>
- Declares the document as HTML5, which helps the browser interpret the HTML code in modern standards.
HTML Tag:
<html lang="en">
- Defines the beginning of the HTML document with the language set to English (lang="en").
Head Section
Head Tag:
<head> ... </head>
- Contains metadata and links to resources like stylesheets and fonts that don’t directly display on the page but are necessary for setting up the content.
Charset:
<meta charset="UTF-8">
- Specifies that the character encoding is UTF-8, ensuring proper display of text characters in all languages.
Viewport:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- Ensures the webpage scales well on different screen sizes, like mobile devices.
Title:
<title>GitHub Profile Finder</title>
- Sets the page title shown in the browser tab.
CSS Link:
<link rel="stylesheet" href="styles.css">
- Links an external CSS file named styles.css for styling the page’s content.
Font Preconnect and Link:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
- Optimizes font loading by preconnecting to Google Fonts servers and imports the Poppins font for a clean, modern appearance.
Body Section
Body Tag:
<body> ... </body>
- Contains all visible content on the webpage.
Container:
<div class="container"> ... </div>
- A div with a class container wraps the main content. Typically used for layout and styling.
Header:
<h1>GitHub Profile Finder</h1>
- A header (h1) displays the title of the app at the top of the page.
Search Box:
<div class="search-box">
<input type="text" id="username" placeholder="Enter GitHub username..." />
<button onclick="fetchProfile()">Search</button>
</div>
- Search Box div: Groups the search input and button together.
Input Field:
- An input field where users can type a GitHub username.
- Includes id="username" to be referenced by JavaScript and placeholder="Enter GitHub username..." to provide guidance text.
Button:
- A button that triggers the JavaScript function fetchProfile() when clicked, which initiates the search for the GitHub profile.
Loading Spinner:
<div id="loading" class="loading-spinner"></div>
- An empty div with an id of loading and a class of loading-spinner. This could be styled to show a loading animation when a profile search is in progress.
Profile Card:
<div id="profile" class="profile-card"></div>
- An empty div with id="profile" and class profile-card, intended to display the fetched profile details once loaded.
JavaScript Link:
<script src="script.js"></script>
- Links an external JavaScript file, script.js, which contains the logic for fetching and displaying the GitHub profile information.
Step 2 (CSS Code):
Create a modern look using CSS. Add styling for the container, search box, button, and profile display area. Here's an explanation of each section:
Global Styles
Universal Selector *:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
- Sets all elements’ margins and padding to zero to avoid default spacing.
- Uses box-sizing: border-box to make width and height calculations include padding and borders, helping with consistent layout.
- Sets the font to Poppins, giving a modern, clean look.
Body Styles
Body:
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #825CFF;
}
- Centers the content both horizontally and vertically using Flexbox.
- Sets a minimum height of 100% viewport height, ensuring the content fills the screen vertically.
- Applies a background color #825CFF for a visually appealing backdrop.
Container and Header
Container:
.container {
text-align: center;
max-width: 500px;
width: 100%;
}
- Centers text within the container and limits the width to 500px for better readability.
- Makes the container responsive by setting the width to 100%.
Header (h1):
h1 {
color: #fff;
margin-bottom: 20px;
font-weight: 600;
}
- Sets the header text color to white for contrast against the background.
- Adds a 20px margin below the header and a bold font weight for emphasis.
Search Box
Search Box Container:
.search-box {
display: flex;
margin-bottom: 20px;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 50px;
padding: 5px;
}
- Uses Flexbox to align the input field and button horizontally.
- Adds a white background with a subtle shadow and rounded corners for a modern look.
- Adds padding around the inner elements.
Search Input Field:
.search-box input {
flex: 1;
padding: 12px 20px;
font-size: 1rem;
border: none;
border-radius: 50px;
outline: none;
transition: all 0.3s ease;
}
- flex: 1 makes the input field take all available space within the .search-box.
- Adds padding for comfortable typing space and removes borders for a cleaner look.
- Adds a background change on focus to indicate interaction.
Search Button:
.search-box button {
padding: 10px 20px;
font-size: 1rem;
border: none;
background-color: #007bff;
color: #fff;
cursor: pointer;
border-radius: 50px;
transition: background-color 0.3s ease;
font-weight: 600;
}
- Adds padding, font size, and no borders.
- Sets a blue background and white text for contrast, with a slight hover effect for interactivity.
Loading Spinner
Loading Spinner:
.loading-spinner {
display: none;
margin: 20px auto;
border: 4px solid #f3f3f3;
border-radius: 50%;
border-top: 4px solid #3498db;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
- Hidden by default but styled to appear as a spinning circle when active.
- The @keyframes spin animation makes the spinner rotate continuously.
Spin Animation:
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
- Defines the animation for rotating the spinner from 0 to 360 degrees.
Profile Card
Profile Card Container:
.profile-card {
display: none;
background: #fff;
padding: 20px;
margin-bottom: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: left;
transition: all 0.3s ease;
}
- Hidden by default but styled to appear as a white card with padding, rounded corners, and a shadow.
- Text aligns to the left for a more organized display.
Profile Card Image:
.profile-card img {
border-radius: 50%;
margin-bottom: 15px;
width: 100px;
}
- Displays a circular profile image by applying a border radius of 50%.
- Sets a 100px width and adds margin below the image.
Profile Card Heading and Text:
.profile-card h2 {
color: #333;
font-weight: 600;
}
.profile-card p, .profile-card span {
color: #666;
font-size: 0.9rem;
margin: 5px 0;
}
- The heading (h2) has a dark color and bold font for emphasis.
- The descriptive text is a smaller font size, colored gray for a soft contrast.
Profile Link:
.profile-card a {
display: inline-block;
margin-top: 10px;
text-decoration: none;
color: #007bff;
font-weight: 600;
}
- A blue, bold link is styled to appear as a button, encouraging users to click.
Profile Stats:
.profile-stats {
display: flex;
justify-content: space-around;
margin-top: 10px;
}
.stat {
text-align: center;
}
.stat p {
margin-top: 5px;
color: #333;
font-weight: bold;
}
- Align the profile stats (like followers or repositories) horizontally.
- Each state has centered text with bold, dark numbers for emphasis.
Error Message
.error {
color: #ff4d4f;
margin-top: 20px;
}
- Adds a red error message with some top margin to show user feedback when necessary.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #825CFF;
}
.container {
text-align: center;
max-width: 500px;
width: 100%;
}
h1 {
color: #fff;
margin-bottom: 20px;
font-weight: 600;
}
.search-box {
display: flex;
margin-bottom: 20px;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 50px;
padding: 5px;
}
.search-box input {
flex: 1;
padding: 12px 20px;
font-size: 1rem;
border: none;
border-radius: 50px;
outline: none;
transition: all 0.3s ease;
}
.search-box input:focus {
background-color: #f0f2f5;
}
.search-box button {
padding: 10px 20px;
font-size: 1rem;
border: none;
background-color: #007bff;
color: #fff;
cursor: pointer;
border-radius: 50px;
transition: background-color 0.3s ease;
font-weight: 600;
}
.search-box button:hover {
background-color: #0056b3;
}
.loading-spinner {
display: none;
margin: 20px auto;
border: 4px solid #f3f3f3;
border-radius: 50%;
border-top: 4px solid #3498db;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.profile-card {
display: none;
background: #fff;
padding: 20px;
margin-bottom: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: left;
transition: all 0.3s ease;
}
.profile-card img {
border-radius: 50%;
margin-bottom: 15px;
width: 100px;
}
.profile-card h2 {
color: #333;
font-weight: 600;
}
.profile-card p, .profile-card span {
color: #666;
font-size: 0.9rem;
margin: 5px 0;
}
.profile-card a {
display: inline-block;
margin-top: 10px;
text-decoration: none;
color: #007bff;
font-weight: 600;
}
.profile-stats {
display: flex;
justify-content: space-around;
margin-top: 10px;
}
.stat {
text-align: center;
}
.stat p {
margin-top: 5px;
color: #333;
font-weight: bold;
}
.error {
color: #ff4d4f;
margin-top: 20px;
}
Step 3 (JavaScript Code):
Now, write JavaScript to fetch data from the GitHub API. This function will look for the username entered, fetch details if available, and display them in the profile area. Here’s a breakdown of each part:
Function Declaration:
async function fetchProfile() {
- Declares an asynchronous function to handle the profile retrieval process. The async keyword enables the use of await within the function, allowing asynchronous code to look synchronous.
Getting User Input:
const username = document.getElementById('username').value;
- Retrieves the value of the input element with ID username, where the user enters their GitHub username.
Selecting DOM Elements:
const profileDiv = document.getElementById('profile');
const loadingSpinner = document.getElementById('loading');
- Selects elements with IDs profile and loading, which display the profile information and loading spinner, respectively.
Check if Username is Provided:
if (username) {
- Only proceeds if the username field has a value, preventing empty requests.
Show Loading Spinner and Hide Profile Div:
profileDiv.style.display = "none";
loadingSpinner.style.display = "block";
- Hides the profileDiv and shows the loadingSpinner to indicate data is being fetched.
Fetch API Request:
const response = await fetch(`https://api.github.com/users/${username}`);
- Makes a GET request to the GitHub API to fetch user data for the specified username. The await pauses execution until the request completes.
Convert Response to JSON:
const data = await response.json();
- Converts the response to JSON format to access the GitHub user’s data.
Hide Loading Spinner:
loadingSpinner.style.display = "none";
- Hides the loading spinner once the data is received.
Error Handling:
if (data.message === 'Not Found') {
- Checks if GitHub returned a Not Found error. If so, displays an error message in the profileDiv.
Display User Profile Information:
profileDiv.innerHTML = `
<img src="${data.avatar_url}" alt="${data.login}">
<h2>${data.name || data.login}</h2>
<p>${data.bio || 'No bio available'}</p>
...
`;
- If the user is found, it fills the profileDiv with HTML containing the user’s profile data, including their avatar, name, bio, location, company, Twitter handle, account creation date, repository count, followers, and following count.
Profile Stats:
<div class="profile-stats">
<div class="stat">
<p>${data.public_repos}</p>
<span>Repos</span>
</div>
...
</div>
- Displays the user's GitHub statistics, including the count of repositories, followers, and following, each wrapped in a stat div for easy styling.
Error Handling for Fetch Request Failure:
catch (error) {
loadingSpinner.style.display = "none";
profileDiv.innerHTML = "<p class='error'>An error occurred. Please try again later.</p>";
profileDiv.style.display = "block";
}
- If the fetch request fails, it hides the loading spinner, shows the profileDiv, and displays an error message.
async function fetchProfile() {
const username = document.getElementById('username').value;
const profileDiv = document.getElementById('profile');
const loadingSpinner = document.getElementById('loading');
if (username) {
profileDiv.style.display = "none";
loadingSpinner.style.display = "block";
try {
const response = await fetch(`https://api.github.com/users/${username}`);
const data = await response.json();
loadingSpinner.style.display = "none";
if (data.message === 'Not Found') {
profileDiv.innerHTML = "<p class='error'>User not found. Please try again.</p>";
profileDiv.style.display = "block";
} else {
profileDiv.innerHTML = `
<img src="${data.avatar_url}" alt="${data.login}">
<h2>${data.name || data.login}</h2>
<p>${data.bio || 'No bio available'}</p>
<span><strong>Location:</strong> ${data.location || 'Not specified'}</span>
<span><strong>Company:</strong> ${data.company || 'Not specified'}</span>
<span><strong>Twitter:</strong> ${data.twitter_username ? '@' + data.twitter_username : 'Not available'}</span>
<span><strong>Member since:</strong> ${new Date(data.created_at).toLocaleDateString()}</span>
<div class="profile-stats">
<div class="stat">
<p>${data.public_repos}</p>
<span>Repos</span>
</div>
<div class="stat">
<p>${data.followers}</p>
<span>Followers</span>
</div>
<div class="stat">
<p>${data.following}</p>
<span>Following</span>
</div>
</div>
<a href="${data.html_url}" target="_blank">Visit GitHub Profile</a>
`;
profileDiv.style.display = "block";
}
} catch (error) {
loadingSpinner.style.display = "none";
profileDiv.innerHTML = "<p class='error'>An error occurred. Please try again later.</p>";
profileDiv.style.display = "block";
}
}
}
Final Output:
Conclusion:
Congratulations! You’ve built a GitHub Profile Finder using HTML, CSS, and JavaScript. This project gave you experience with APIs, working with JSON data, and dynamically updating the DOM. Experiment with adding more features, like error handling or additional user statistics.
By following these steps, you’ve created a tool to fetch and display GitHub user data interactively, enhancing your JavaScript skills. Keep practicing, and you’ll be ready for more complex projects in no time!
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊