DAY 34: Mastered React Router & Build a GitHub User Finder App | ReactJS


π Introduction
Welcome to Day 34 of my Web Development Journey!
After building a solid foundation with HTML, CSS, and JavaScript, Iβve been diving into ReactJS β a powerful library for creating dynamic, interactive user interfaces.
Over the past few days, I focused on mastering React Router, exploring routing concepts like route configuration, Link vs NavLink, dynamic routes, and the useParams
hook. Alongside, I built a GitHub User Finder App, which really boosted my confidence with React.
To stay consistent and share my learnings, Iβm documenting this journey publicly. Whether youβre just starting with React or need a refresher, I hope this blog offers something valuable!
Feel free to follow me on Twitter for real-time updates and coding insights!
π Hereβs What I Covered Over the Last 3 Days:
Day 31:
- Deep-dived into React Router
- Explored route configurations and outlets
- Learned about dynamic routing
- Compared Link vs NavLink
- Practiced using the
useParams
hook
Day 32:
- Started building the GitHub User Finder App
- Created UI components
- Used
useReducer
for state management
Day 33:
- Fetched data from the GitHub API
- Managed state with dispatch and actions
- Rendered user data dynamically on the UI
- Implemented error handling and loading states
Letβs dive into these concepts and the app in more detail below π
1. React Router:
Here, Iβll explain the core concepts of React Router that I used in my project, including route configuration, outlets, Link vs NavLink, dynamic routing, and the useParams
hook. Below is an overview of how these pieces work together in my app.
1. React Router Setup & Route Configuration:
React Router helps in creating client-side routing in React apps, enabling navigation without full page reloads.
In my project, I used the createBrowserRouter
API to define the route configuration:
const router = createBrowserRouter([
{
path: "/",
element: <Layout />, // Shared layout component with header/footer
children: [
{ index: true, element: <Home /> }, // Default route for "/"
{ path: "about", element: <About /> }, // "/about"
{ path: "contact", element: <Contact /> }, // "/contact"
{ path: "user/:id", element: <User /> }, // Dynamic route with user id
{ path: "github", element: <Github /> }, // "/github"
],
},
]);
createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);
- The
path
defines the URL segment. - The
element
is the React component to render for that route. - Nested
children
routes allow creating a nested UI structure.
2. Outlets: Nested Route Rendering
The Layout component wraps all pages and includes common UI elements like header and footer.
import { Outlet } from "react-router-dom";
const Layout = () => {
return (
<>
<Header />
<Outlet /> {/* Renders the matched child route component here */}
<Footer />
</>
);
};
export default Layout;
- The
<Outlet />
component is a placeholder where the child routes get rendered inside the parent layout. - This allows reusing the same header/footer and switching the main content based on the route.
3. Link vs NavLink:
Link
is used to navigate between routes without refreshing the page.NavLink
is similar toLink
but adds styling attributes when the link is active, which is useful for navigation menus to highlight the current page.
<NavLink
to="/about"
className={({ isActive }) =>
`... ${isActive ? "text-orange-700" : "text-gray-700"} ...`
}
>
About
</NavLink>
Here, the className
function receives an isActive
boolean that indicates if the link matches the current URL.
- If
isActive
istrue
, the link text will have thetext-orange-700
class (highlighted color). - If
isActive
isfalse
, it getstext-gray-700
(normal color).
This dynamic styling makes it easy to visually distinguish the active navigation link.
4. Dynamic Routing with URL Parameters:
Routes like /user/:id
include dynamic segments.
{
path: "user/:id",
element: <User />,
}
- The
:id
is a URL parameter that can represent any user ID. - React Router parses this and passes it to the component via the
useParams()
hook.
5. Using useParams Hook to Access Route Params:
In the User
component, I accessed the dynamic id
parameter like this:
import { useParams } from "react-router-dom";
const User = () => {
const { id } = useParams(); // Extract "id" from the URL params
return (
<h1 className="bg-teal-700 py-8 text-4xl text-center text-white font-semibold">
User : {id}
</h1>
);
};
export default User;
useParams()
returns an object of key-value pairs representing the dynamic segments.- This allows components to render content dynamically based on the route.
Final Thoughts:
- React Router is a powerful library that simplifies navigation and routing in React applications.
- Using route configuration with nested routes and the
<Outlet />
component helps create clean, reusable layouts with shared UI elements like headers and footers. - The distinction between Link and NavLink is useful for building navigation menus that clearly indicate the active page with dynamic styling.
- Dynamic routing with URL parameters (like
/user/:id
) combined with the useParams hook enables highly flexible and dynamic page rendering based on route data. - Mastering these concepts makes building scalable and user-friendly React apps much easier and more maintainable.
2. Github User Finder App:
After learning React hooks like useReducer
and handling side effects with useEffect
, I wanted to build a practical app that consumes real-world APIs and manages complex state elegantly. This led me to create a GitHub User Finder β a sleek tool that lets users search for GitHub profiles and instantly view their details and repositories.
Project Overview:
This GitHub User Finder App allows users to:
- Search for any GitHub username and fetch profile data dynamically
- View detailed user info including avatar, bio, location, company, blog, and social links
- See real-time follower, following, and repository statistics
- Browse the latest public repositories with metadata like stars, forks, language, and creation date
- Handle loading states and error messages (e.g., user not found or network errors) gracefully
- Use environment variables to securely store and use GitHub API tokens
- Enjoy a responsive and clean UI built with React and Tailwind CSS
The app is built entirely with React functional components and uses useReducer
for state management along with multiple useEffect
hooks for API calls.
Key Features & Functionalities:
GitHub API Integration
- User Search: Fetches user profiles via GitHub API
- Repository Fetching: Automatically retrieves user repositories
- Authentication: Uses GitHub token for API access
State Management
- Reducer Pattern: Centralized state management with actions
- Loading States: Proper loading indicators during API calls
- Error Handling: Comprehensive error management for various scenarios
User Experience
- Responsive Design: Mobile-first approach with Tailwind CSS
- Keyboard Support: Enter key triggers search
- Real-time Updates: Immediate UI updates based on API responses
Data Display
- Profile Information: Avatar, bio, location, join date
- Social Links: Company, blog, Twitter integration
- Statistics: Followers, following, repository count
- Repository Details: Name, description, language, stars, forks, creation date
Performance Optimizations
- Limited Repository Display: Shows only 6 most recent repositories
- Conditional Rendering: Components render based on data availability
- Efficient State Updates: Batch updates using
SET_ALL
action
This project demonstrates a well-structured React application with proper separation of concerns, efficient state management, and a clean component hierarchy for building a GitHub user search and profile viewing application.
React Concepts Applied:
useReducer
β to manage complex state including query, search term, user data, loading state, error messages, and repos.useEffect
β two separate effects: one watches forsearchTerm
changes to fetch user data, the other watchesuserData
to fetch repositories.- Environment Variables β for storing the GitHub token securely (
import.meta.env.VITE_GITHUB_TOKEN
). - Component Composition β breaking the UI into reusable components like
SearchUser
,UserDetails
,ProfileStats
,RepositoryList
, andRepositoryCard
. - Conditional Rendering β to handle loading screens, errors, and initial empty states.
- Utility Functions β like
formatDate
to format dates neatly for UI.
Folder Structure:
src/
βββ App.jsx
βββ index.css
βββ main.jsx
βββ components/
β βββ Button.jsx
β βββ ProfileStats.jsx
β βββ RepositoryCard.jsx
β βββ RepositoryList.jsx
β βββ SearchUser.jsx
β βββ StatsCard.jsx
β βββ UserDetails.jsx
βββ utils/
βββ formatDate.js
Code Walkthrough:
1. Main Entry & State Management (App.jsx
)
The app uses useReducer
to handle complex state transitions related to the input query, search term, loading state, error messages, user data, and repository lists.
The ACTIONS
object defines all possible action types to update specific parts of the state. The initialState
sets the default values for all these properties.
The reducer
function listens for dispatched actions and updates the state accordingly:
- For example,
SET_QUERY
updates the search input value, SET_SEARCH_TERM
triggers the actual search,SET_USER_DATA
stores fetched user profile data,SET_ERROR
handles error messages,SET_LOADING
manages loading indicators,SET_REPOS
saves the list of repositories,SET_ALL
allows batch updates of multiple state properties at once.
This pattern centralizes and organizes state changes in a predictable way, making state management more maintainable as the app grows.
export const ACTIONS = {
SET_QUERY: "set_query",
SET_USER_DATA: "set_user_data",
SET_SEARCH_TERM: "set_search_term",
SET_ERROR: "set_error",
SET_LOADING: "set_loading",
SET_REPOS: "set_repos",
SET_ALL: "set_all",
};
const initialState = {
query: "",
userData: null,
searchTerm: "",
error: "",
loading: false,
repos: [],
};
const reducer = (state, action) => {
switch (action.type) {
case ACTIONS.SET_QUERY:
return { ...state, query: action.query };
case ACTIONS.SET_SEARCH_TERM:
return { ...state, searchTerm: action.searchTerm };
case ACTIONS.SET_USER_DATA:
return { ...state, userData: action.data };
case ACTIONS.SET_ERROR:
return { ...state, error: action.message };
case ACTIONS.SET_LOADING:
return { ...state, loading: action.loading };
case ACTIONS.SET_REPOS:
return { ...state, repos: action.repos };
case ACTIONS.SET_ALL:
return { ...state, ...action.payload };
default:
return state;
}
};
2. Fetching User Data & Repositories
Two useEffect
hooks orchestrate data fetching:
The first
useEffect
listens for changes in thesearchTerm
state. When a new search term is set, it triggers a fetch request to get the GitHub userβs profile data.useEffect(() => { if (!state.searchTerm.trim()) return; if (!token) { dispatch({ type: ACTIONS.SET_ALL, payload: { error: "GitHub token missing", query: "" }, }); return; } dispatch({ type: ACTIONS.SET_ALL, payload: { loading: true, error: "", repos: [] }, }); fetch(`https://api.github.com/users/${state.searchTerm}`, { headers: { Authorization: `Bearer ${token}`, }, }) .then((res) => { return res.json(); }) .then((data) => { if (data?.message === "Not Found") { dispatch({ type: ACTIONS.SET_ALL, payload: { error: "No User Found", query: "", loading: false, }, }); return; } dispatch({ type: ACTIONS.SET_ALL, payload: { userData: data, query: "", error: "", loading: false, }, }); }) .catch((err) => { dispatch({ type: ACTIONS.SET_ALL, payload: { error: "Network Error", query: "", loading: false, }, }); }); }, [state.searchTerm]);
- Before fetching, it checks if the search term is empty or if the GitHub token is missing and handles these cases with appropriate error messages.
- While fetching, it sets the loading state to show a spinner or loading text.
- On success, it updates the state with the user data and clears any previous errors.
- If the user is not found, it sets an error message accordingly.
- Any network errors during fetch also update the state with an error message.
The token is included in the API request headers for authentication.
The second
useEffect
runs whenuserData
becomes available after a successful user fetch. It then fetches the userβs repositories separately from the URL provided inuserData.repos_url
.useEffect(() => { if (!state.userData) return; fetch(state.userData.repos_url) .then((res) => res.json()) .then((data) => { if (!Array.isArray(data)) { dispatch({ type: ACTIONS.SET_ALL, payload: { error: "Repos not available", repos: [] }, }); return; } dispatch({ type: ACTIONS.SET_ALL, payload: { repos: data, error: "" }, }); }) .catch((err) => { console.log(err); dispatch({ type: ACTIONS.SET_ERROR, message: "Repos Not Found" }); }); }, [state.userData]);
- It validates the repository data to ensure itβs an array before updating the state.
- If repositories arenβt available or an error occurs, it sets an error message related to repo fetching.
3. Search Input (SearchUser.jsx)
This component manages the user input for the GitHub username search. It updates the query state as the user types and triggers the search when the user either clicks the search button or presses the Enter key.
By dispatching the SET_SEARCH_TERM
action with the current query, it updates the searchTerm
in the global state, which in turn initiates the data fetching process in the main component.
const SearchUser = ({ dispatch, state }) => {
const handleSearch = () => {
if (!state.query.trim()) return;
dispatch({ type: ACTIONS.SET_SEARCH_TERM, searchTerm: state.query });
};
// input field and search button UI
};
4. User Details (UserDetails.jsx)
This component displays detailed information about the GitHub user, including their avatar, name, bio, location, and formatted join date. It also shows the user's company, blog domain, and Twitter link if available. A button is provided to directly visit the user's GitHub profile.
The component uses helper subcomponents for cleaner organization:
- ProfileStats: Displays the count of followers, following, and public repositories.
- RepositoryList: Shows up to 6 of the userβs latest repositories with details like name, description, language, stars, forks, and creation date.
5. Repository List:
The RepositoryList
component is responsible for displaying the user's repositories. It renders a heading and then displays up to 6 of the most recent repositories in a responsive grid layout.
Each repository is passed as props to the RepositoryCard
component, which shows details such as:
- Repository name
- Description (or a fallback message if none)
- Star count
- Primary language
- Fork count
- Creation date (formatted)
This component helps keep the repository display concise and organized for better user experience.
const RepositoryList = ({ repos }) => {
return (
<div className="">
<h2 className="mt-2 border-b-2 border-[#323232] pb-4 text-xl font-bold">
Latest Repositories
</h2>
<div className="mt-4 grid gap-4 md:grid-cols-2">
{repos.slice(0, 6).map((repo) => {
return (
<RepositoryCard
key={repo.id}
name={repo.name}
desc={repo.description}
star={repo.stargazers_count}
lang={repo.language}
fork={repo.forks_count}
date={formatDate(repo.created_at)}
/>
);
})}
</div>
</div>
);
};
6. Repository Cards (RepositoryCard.jsx)
Each repository card presents key information about a repository in a clean, organized layout. It displays:
- Repository name with a code-branch icon
- Description, or a fallback message if no description is provided
- Primary programming language with a colored circle icon
- Star count with a star icon
- Fork count with a fork icon
- Creation date with a clock icon
Icons alongside text improve clarity and visual appeal, making it easy for users to scan repository details quickly.
7. Utility (formatDate.js)
This utility function formats date strings received from the GitHub API into a more readable, short date format. For example, it converts "2023-01-01T00:00:00Z"
into "Jan 1, 2023"
, improving the display of dates throughout the app.
export const formatDate = (dateStr) => {
const date = new Date(dateStr);
const formattedDate = date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
return formattedDate;
};
UI & UX Details:
- Dark-themed UI with purple accents, styled using Tailwind CSS
- Clear loading indicators and user-friendly error messages
- Responsive design optimized for both desktop and mobile devices
- Smooth user experience with instant updates and intuitive navigation
GitHub Repository:
You can check out the full source code of this project here:
GitHub - Github User Finder App
Final Thoughts:
This GitHub User Finder project gave me hands-on experience with:
- Managing complex state transitions cleanly with
useReducer
. - Coordinating multiple API calls with dependent data.
- Handling loading and error states comprehensively.
- Building modular, reusable React components with clear responsibilities.
- Securely using environment variables for sensitive tokens.
- Applying real-world API integration in React apps with responsive and accessible UI.
The app serves as a practical example of combining React hooks, API consumption, and thoughtful UI design to build a useful developer tool.
3. Whatβs Next:
Iβm excited to keep growing and sharing along the way! Hereβs whatβs coming up:
- Posting new blog updates every 3 days to share what Iβm learning and building.
- Diving deeper into Data Structures & Algorithms with Java β check out my ongoing DSA Journey Blog for detailed walkthroughs and solutions.
- Sharing regular progress and insights on X (Twitter) β feel free to follow me there and join the conversation!
Thanks for being part of this journey!
4. Conclusion:
In this blog of my web dev journey, I explored two important parts of modern React development:
React Router: I demonstrated how to set up client-side routing with nested routes, dynamic URL parameters, and navigation links that reflect the active page. Understanding these concepts makes it easier to build scalable React apps with clean, maintainable navigation structures.
GitHub User Finder App: I built a practical application consuming GitHub's API using React hooks like
useReducer
anduseEffect
. This project showcased how to manage complex state transitions, coordinate multiple API calls, handle loading and error states gracefully, and create a responsive, user-friendly interface with Tailwind CSS.
Together, these topics reinforce key React patterns and best practices, from routing to state management to API integration, helping to develop solid skills for building real-world web applications.
Feel free to check out the full source code on GitHub and try building on top of this foundation to create even more powerful React projects!
Thanks for reading, and feel free to connect or follow along as I continue building and learning.
Subscribe to my newsletter
Read articles from Ritik Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ritik Kumar
Ritik Kumar
π¨βπ» Aspiring Software Developer | MERN Stack Developer.π Documenting my journey in Full-Stack Development & DSA with Java.π Focused on writing clean code, building real-world projects, and continuous learning.