The full source code for the ReactSparkPortfolio is available on GitHub

Building My First React Site Using Vite and GitHub

As I work on the project, GitHub pages will be the place to view the application: ReactSparkPortfolio on GitHub Pages



Why Learn React Native and TypeScript?

I am currently working on a team that develops with React Native and TypeScript for both web and mobile. To date, most of my work has been in C#, on the API side of things. Working with web APIs, Microsoft SQL Server, and Azure Cloud Services has been my bread and butter for the past few years. I wanted to expand my skill set and learn more about what fellow developers where using. I started with Vite, a next-generation front-end build tool. Vite is fast, lean, and highly configurable.

React Native, with its component-based architecture, felt a bit like working with C# classes but with a twist. Each React component is a self-contained unit, much like a class, which you can compose together to build complex UIs. The addition of TypeScript was particularly comforting, as it brought in type safety and a clear structure to the code, reducing the likelihood of bugs and making the codebase easier to maintain.

I have enjoyed learning about React Native with TypeScript. It combines the flexibility of JavaScript for mobile and web development with the robust type-checking of TypeScript, creating a powerful toolkit for building scalable and maintainable applications. It's been a rewarding journey to expand my skills and experience.

A step-by-step guide to setting up and deploying a React project

I started my first project, a React portfolio project using Vite and deployed it to GitHub Pages. Vite offers an incredibly fast and efficient way to create and deploy React sites. I want to walk through the process from setting up Vite to deploying the project on GitHub Pages.

Initial Project Setup

I started by using Vite's project scaffolding for React. This sets up the initial folder structure and installs dependencies. I then navigated to the project folder and installed the project dependencies.

npm create vite@latest
cd ReactSparkPortfolio
npm install
Writing and Organizing My React Components

After setting up the project, I created components for my portfolio. I wanted a simple, clean layout with sections for Home, About, Projects, and Joke.

import React, { useState } from 'react';
import Header from './components/Header';
import Hero from './components/Hero';
import About from './components/About';
import Projects from './components/Projects';
import Joke from './components/Joke';
import Footer from './components/Footer';

const App: React.FC = () => {
  const [activeSection, setActiveSection] = useState<string>('home');

  const handleSectionChange = (section: string) => {
    setActiveSection(section);
  };

  return (
    <div className="d-flex flex-column min-vh-100">
      <Header onSectionChange={handleSectionChange} />
      <main className="flex-grow-1 pt-5 mt-5 container">
        <div className="row justify-content-center">
          <div className="col-md-10">
            {activeSection === 'home' && <Hero />}
            {activeSection === 'about' && <About />}
            {activeSection === 'projects' && <Projects />}
            {activeSection === 'joke' && <Joke />}
          </div>
        </div>
      </main>
      <Footer />
    </div>
  );
};

export default App;
Configure Vite for GitHub Pages

Vite builds the production files into a "dist" folder by default. For GitHub Pages, I modified Vite’s configuration to output into the "docs" folder. This makes setting up the GitHub Pages deployment easier as it serves the site from the "docs" folder by default.

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  base: "./",
  build: {
    outDir: "docs",
  },
  plugins: [react()],
});
Adding the Articles Component

To display content from an RSS feed, create an "Articles" component that fetches and parses the RSS XML file, rendering article titles and links.

This TypeScript code defines a React functional component that dynamically fetches and displays a list of articles from an RSS feed. The component uses React’s useState hook to create a state variable that holds the articles, which is initialized as an empty array. The state is updated once the RSS feed is fetched and parsed. Additionally, useEffect is used to ensure that the fetch operation happens when the component first renders. Since useEffect is invoked with an empty dependency array, it acts like a componentDidMount lifecycle event, executing the fetch operation only once when the component is mounted.

The RSS feed is fetched asynchronously using the native fetch API, targeting the /rss.xml endpoint on the server. After retrieving the raw XML data as plain text, the code uses DOMParser to parse this text into an XML structure. Once parsed, the <item> elements, which represent individual articles, are extracted. For each <item>, the component pulls relevant data such as the title, link, and publication date by accessing the corresponding child elements. This extracted information is stored in a structured array, which is then used to update the component’s state.

Once the state is updated with the parsed articles, the component renders a list of articles in the browser. Each article is displayed as a clickable link along with its publication date. The component efficiently maps through the state array of articles to dynamically generate the list items, ensuring that the data is presented as soon as it is fetched and parsed. By combining useEffect for side effects and useState for managing data, the component handles asynchronous data fetching and re-renders itself when new data becomes available, creating a simple and effective way to display RSS feed content in a React application.

import React, { useEffect, useState } from 'react';

const Articles: React.FC = () => {
  const [articles, setArticles] = useState<any[]>([]);

  useEffect(() => {
    const fetchRSSFeed = async () => {
      const response = await fetch('/rss.xml');
      const rssData = await response.text();

      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(rssData, 'application/xml');
      const items = xmlDoc.getElementsByTagName('item');

      const parsedArticles = Array.from(items).map(item => ({
        title: item.getElementsByTagName('title')[0].textContent,
        link: item.getElementsByTagName('link')[0].textContent,
        pubDate: item.getElementsByTagName('pubDate')[0].textContent,
      }));

      setArticles(parsedArticles);
    };
    fetchRSSFeed();
  }, []);

  return (
    <div>
      <h2>Latest Articles</h2>
      <ul>
        {articles.map((article, index) => (
          <li key={index}>
            <a href={article.link}>{article.title}</a> - {article.pubDate}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Articles;
Deploying the Site on GitHub Pages

Deploying a Vite site to GitHub Pages involves configuring a GitHub Actions workflow. This automates the process and pushes the build output to the "docs" folder on every push.

name: Deploy React App to GitHub Pages

on:
push:
  branches:
  - main

jobs:
build:
  runs-on: ubuntu-latest
  steps:
  - name: Checkout code
    uses: actions/checkout@v2

  - name: Set up Node.js
    uses: actions/setup-node@v2
    with:
    node-version: '16'

  - name: Install dependencies
    run: npm install

  - name: Build the project
    run: npm run build  # No need to move the folder since it's already output to docs

  - name: Deploy to GitHub Pages
    uses: peaceiris/actions-gh-pages@v3
    with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./docs

permissions:
contents: write
Final Thoughts

Vite simplified React development with features like instant hot module replacement and minimal configuration. With just a few steps, I was able to deploy a project using GitHub Pages. Vite offers flexible solutions that help you get your project online quickly. Now that I got the initial project completed and deployed, I can focus on adding more features and content to the project as I learn more about React Native with TypeScript.

Explore TypeScript concepts through examples and interactive code

Type annotations in TypeScript allow developers to explicitly define the data types for variables, function arguments, and return values. This proactive approach helps catch potential errors early in the development process, promoting the use of correct data types and improving overall code stability. Unlike JavaScript, which is more flexible with data types, TypeScript enforces stricter rules, bringing more predictability and helping to prevent unexpected bugs.

In the context of React Native, type annotations play a critical role in managing components and their props. By ensuring props are correctly typed, they help reduce runtime errors. Type annotations also simplify state management and API interactions by enforcing data structures and identifying mismatches early on. Overall, they contribute to a smoother development experience, especially with TypeScript’s helpful autocompletion and type inference features.

Type annotations in my App.tsx file enhance code quality and developer experience by ensuring type safety and improving readability. Integrating these annotations fosters a more reliable and maintainable codebase. Here are three key points:

  • Component Definition: The React.FC annotation in const App: React.FC identifies App as a React functional component, ensuring type safety for any props it might receive.
  • State Management: Using <string> in useState<string>('home') ensures that activeSection is always treated as a string, catching type errors early and improving code robustness.
  • Function Parameters: Annotating parameters like (section: string) in handleSectionChange ensures that only strings are passed to this function, preventing potential bugs from incorrect argument types.
const App: React.FC = () => {
  // State to keep track of which section is active
  const [activeSection, setActiveSection] = useState<string>('home');
  // Handler to update the active section based on navigation clicks
  const handleSectionChange = (section: string) => {
    setActiveSection(section);
  };
  return (
    <div className="d-flex flex-column min-vh-100">
      <Header onSectionChange={handleSectionChange} />
      <main className="flex-grow-1 pt-5 mt-5 container">
        <div className="row justify-content-center">
          <div className="col-md-10">
            {activeSection === 'home' && <Hero />}
            {activeSection === 'about' && <About />}
            {activeSection === 'projects' && <Projects />}
            {activeSection === 'contact' && <Contact />}
          </div>
        </div>
      </main>
      <Footer />
    </div>
  );
}

Functions with typed parameters and return types are essential in TypeScript for making sure that your code is robust and less prone to errors. This is particularly useful in a React Native app, where type safety can help you avoid many common bugs.

Typed Parameters
This allows you to specify what type of arguments a function expects. If a function receives an argument of a different type, TypeScript will throw an error.
Return Types
This enables you to define what type of value a function will return. This ensures that your function always returns the expected type of value.
import React from 'react';
import { View, Text } from 'react-native';

// Define a type for the function parameters
type GreetingProps = {
  name: string;
  age: number;
};

// Define the function with typed parameters and return type
const Greet: React.FC<GreetingProps> = ({ name, age }: GreetingProps): JSX.Element => {
  return (
    <View>
      <Text>Hello, my name is {name} and I am {age} years old.</Text>
    </View>
    );
};

// Usage of the function in a React component
const App: React.FC = () => {
  return (
    <View>
      <Greet name="Alice" age={30} />
    </View>);
};

export default App;
GreetingProps
We define a type for the function parameters. It expects two properties: name (a string) and age (a number).
Greet
This is a React functional component that uses the GreetingProps type for its parameters. The function returns a JSX element (the type is JSX.Element).
App
This is the main component that uses the Greet component, passing the required props (name and age).

By using typed parameters and return types, you ensure that your components receive the expected types, reducing the chance of bugs and making your code easier to maintain. Plus, TypeScript provides auto-completion and type-checking features, which makes development more efficient.

In TypeScript, function parameters can be optional or have default values. Optional and default parameters in TypeScript provide a way to make functions and components more flexible and robust:

Optional Parameters
These allow you to call functions without providing all arguments, ensuring smoother function execution even with missing inputs.
Default Parameters
They provide a fallback value if no argument is supplied, preventing undefined values and potential errors.
Type Safety
Both optional and default parameters enhance code readability and reliability by explicitly defining the expected types and values, reducing bugs and improving maintainability.

In Hero.tsx, the concept of optional and default parameters is applied to enhance flexibility and robustness. The component accepts an optional profileData parameter, allowing it to function seamlessly even when this data isn't provided. If profileData is not available, the function defaults to a predefined defaultProfile, ensuring the component always has data to render. This approach reduces the risk of errors due to missing information and maintains the smooth operation of the component under various circumstances. Ultimately, it exemplifies how TypeScript's optional and default parameters can create more adaptable and resilient code.

import React from 'react';
import defaultProfile from '../data/profile.json'; // Adjust the path according to where you place profile.json

interface Profile {
name: string;
introduction: string;
ctaLink: string;
ctaText: string;
}

const renderHero = (profileData?: Profile) => {
const profile = profileData || defaultProfile;

// Check if profile is null or empty
if (!profile || Object.keys(profile).length === 0) {
  return (
  <section id="hero" className="bg-secondary text-white text-center py-5">
    <div className="container">
    <div className="row justify-content-center">
      <div className="col-md-8">
      <h1 className="display-4">Error</h1>
      <p className="lead">Profile data is missing or unavailable.</p>
      </div>
    </div>
    </div>
  </section>
  );
}
return (
  <section id="hero" className="bg-secondary text-white text-center py-5">
  <div className="container">
    <div className="row justify-content-center">
    <div className="col-md-8">
      <h1 className="display-4">{profile.name}</h1>
      <p className="lead">{profile.introduction}</p>
      <a href={profile.ctaLink} className="btn btn-primary btn-lg mt-4">
      {profile.ctaText}
      </a>
    </div>
    </div>
  </div>
  </section>
);
};

const Hero: React.FC<{ profileData?: Profile }> = ({ profileData }) => {
return renderHero(profileData);
};

export default Hero;



TypeScript interfaces are powerful tools that define the structure of an object, detailing the expected properties and their types. They enforce type-checking, ensuring that any object adhering to the interface meets these criteria, thus reducing runtime errors. In the Articles.tsx file, the Article interface outlines the structure for an article object, specifying that each article should have a title, link, and pubDate, all of which are strings.

This interface is then used in the state management, ensuring that the articles state array contains objects that conform to this structure. By defining the shape of the article data upfront, the code becomes more predictable and easier to maintain, providing clear expectations for the data being handled.

interface Article {
title: string;
link: string;
pubDate: string;
}

Type aliases allow you to define custom types, which can make your code more readable and reusable. A Type Alias in TypeScript provides a new name for any type, like a shorthand or simplification. This could be a primitive type, a union of types, an object type, or any other structure. It's a way to make complex types more readable and easier to manage.

The keyword type is used to create these aliases, essentially giving a new name to an existing type or a complex structure. This is especially useful for making your code cleaner and more understandable, particularly when dealing with intricate type definitions. They don't add any new functionality or behavior to the type itself—they just give it a more convenient or meaningful name.

On the other hand, the type keyword in TypeScript is also used to define these Type Aliases. So when we talk about 'type' in TypeScript, it often refers to the process of creating these aliases, encapsulating the concept of type definitions that enhance code clarity and maintenance. Here is a type alias example in TypeScript that defines an article.

type Article = {
  title: string;
  link: string;
  pubDate: string;
};

TypeScript's Union and Intersection types add flexibility and power to type definitions, particularly useful in complex React applications.

Union Types
They allow a variable to be one of several types. For example, if you have a component that can accept a string or number as a prop, you’d use a union type. This is handy for functions and components that can handle multiple types of inputs.
Intersection Types
They combine multiple types into one. This means an object of this type must satisfy all the combined types. Useful when you need an object to have multiple sets of properties.

Example in a Web Application: Imagine you have a UserProfile component that displays different user information based on user roles.

Union Type:
The user prop can be either User or SuperUser. This allows the UserProfile component to accept different user types, providing flexibility.
Intersection Type:
SuperUser combines the properties of both Admin and User, meaning a SuperUser must have all properties of both types. This is useful for users who have combined roles.
import React from 'react';

type Admin = {
  adminRights: string[];
};

type User = {
  name: string;
  email: string;
};

type SuperUser = Admin & User;

const UserProfile: React.FC<{ user: User | SuperUser }> = ({ user }) => {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      {'adminRights' in user && (
        <ul>
          {user.adminRights.map((right) => (
            <li key={right}>{right}</li>))}
        </ul>
      )}
    </div>
    );
  };

export default UserProfile;

Generics in TypeScript provide a way to create reusable components that work with a variety of types instead of a single one. They allow you to write flexible and type-safe code by enabling you to define components, functions, or hooks that can operate with different data types while preserving type information. In the context of React, generics are often used in components and hooks to ensure they can handle a wide range of data types without losing the benefits of TypeScript's type-checking.

Example in a Web Application: Let's say you have a list component that can render lists of different types, such as users, products, or orders. By using generics, you can create a single List component that works for any type of data.

  • The List component is a generic component that takes a type parameter T.
  • The ListProps interface uses this type parameter to define the shape of the props.
  • The items prop is an array of T, and the renderItem prop is a function that takes an item of type T and returns a React node.

Using generics this way ensures that the List component can handle any type of data, making it highly reusable and type-safe. This flexibility is a powerful feature of TypeScript, allowing you to write more generic and adaptable components while retaining strong type-checking benefits.

import React from 'react';

interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
    {items.map((item, index) => (
      <li key={index}>{renderItem(item)}</li>)
    )}
    </ul>
  );
}
const users = [{ name: 'Alice' }, { name: 'Bob' }];
const products = [{ name: 'Laptop' }, { name: 'Phone' }];

const App: React.FC = () => {
  return (
    <div>
      <h2>Users:</h2>
      <List items={users} renderItem={(user) => <span>{user.name}</span>} />
      <h2>Products:</h2>
      <List items={products} renderItem={(product) => <span>{product.name}</span>} />
    </div>
    );
};
export default App;

TypeScript type assertions are a way to tell the compiler that you know more about the type of a value than it does. It's essentially a way to override the compiler's inferred type and specify a more specific type, which can be useful in certain scenarios. For instance, let's say you retrieve a value from an API response that is of type unknown or any, and you know it’s actually a string. You can use type assertions to inform TypeScript of this fact and proceed with string-specific operations. There are two syntaxes for type assertions: the as syntax and the angle-bracket <Type> syntax.

Imagine you have an input field where users can enter their age, and you want to ensure that the value is treated as a number. Since the input value is always a string by default, you can use a type assertion to convert it to a number.

import React, { useState } from 'react';
const AgeInput: React.FC = () => {
  const [age, setAge] = useState<number | null>(null);

  const handleAgeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const inputAge = e.target.value as unknown as number;
    setAge(inputAge);
  };

  return (
    <div>
      <label htmlFor="age">Enter your age:</label>
      <input type="text" id="age" onChange={handleAgeChange} />
        {age !== null && <p>Your age is: {age}</p>}
    </div>
  );
};
export default AgeInput;

Enums, or enumerations, in TypeScript are a way to define a set of named constants. They can be a great tool to make your code more readable and expressive, especially when dealing with sets of related values. Enums help avoid magic strings or numbers in your code, making it easier to manage and understand. In the context of a React application, enums are often used to represent a fixed set of possible values for a prop, state, or parameter. This ensures that only valid values are used, reducing the risk of bugs.

Example in a Web Application: Suppose you are building a task management app where each task can have a specific status.

  • An enum TaskStatus is created to define the possible statuses a task can have: ToDo, InProgress, and Done.
  • The Task interface includes a status property that uses the TaskStatus enum, ensuring that only valid statuses can be assigned.
  • The TaskComponent uses the enum to manage and display the task's status, providing buttons to change the status, each using values from the TaskStatus enum.
import React, { useState } from 'react';

enum TaskStatus {
  ToDo = 'ToDo',
  InProgress = 'InProgress',
  Done = 'Done'
}

interface Task {
  id: number;
  title: string;
  status: TaskStatus;
}

const TaskComponent: React.FC<{ task: Task }> = ({ task }) => {
  const [status, setStatus] = useState<TaskStatus>(task.status);

  const handleStatusChange = (newStatus: TaskStatus) => {
    setStatus(newStatus);
  };

  return (
    <div>
      <h3>{task.title}</h3>
      <p>Status: {status}</p>
      <button onClick={() => handleStatusChange(TaskStatus.ToDo)}>To Do</button>
      <button onClick={() => handleStatusChange(TaskStatus.InProgress)}>In Progress</button>
      <button onClick={() => handleStatusChange(TaskStatus.Done)}>Done</button>
    </div>
    );
};

const App: React.FC = () => {
  const task: Task = {
    id: 1,
    title: 'Learn TypeScript enums',
    status: TaskStatus.ToDo
    };

  return <TaskComponent task={task} />;
};

export default App;

React Native, like React, allows components to receive data and event handlers through props. Using TypeScript with React Native adds the benefit of strong typing to these props, enhancing code quality and reducing bugs. Props in React Native: Props are used to pass data from one component to another, creating dynamic and reusable components. They ensure that components can be customized and controlled by their parent components.

GreetingProps Interface:

This defines the structure of the props expected by the Greeting component. It specifies that the component expects a name (mandatory string) and an optional age (number). The age property has a question mark (?), indicating it is optional.

Greeting Component:

This functional component takes GreetingProps as its props type. It destructures name and age from the props. Within the component:

  • It renders a View containing a Text element that always displays the name.
  • It conditionally renders another Text element showing the age if the age prop is provided.
  • This setup ensures that the Greeting component is type-safe and can flexibly handle the presence or absence of the age prop while always displaying the name.
interface GreetingProps {
  name: string;
  age?: number;
}

const Greeting: React.FC<GreetingProps> = ({ name, age }) => {
  return (
    <View>
      <Text>Hello, {name}!</Text>
      {age && <Text>You are {age} years old.</Text>}
    </View>
  );
};

In this example, name is a required prop, and age is optional. TypeScript will enforce the correct usage of these props when using the Greeting component.

TypeScript classes are a way to create reusable blueprints for objects, encapsulating both data and behavior. They bring an object-oriented approach to TypeScript, much like classes in C#. In the context the Article class, TypeScript classes allow you to define the structure and functionality of an Article. The class encapsulates properties like title, link, and pubDate, which are all essential attributes of an article. By defining these properties in the class, you ensure that every instance of Article has a consistent structure.

Classes also allow you to define a constructor, a special method used for creating and initializing objects of the class. In the Article class, the constructor takes parameters that correspond to the article's properties and assigns them to the class's properties. This ensures that whenever you create a new Article, you provide the necessary data for title, link, and pubDate.

TypeScript classes can include methods that define behaviors related to the class's data. In the Article class, for example, you might have a method called getSummary that returns a formatted string summarizing the article's information. This method encapsulates the behavior related to presenting an article's summary, keeping the logic within the class where it belongs.

The use of TypeScript classes also supports inheritance, where a new class can inherit properties and methods from an existing class. This promotes code reuse and allows for the creation of more specialized classes that build on the functionality of base classes.

Overall, TypeScript classes like Article provide a powerful way to define and work with structured data, offering clear, maintainable, and scalable code. They combine data and behavior in a way that aligns with object-oriented principles, making your TypeScript code more robust and easier to understand.

class Article {
  title: string;
  link: string;
  pubDate: string;

  constructor(title: string, link: string, pubDate: string) {
    this.title = title;
    this.link = link;
    this.pubDate = pubDate;
  }

  // Method to get a formatted article summary
  getSummary(): string {
    return `${this.title} was published on ${this.pubDate}. Read more at ${this.link}`;
  }
}

// Example usage
const article = new Article('New TypeScript Release', 'https://typescriptlang.org', '2024-10-12');
console.log(article.getSummary());


These TypeScript code samples cover core features that will improve the development experience for new developers. They ensure safer, more predictable code, especially in React Native projects where maintaining clarity in component structures is essential.




Adding React Router

I decided to implement React Router for navigation between sections using URLs like /#about and /#projects.

Here are the steps I took to integrate React Router into my portfolio project. This enabled me to navigate to different sections using URLs like /#about and /#projects.

Step 1: Install react-router-dom

The first step is to install the React Router library by running the following command:

npm install react-router-dom
Step 2: Update App.tsx to Use HashRouter

Next, update the `App.tsx` file to use `HashRouter` for URL navigation. The following code sets up routes for different sections of your portfolio:

import { HashRouter, Routes, Route } from 'react-router-dom';
import Header from './components/Header';
import Footer from './components/Footer';
import { lazy, Suspense } from 'react';

const Hero = lazy(() => import('./components/Hero'));
const About = lazy(() => import('./components/About'));
const Projects = lazy(() => import('./components/Projects'));
const Articles = lazy(() => import('./components/Articles'));
const Joke = lazy(() => import('./components/Joke'));

const App: React.FC = () => {
  return (
  <HashRouter>
    <div className="d-flex flex-column min-vh-100">
    <Header />
    <main className="flex-grow-1 pt-5 mt-5 container">
      <div className="row justify-content-center">
      <div className="col-md-10">
        <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/" element={<Hero />} />
          <Route path="/about" element={<About />} />
          <Route path="/projects" element={<Projects />} />
          <Route path="/joke" element={<Joke />} />
          <Route path="/articles" element={<Articles />} />
        </Routes>
        </Suspense>
      </div>
      </div>
    </main>
    <Footer />
    </div>
  </HashRouter>
  );
};

export default App;
Step 3: Modify Header.tsx to Use Links

Modify the `Header.tsx` file to replace traditional anchor `<a>` tags with React Router's `Link` component for route-based navigation:

import { Link } from 'react-router-dom';

const Header: React.FC = () => {
  return (
  <header>
    <nav className="navbar navbar-expand-lg navbar-light bg-light fixed-top">
    <div className="container-fluid">
      <Link className="navbar-brand" to="/">Mark Hazleton</Link>
      <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
      <span className="navbar-toggler-icon"></span>
      </button>
      <div className="collapse navbar-collapse" id="navbarNav">
      <ul className="navbar-nav">
        <li className="nav-item">
        <Link className="nav-link" to="/">Home</Link>
        </li>
        <li className="nav-item">
        <Link className="nav-link" to="/about">About</Link>
        </li>
        <li className="nav-item">
        <Link className="nav-link" to="/projects">Projects</Link>
        </li>
        <li className="nav-item">
        <Link className="nav-link" to="/articles">Articles</Link>
        </li>
        <li className="nav-item">
        <Link className="nav-link" to="/joke">Joke</Link>
        </li>
      </ul>
      </div>
    </div>
    </nav>
  </header>
  );
};

export default Header;
Step 4: Build the Project

Finally, build the project with the following command:

npm run clean
npm run build
npm run dev
Conclusion

Now I have added React Router to the portfolio project. With this setup, users can navigate different sections using hash-based URLs without reloading the page.




Deploy a Vite app to GitHub Pages

Deploying a Vite project to GitHub Pages can be straightforward, but requires attention to configuration, especially around base paths and environment variables. This guide provides clear steps to successfully deploy your Vite app.

Step 1: Setting the Base Path in Vite Configuration

In Vite, the base path must be set to match the subdirectory where GitHub Pages serves the site. This ensures correct paths to assets like JavaScript and CSS. In the `vite.config.ts`, configure the base property:

export default defineConfig({
  base: process.env.VITE_BASE_URL || "/",
  build: {
  outDir: "docs",
  },
});
Step 2: Using Environment Variables with dotenv

Use environment variables for path configuration. Create `.env` and `.env.production` files to manage paths in development and production environments. The production file should reflect your repository name.

VITE_BASE_URL=/
VITE_BASE_URL=/repository-name/
Step 3: Loading Environment Variables in Vite Config

Vite automatically loads environment variables with the `VITE_` prefix. For custom environment handling, import the `dotenv` package and manually load environment files based on the environment mode.

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import * as dotenv from "dotenv";

const mode = process.env.NODE_ENV || "development";
dotenv.config({ path: `.env.${mode}` });

export default defineConfig({
  base: process.env.VITE_BASE_URL || "/",
  build: {
  outDir: "docs",
  },
  plugins: [react()],
});
Step 4: Building the Project for GitHub Pages

After configuring the project, run the build command to generate the production-ready files in the `docs/` folder, which is used by GitHub Pages.

npm run build
Step 5: Deploying to GitHub Pages

Once the build is complete, commit and push the `docs/` folder to your repository. In the GitHub Pages settings, set the source to the `docs/` folder to make your site live.

git add .
git commit -m "Deploy Vite project to GitHub Pages"
git push origin main

By following these steps, you can deploy a Vite project to GitHub Pages with proper handling of environment variables and paths. The critical step is setting the correct base path and using dotenv to manage environment settings across development and production environments.