Building an Interactive Blog with React Routing: Unlocking Dynamic Navigation and Seamless User Experience

Welcome to our blog post, where we dive into the world of React Routing and its application in creating a simple yet powerful blog site. In today's digital landscape, websites are no longer static pages; they are dynamic, interactive experiences that engage users and provide seamless navigation. With React Routing, we can harness the power of React.js to build a robust blog site with smooth transitions, dynamic content loading, and efficient URL management.

Gone are the days of manually reloading pages or struggling with clunky navigation menus. React Routing empowers developers to create single-page applications that offer a fluid user experience, mimicking the responsiveness of native applications. By leveraging the power of routing, we can build a blog site that seamlessly transitions between different sections, such as homepage, blog posts, about, and more, without the need for full page reloads.


In this blog post, we will take you on a step-by-step journey of building a simple blog site using React Routing. We'll explore the fundamental concepts of routing, set up our project environment, and delve into the implementation details of creating different routes for various sections of our blog.

You don't need to be a React expert to follow along; we'll explain the concepts and provide code examples to guide you through the process. By the end of this tutorial, you'll have a solid understanding of how React Routing works and how to leverage its features to create an engaging and user-friendly blog site.

So, if you're ready to enhance your web development skills and take your blog site to the next level, let's jump right in and explore the wonders of React Routing!

Tutorial

In this tutorial we will understand how react routing works by building a simple multi-page blog website.

Initial Setup & Installation

First, begin by creating a new react app using the following command in your terminal:

npx create-react-app blog-site-react --save

This will give us a new folder with a boilerplate code for a sample react project. Change the folder directory to the newly created app using the command cd blog-site-react and type code . to open the project in a new VS code window.

Since we are not concerned with testing at the moment and we will introduce our own styling, just remove all the unnecessary files from the src folder and keep the following files in it: App.js, index.css, and index.js.

Setting Up Routes

To begin with routing, we will install the routing package from the node package manager using the following command:

npm install react-router-dom --save

Note that it will install the latest version of React Router viz. v6 at the time this was written. We can also specify the version of the router in the above command like this: npm install react-router-dom@6 --save or any other version we would like to work with.

Next, we will make changes to our index.js file in order to make the routing work in the following way:

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { BrowserRouter as Router, Route } from "react-router-dom";
import { Routes } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Router>
      <Routes>
        <Route path="/*" element={<App />} />
      </Routes>
    </Router>
  </React.StrictMode>
);

Here's an explanation of the above code:

  1. Importing Dependencies:
    • React, ReactDOM, ./index.css, App, BrowserRouter, Route, Routes: These imports serve the same purposes as in the previous code snippets.
  2. Creating a React Root:
    • const root = ReactDOM.createRoot(document.getElementById("root"));: This line creates a React root using the createRoot method provided by ReactDOM. It takes an HTML element with the id "root" as an argument and returns a root object.
  3. Rendering the Application:
    • root.render(...): This line renders the application inside the React root.
    • <React.StrictMode>: This wrapper component activates additional checks and warnings for potential issues in the application. It is used during development but has no effect in a production build.
    • <Router>: This component sets up the routing functionality provided by react-router-dom. All the components inside this wrapper will have access to routing capabilities.
    • <Routes>: This component is used as a wrapper for defining multiple routes within the application. It is an alternative syntax to using multiple <Route> components.
    • <Route path="/*" element={<App />} />: This line defines a route with a path of "/*" (wildcard path that matches any URL) and associates it with the App component. The element prop is used to specify the component to render when the route matches. In this case, it renders the App component.
    • </Routes>: Closes the routes wrapper component.
    • </Router>: Closes the router component.
    • </React.StrictMode>: Closes the strict mode wrapper.

In summary, this code sets up a React application with routing using the react-router-dom library. It renders the App component for any URL within the application and wraps the application in a strict mode for development purposes. The use of the Routes component provides an alternative syntax for defining routes.

Create Multiple Pages

Now we will begin by creating the multiple page components for our website. First we will simply create new files in the src folder by the names of Header.js, Home.js, Footer.js, About.js, NewPost.js, PostPage.js, Missing.js, and Nav.js files and then create a boilerplate code in all of them. We can use React snippets extension and type rafce or can type manually the following code for all of them:

const Home = () => {
  return (
    <main>
      <h1>Home</h1>
    </main>
  );
};

export default Home;

Simply change the function name to the specific file name and change the semantic HTML tags as per the file for instance in Header.js, instead of the main element, we will have <header></header> and so on.

Once these files are created, import them in App.js and place them in the JSX as HTML tags with the routes to individual pages.

But first, we will create a Layout.js file which will hold the common components that need to appear on all pages in the following way:

import Header from "./Header";
import Footer from "./Footer";
import Nav from "./Nav";
import { Outlet } from "react-router-dom";

const Layout = () => {
  return (
    <div className="App">
      <Header />
      <Nav />
      <Outlet />
      <Footer />
    </div>
  );
};

export default Layout;

The above code exports a React component named Layout that represents a common layout structure for a web application. Here's an explanation of the code:

  1. Importing Dependencies:
    • Header, Footer, Nav: These import statements are used to import other components from separate files. It suggests that there are separate files named Header.js, Footer.js, and Nav.js that define those components.
    • Outlet: This import statement imports the Outlet component from the react-router-dom library. The Outlet component is used to render the nested child routes within the parent component.
  2. Defining the Layout Component:
    • const Layout = () => { ... }: This defines a functional component named Layout using arrow function syntax.
    • Inside the component's body, it returns JSX code that represents the layout structure of the application.
    • <div className="App">: This is a div element with the CSS class name "App". It serves as the root container for the layout.
    • <Header />: This renders the Header component. It assumes that the Header component is defined in a separate file and exports a React component.
    • <Nav />: This renders the Nav component. It assumes that the Nav component is defined in a separate file and exports a React component.
    • <Outlet />: This renders the child routes within the parent component. It indicates that this component serves as a placeholder for rendering child components defined by the routing system.
    • <Footer />: This renders the Footer component. It assumes that the Footer component is defined in a separate file and exports a React component.
    • </div>: Closes the root div element.
  3. Exporting the Layout Component:
    • export default Layout;: This exports the Layout component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, the Layout component represents a common layout structure for a web application. It includes a header, navigation, a placeholder for rendering child components defined by the routing system, and a footer. Other components and routes can be nested inside the Layout component to form the complete application layout.

Create Route Logic

Next we will write the routing logic for the different pages in App.js file in the following way:

import About from "./About";
import Home from "./Home";
import NewPost from "./NewPost";
import PostPage from "./PostPage";
import Missing from "./Missing";
import { useState, useEffect } from "react";
import { Route, Routes, useNavigate } from "react-router-dom";
import Layout from "./Layout";

function App() {
  return (
    <Routes>
      <Route element={<Layout />} path="/">
        <Route index element={<Home />} />

        <Route path="post">
          <Route index element={<NewPost />} />

          <Route path=":id" element={<PostPage />} />
        </Route>

        <Route path="about" element={<About />} />

        <Route path="*" element={<Missing />} />
      </Route>
    </Routes>
  );
}

export default App;

The above code sets up the routing configuration for a React application using the react-router-dom library. It defines different routes and associates them with specific components. Here's an explanation of the code:

  1. Importing Dependencies:
    • About, Home, NewPost, PostPage, Missing: These import statements are used to import components from separate files. It suggests that there are separate files named About.js, Home.js, NewPost.js, PostPage.js, and Missing.js that define those components.
    • useState, useEffect: These import statements import the useState and useEffect hooks from the React library.
    • Route, Routes, useNavigate: These import statements import components and hooks from the react-router-dom library.
  2. Defining the App Component:
    • function App() { ... }: This defines a functional component named App.
    • Inside the component's body, it returns JSX code that represents the routing configuration for the application.
  3. Routing Configuration:
    • <Routes>: This is the top-level component that wraps all the route definitions.
    • <Route element={<Layout />} path="/">: This defines a route with a path of "/" (the root path) and associates it with the Layout component. The element prop is used to specify the component to render when the route matches. In this case, it renders the Layout component.
    • Inside the root route, there are several nested <Route> components that define child routes and their associated components.
    • <Route index element={<Home />} />: This defines a route with a path of "/" (the root path) and associates it with the Home component. It will be rendered when the user navigates to the root URL.
    • <Route path="post">: This defines a route with a path of "/post" and creates a nested route configuration for posts.
    • <Route index element={<NewPost />} />: This defines a sub-route with a path of "/post" and associates it with the NewPost component. It will be rendered when the user navigates to the "/post" URL.
    • <Route path=":id" element={<PostPage />} />: This defines a dynamic sub-route with a path parameter :id. It associates the PostPage component with the route and will be rendered when the user navigates to a URL that matches the pattern "/post/:id".
    • <Route path="about" element={<About />} />: This defines a route with a path of "/about" and associates it with the About component. It will be rendered when the user navigates to the "/about" URL.
    • <Route path="*" element={<Missing />} />: This defines a route with a path of "*" (wildcard path) and associates it with the Missing component. It will be rendered when none of the previous routes match the URL.
  4. Exporting the App Component:
    • export default App;: This exports the App component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code sets up the routing configuration for a React application. It defines different routes and associates them with specific components. The Layout component is used as the top-level layout structure, and various components such as Home, NewPost, PostPage, About, and Missing are associated with different routes. The react-router-dom library is used to handle the routing functionality.

Implement CSS Styling

Now we will use some standard CSS for styling which can be modified as per needs later on in index.css file in the following way:

@import url("<https://fonts.googleapis.com/css2?family=Open+Sans&display=swap>");

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  font-size: 16px;
}

body {
  min-height: 100vh;
  font-family: "Open Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  display: flex;
  background-color: #efefef;
}

#root {
  flex-grow: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}

.App {
  width: 100%;
  max-width: 800px;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  border: 1px solid #333;
  box-shadow: 0px 0px 15px gray;
}

.Header,
.Footer {
  width: 100%;
  background-color: #66d8f5;
  padding: 1rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.Header h1 {
  font-size: 1.5rem;
}

.Header svg {
  font-size: 2rem;
}

.Footer {
  padding: 0.75rem;
  display: grid;
  place-content: center;
}

.Nav {
  width: 100%;
  background-color: #333;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;
}

.searchForm {
  width: 80%;
  padding: 1rem 0 0 0.75rem;
}

.searchForm input[type="text"] {
  font-family: "Open Sans", sans-serif;
  width: 100%;
  min-height: 48px;
  font-size: 1rem;
  padding: 0.25rem;
  border-radius: 0.25rem;
  outline: none;
}

.searchForm label {
  position: absolute;
  left: -99999px;
}

.Nav ul {
  color: #fff;
  list-style-type: none;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
}

.Nav li {
  padding: 1rem;
}

.Nav li:hover,
.Nav li:focus {
  padding: 1rem;
}

.Nav li a {
  color: #fff;
  text-decoration: none;
}

.Nav li:hover,
.Nav li:focus,
.Nav li:hover a,
.Nav li:focus a {
  background-color: #eee;
  color: #333;
}

.Nav li:hover a,
.Nav li:focus a {
  cursor: pointer;
}

.Home,
.NewPost,
.PostPage,
.About,
.Missing {
  width: 100%;
  flex-grow: 1;
  padding: 1rem;
  overflow-y: auto;
  background-color: #fff;
}

.post {
  margin-top: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid lightgray;
}

.Home .post a {
  text-decoration: none;
}

.Home .post a,
.Home .post a:visited {
  color: #000;
}

.post:first-child {
  margin-top: 0;
}

.post:last-child {
  border-bottom: none;
}

.postDate {
  font-size: 0.75rem;
  margin-top: 0.25rem;
}

.postBody {
  margin: 1rem 0;
}

.newPostForm {
  display: flex;
  flex-direction: column;
}

.newPostForm label {
  margin-top: 1rem;
}

.newPostForm input[type="text"],
.newPostForm textarea {
  font-family: "Open Sans", sans-serif;
  width: 100%;
  min-height: 48px;
  font-size: 1rem;
  padding: 0.25rem;
  border-radius: 0.25rem;
  margin-right: 0.25rem;
  outline: none;
}

.newPostForm textarea {
  height: 100px;
}

.newPostForm button {
  margin-top: 1rem;
  height: 48px;
  min-width: 48px;
  border-radius: 10px;
  padding: 0.5rem;
  font-size: 1rem;
  cursor: pointer;
}

.Missing h2,
.PostPage h2,
.Missing p,
.PostPage p {
  margin-bottom: 1rem;
}

.PostPage button {
  height: 48px;
  min-width: 48px;
  border-radius: 0.25rem;
  padding: 0.5rem;
  font-size: 1rem;
  background-color: red;
  color: #fff;
  cursor: pointer;
}

.statusMsg {
  margin-top: 2rem;
}

@media only screen and (min-width: 610px) {
  html {
    font-size: 22px;
  }

  .Header h1 {
    font-size: 2rem;
  }

  .Nav {
    flex-direction: row;
    flex-wrap: nowrap;
    justify-content: space-between;
    align-items: center;
  }

  .Nav ul {
    text-align: right;
  }

  .Nav li:hover,
  .Nav li:focus,
  .Nav li:hover a,
  .Nav li:focus a {
    background-color: #eee;
    color: #333;
  }

  .searchForm {
    width: 50%;
    padding: 0.5rem 0;
  }

  .searchForm input[type="text"] {
    margin-left: 0.5rem;
  }

  .newPostForm textarea {
    height: 300px;
  }
}

@media only screen and (min-width: 992px) {
  .Header svg {
    font-size: 3rem;
  }
}

The above CSS shall be used for styling the entire application and will undergo changes as per requirements.

Create Header Component

Next, we will pass the title property and write the component logic for Header.js in the following way:

const Header = ({ title }) => {
  return (
    <header className="Header">
      <h1>{title}</h1>
    </header>
  );
};

export default Header;

The above code defines a React functional component named Header that represents a header section in a web application. Here's an explanation of the code:

  1. Function Signature:
    • const Header = ({ title }) => { ... }: This defines a functional component named Header using arrow function syntax. It accepts a single argument, which is an object destructuring assignment of the title property.
  2. Rendering the Header:
    • The component's body returns JSX code that represents the structure and content of the header section.
    • <header className="Header">: This is an HTML <header> element with the CSS class name "Header". It serves as the container for the header content.
    • <h1>{title}</h1>: This is an HTML <h1> element that displays the title prop value passed to the component. It renders the value dynamically within the header.
  3. Exporting the Header Component:
    • export default Header;: This exports the Header component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code defines a React functional component named Header that represents a header section in a web application. It accepts a title prop and renders it within an <h1> element inside a <header> element. Other components can import and use this Header component to display a consistent header section with a dynamic title.

Nav Component

After this we will build the Nav component with multiple router links where we will have a search bar and links to different pages in Nav.js file in the following way:

import { Link } from "react-router-dom";
const Nav = ({ search, setSearch }) => {
  return (
    <nav className="Nav">
      <form className="searchForm" onSubmit={(e) => e.preventDefault()}>
        <label htmlFor="search">Search Posts</label>
        <input
          type="text"
          id="search"
          placeholder="Search Posts"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
      </form>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/post">New Post</Link>
        </li>
        <li>
          <Link to="/about">About</Link>
        </li>
      </ul>
    </nav>
  );
};

export default Nav;

The above code defines a React functional component named Nav that represents a navigation bar in a web application. It uses the Link component from the react-router-dom library to provide navigation links. Here's an explanation of the code:

  1. Importing Dependencies:
    • Link: This import statement imports the Link component from the react-router-dom library. The Link component is used to create links for navigation within the application.
  2. Function Signature:
    • const Nav = ({ search, setSearch }) => { ... }: This defines a functional component named Nav using arrow function syntax. It accepts two props: search and setSearch. These props are used to manage search functionality in the navigation component.
  3. Rendering the Navigation:
    • The component's body returns JSX code that represents the structure and content of the navigation bar.
    • <nav className="Nav">: This is an HTML <nav> element with the CSS class name "Nav". It serves as the container for the navigation content.
    • <form className="searchForm" onSubmit={(e) => e.preventDefault()}>: This is an HTML <form> element with the CSS class name "searchForm". It handles form submission and prevents the default form behavior to avoid page refresh.
    • <label htmlFor="search">Search Posts</label>: This is an HTML <label> element that provides a label for the search input.
    • <input type="text" id="search" placeholder="Search Posts" value={search} onChange={(e) => setSearch(e.target.value)} />: This is an HTML <input> element of type "text". It represents the search input field and is associated with the search state value and setSearch function through the value and onChange attributes, respectively.
    • <ul>: This is an HTML <ul> element that represents an unordered list of navigation items.
    • <li><Link to="/">Home</Link></li>: This is an HTML <li> element that represents a navigation item. The <Link> component is used to create a link to the root URL ("/") and display the text "Home".
    • <li><Link to="/post">New Post</Link></li>: This is an HTML <li> element that represents another navigation item. The <Link> component is used to create a link to the "/post" URL and display the text "New Post".
    • <li><Link to="/about">About</Link></li>: This is an HTML <li> element that represents yet another navigation item. The <Link> component is used to create a link to the "/about" URL and display the text "About".
  4. Exporting the Nav Component:
    • export default Nav;: This exports the Nav component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code defines a React functional component named Nav that represents a navigation bar in a web application. It includes a search input field, a list of navigation items with links created using the Link component, and handles search functionality through the search and setSearch props. Other components can import and use this Nav component to display a navigation bar with search functionality and links for navigation within the application.

Next we will pass the necessary search props to the Nav component in Layout.js file in the following way:

const Layout = ({ search, setSearch }) => {
  return (
    <div className="App">
      <Header title="React JS Blog" />
      <Nav search={search} setSearch={setSearch} />
      <Outlet />
      <Footer />
    </div>
  );
};

Next, we will add the search state in the App.js file:

const [search, setSearch] = useState([]);

Create Static Posts Data

After this, we will create a posts state and use some hardcoded post data to display on the Home page in the App.js file in the following way:

const [posts, setPosts] = useState([
    {
      id: 1,
      title: "React Router",
      body: "React Router is a collection of navigational components that compose declaratively with your application.",
      datetime: "July 26, 2021 11:17:00 AM",
    },
    {
      id: 2,
      title: "React.js",
      body: "React is a JavaScript library for building user interfaces. It is maintained by Facebook and a community of individual developers and companies.",
      datetime: "July 26, 2021 12:17:00 AM",
    },
    {
      id: 3,
      title: "React Hooks",
      body: "Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.",
      datetime: "July 26, 2021 10:17:00 AM",
    },
    {
      id: 4,
      title: "React Context",
      body: "Context provides a way to pass data through the component tree without having to pass props down manually at every level.",
      datetime: "July 26, 2021 09:17:00 AM",
    },
  ]);

Also, we need to pass the posts state as props to the Home component route like this:

<Route index element={<Home posts={posts} />} />

Create Home Component

Next we will write the logic for the Home page component in Home.js file where we will import a Feed component that will display a feed of blog posts in the following way:

import Feed from "./Feed";

const Home = ({ posts }) => {
  return (
    <main className="Home">
      {posts.length ? (
        <Feed posts={posts} />
      ) : (
        <p style={{ marginTop: "2rem" }}> No posts to display yet! </p>
      )}
    </main>
  );
};

export default Home;

The above code defines a React functional component named Home that represents the main content area of a home page in a web application. Here's an explanation of the code:

  1. Importing Dependencies:
    • Feed: This import statement is used to import the Feed component from a separate file. It suggests that there is a file named Feed.js that defines the Feed component.
  2. Function Signature:
    • const Home = ({ posts }) => { ... }: This defines a functional component named Home using arrow function syntax. It accepts a single prop named posts.
  3. Rendering the Home Content:
    • The component's body returns JSX code that represents the structure and content of the home page.
    • <main className="Home">: This is an HTML <main> element with the CSS class name "Home". It serves as the container for the main content of the home page.
    • {posts.length ? ... : ...}: This is a conditional rendering statement that checks if the posts array has any elements. If it does, the first part of the ternary operator (<Feed posts={posts} />) is rendered. Otherwise, the second part of the ternary operator (<p style={{ marginTop: "2rem" }}> No posts to display yet! </p>) is rendered.
    • <Feed posts={posts} />: This renders the Feed component and passes the posts array as a prop to it. It suggests that the Feed component is responsible for rendering the list of posts based on the provided data.
    • <p style={{ marginTop: "2rem" }}> No posts to display yet! </p>: This is a paragraph element that is rendered when there are no posts to display. It displays the message "No posts to display yet!" and applies some inline CSS to add a margin-top of "2rem" for spacing.
  4. Exporting the Home Component:
    • export default Home;: This exports the Home component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code defines a React functional component named Home that represents the main content area of a home page in a web application. It receives a posts prop, and based on the length of the posts array, it either renders the Feed component to display the posts or a message indicating that there are no posts to display yet. Other components can import and use this Home component to render the main content of the home page.

Create Feed Component

Next we will write the logic to display the posts feed by creating a new file Feed.js file and passing the posts as props in the following way:

import Post from "./Post";

const Feed = ({ posts }) => {
  return (
    <>
      {posts.map((post) => (
        <Post key={post.id} post={post} />
      ))}
    </>
  );
};

export default Feed;

The above code defines a React functional component named Feed that represents a feed or list of posts in a web application. Here's an explanation of the code:

  1. Importing Dependencies:
    • Post: This import statement is used to import the Post component from a separate file. It suggests that there is a file named Post.js that defines the Post component.
  2. Function Signature:
    • const Feed = ({ posts }) => { ... }: This defines a functional component named Feed using arrow function syntax. It accepts a single prop named posts, which represents an array of post data.
  3. Rendering the Feed:
    • The component's body returns JSX code that represents the structure and content of the feed.
    • <>...</>: This is a React fragment, denoted by empty angle brackets (<>...</>). It allows returning multiple elements without the need for an additional wrapping element.
    • {posts.map((post) => ( ... ))}: This uses the map method to iterate over each post object in the posts array and returns a new array of JSX elements.
    • <Post key={post.id} post={post} />: This renders the Post component for each post in the posts array. The key prop is set to the id property of the post object to provide a unique identifier for React's reconciliation process. The post object is passed as a prop to the Post component.
  4. Exporting the Feed Component:
    • export default Feed;: This exports the Feed component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code defines a React functional component named Feed that represents a feed or list of posts in a web application. It receives a posts prop, which is an array of post data. The component iterates over the posts array using the map method and renders the Post component for each post, passing the post data as a prop. Other components can import and use this Feed component to render a list of posts in the application's feed or similar sections.



Create Post Component

Next, we will create a Post.js file and write the logic for an individual post with the post props passed in the following way:

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

const Post = ({ post }) => {
  return (
    <article className="post">
      <Link to={`/post/${post.id}`}>
        <h2>{post.title}</h2>
        <p className="postDate">{post.datetime}</p>
      </Link>
      <p className="postBody">
        {post.body.length <= 25 ? post.body : `${post.body.slice(0, 25)}...`}
      </p>
    </article>
  );
};

export default Post;

The above code defines a React functional component named Post that represents a single post within a web application. Here's an explanation of the code:

  1. Importing Dependencies:
    • Link: This import statement is used to import the Link component from the react-router-dom library. The Link component is used to create links for navigation within the application.
  2. Function Signature:
    • const Post = ({ post }) => { ... }: This defines a functional component named Post using arrow function syntax. It accepts a single prop named post, which represents the data of a single post.
  3. Rendering the Post:
    • The component's body returns JSX code that represents the structure and content of a single post.
    • <article className="post">: This is an HTML <article> element with the CSS class name "post". It serves as the container for the post content.
    • <Link to={/post/${[post.id](<http://post.id/>)}}>: This uses the Link component to create a link to a specific post using the post's id property. The URL is generated dynamically using template literals.
    • <h2>{post.title}</h2>: This is an HTML heading level 2 element (<h2>) that displays the title of the post. The title is obtained from the post prop.
    • <p className="postDate">{post.datetime}</p>: This is an HTML paragraph element (<p>) with the CSS class name "postDate". It displays the datetime of the post. The datetime is obtained from the post prop.
    • <p className="postBody">...</p>: This is an HTML paragraph element (<p>) with the CSS class name "postBody". It displays the body content of the post. If the length of the body content is less than or equal to 25 characters, the entire body is displayed. Otherwise, only the first 25 characters followed by an ellipsis ("...") are displayed. This is achieved using a conditional expression (post.body.length <= 25 ? post.body : ${post.body.slice(0, 25)}...``)`
  4. Exporting the Post Component:
    • export default Post;: This exports the Post component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code defines a React functional component named Post that represents a single post within a web application. It receives a post prop, which contains the data for a post. The component renders the post's title, datetime, and a truncated version of the post's body. It also wraps the content in a Link component, allowing the post to be clickable and navigate to a specific post page. Other components can import and use this Post component to render individual posts in the application.

Once this is done, our Home page will display a list of blog posts.

Create PostPage Component

Next we will write the logic to display individual post content on the PostPage.js file in the following way:

import { useParams, Link } from "react-router-dom";

const PostPage = ({ posts, handleDelete }) => {
  const { id } = useParams();
  const post = posts.find((post) => post.id.toString() === id);
  return (
    <main className="PostPage">
      <article className="post">
        {post && (
          <>
            <h2>{post.title}</h2>
            <p className="postDate">{post.datetime}</p>
            <p className="postBody">{post.body}</p>
            <button onClick={() => handleDelete(post.id)}>Delete Post</button>
          </>
        )}
        {!post && (
          <>
            <h2>Post Not Found</h2>
            <p>Well, that's disappointing</p>
            <p>
              <Link to="/">Visit Our Home Page</Link>
            </p>
          </>
        )}
      </article>
    </main>
  );
};

export default PostPage;

The above code defines a React functional component named PostPage that represents a single post page within a web application. Here's an explanation of the code:

  1. Importing Dependencies:
    • useParams: This import statement is used to import the useParams hook from the react-router-dom library. The useParams hook allows accessing the parameters of the current route.
  2. Function Signature:
    • const PostPage = ({ posts, handleDelete }) => { ... }: This defines a functional component named PostPage using arrow function syntax. It accepts two props: posts and handleDelete. The posts prop represents an array of post data, and handleDelete is a function to handle the deletion of a post.
  3. Accessing Post Data:
    • The component uses the useParams hook to retrieve the id parameter from the current route. The id parameter represents the unique identifier of the post being displayed. This is achieved with the line const { id } = useParams();.
    • The component then searches the posts array for a post whose id matches the id parameter. It uses the find method with a callback function to perform the search: const post = posts.find((post) => post.id.toString() === id);.
  4. Rendering the Post Page:
    • The component's body returns JSX code that represents the structure and content of the post page.
    • <main className="PostPage">: This is an HTML <main> element with the CSS class name "PostPage". It serves as the container for the main content of the post page.
    • <article className="post">: This is an HTML <article> element with the CSS class name "post". It serves as the container for the post content.
    • {post && ...}: This is a conditional rendering statement that checks if a post object is found based on the provided id. If a post is found, the content inside the curly braces is rendered. Otherwise, the content inside the {!post && ...} block is rendered.
    • <h2>{post.title}</h2>, <p className="postDate">{post.datetime}</p>, <p className="postBody">{post.body}</p>: These JSX elements display the title, datetime, and body of the post, respectively. The values are obtained from the post object.
    • <button onClick={() => handleDelete()}>Delete Post</button>: This is a button element that triggers the handleDelete function when clicked. It allows deleting the post.
    • {!post && ...}: This is the conditional rendering block that is executed when no post is found based on the provided id.
    • <h2>Post Not Found</h2>, <p>Well, that's disappointing</p>, <p>...</p>: These JSX elements display a message indicating that the post was not found. It provides an option to visit the home page using a Link component.
    • <Link to="/">Visit Our Home Page</Link>: This is a Link component provided by react-router-dom that creates a link to the home page of the application.
  5. Exporting the PostPage Component:
    • export default PostPage;: This exports the PostPage component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code defines a React functional component named PostPage that represents a single post page within a web application.



The above component will only function when we will pass the necessary props from the App.js component in the following way:

<Route
            path=":id"
            element={<PostPage posts={posts} handleDelete={handleDelete} />}
          />
        </Route>

Next, we will write the logic for the handleDelete function in the App.js file in the following way:

const navigate = useNavigate();

  const handleDelete = (id) => {
    const postsList = posts.filter((post) => post.id !== id);
    setPosts(postsList);
    navigate("/");
  };

The above code snippet shows a React component that includes a handleDelete function and uses the useNavigate hook from the react-router-dom library. Here's an explanation of the code:

  1. const navigate = useNavigate();: This line declares a constant variable navigate and assigns it the value returned by the useNavigate hook. The useNavigate hook is a utility provided by react-router-dom that returns a navigation function. This function can be used to programmatically navigate to different routes within the application.
  2. const handleDelete = (id) => { ... }: This declares an arrow function named handleDelete that takes an id parameter. This function is responsible for handling the deletion of a post.
  3. const postsList = posts.filter((post) => post.id !== id);: This line filters the posts array to create a new array called postsList that excludes the post with the specified id. The filter method is used, and it checks if each post's id is not equal to the provided id parameter.
  4. setPosts(postsList);: This line suggests that there is a state variable named posts (presumably declared using the useState hook) and a corresponding setPosts function to update that state. It sets the value of posts to the filtered postsList, effectively removing the post with the specified id from the list.
  5. navigate("/");: This line uses the navigate function from the useNavigate hook to navigate to the root or home page of the application. It redirects the user to the specified route.

In summary, the code defines a handleDelete function that filters the posts array to remove the post with the given id, updates the posts state with the filtered array, and then navigates to the root or home page of the application.

Next, we will pass some states as props for the New Post page in App.js page in the following way:

const [postTitle, setPostTitle] = useState("");
const [postBody, setPostBody] = useState("");
const handleSubmit = () => {};
<Route
            index
            element={
              <NewPost
                handleSubmit={handleSubmit}
                postTitle={postTitle}
                postBody={postBody}
                setPostTitle={setPostTitle}
                setPostBody={setPostBody}
              />
            }
          />

Create NewPost Component

Now we will create the NewPost.js code for displaying a form where users can create a new blog post in the following way:

const NewPost = ({
  handleSubmit,
  postTitle,
  setPostTitle,
  postBody,
  setPostBody,
}) => {
  return (
    <main className="NewPost">
      <h2>New Post</h2>
      <form className="newPostForm" onSubmit={handleSubmit}>
        <label htmlFor="postTitle">Title: </label>
        <input
          type="text"
          required
          value={postTitle}
          onChange={(e) => setPostTitle(e.target.value)}
          id="postTitle"
        />

        <label htmlFor="postBody">Post: </label>
        <textarea
          type="text"
          id="postBody"
          required
          value={postBody}
          onChange={(e) => setPostBody(e.target.value)}
        />
        <button type="submit">Submit</button>
      </form>
    </main>
  );
};

export default NewPost;

The above code defines a React functional component named NewPost that represents a form for creating a new post. Here's an explanation of the code:

  1. Function Signature:
    • const NewPost = ({ handleSubmit, postTitle, setPostTitle, postBody, setPostBody }) => { ... }: This defines a functional component named NewPost using arrow function syntax. It accepts several props: handleSubmit, postTitle, setPostTitle, postBody, and setPostBody. These props are used to handle form submission and manage the state of the post title and body.
  2. Rendering the New Post Form:
    • The component's body returns JSX code that represents the structure and content of the new post form.
    • <main className="NewPost">: This is an HTML <main> element with the CSS class name "NewPost". It serves as the container for the main content of the new post form.
    • <h2>New Post</h2>: This is an HTML heading element that displays the text "New Post" as the title of the form.
    • <form className="newPostForm" onSubmit={handleSubmit}>: This is an HTML <form> element with the CSS class name "newPostForm". It represents the form for creating a new post and has an onSubmit event handler that calls the handleSubmit function when the form is submitted.
    • <label htmlFor="postTitle">Title: </label>: This is an HTML <label> element associated with the post title input field. It displays the text "Title:" as the label for the input.
    • <input type="text" required value={postTitle} onChange={(e) => setPostTitle(e.target.value)} id="postTitle" />: This is an HTML <input> element of type "text" that represents the input field for the post title. It is a controlled component, meaning its value is controlled by the postTitle prop and updated through the setPostTitle function when the user types in the input field.
    • <label htmlFor="postBody">Post: </label>: This is an HTML <label> element associated with the post body textarea. It displays the text "Post:" as the label for the textarea.
    • <textarea type="text" id="postBody" required value={postBody} onChange={(e) => setPostBody(e.target.value)} />: This is an HTML <textarea> element that represents the input field for the post body. Similar to the post title input, it is a controlled component with its value controlled by the postBody prop and updated through the setPostBody function.
    • <button type="submit">Submit</button>: This is an HTML <button> element of type "submit" that represents the submit button for the form. When clicked, it triggers the form submission and calls the handleSubmit function provided as a prop.
  3. Exporting the NewPost Component:
    • export default NewPost;: This exports the NewPost component as the default export of the current module. It means that other parts of the application can import and use this component.

In summary, this code defines a React functional component NewPost that represents a form for creating a new post. It includes input fields for the post title and body, and a submit button to trigger the form submission. The values of the input fields are controlled by props (postTitle and postBody) and can be updated through corresponding setter functions (setPostTitle and setPostBody). When the form is submitted, the provided handleSubmit function is called to handle the form submission

logic.



Create Submit Function

Next we will write the logic for the handleSubmit function and install the date package using the command, npm install date-fns --save in the App.js file in the following way:

import { format } from "date-fns";
const handleSubmit = (e) => {
    e.preventDefault();
    const id = posts.length ? posts[posts.length - 1] + 1 : 1;
    //npm install date-fns --save
    const datetime = format(new Date(), "MMMM dd, yyyy pp");

    const newPost = { id, title: postTitle, datetime, body: postBody };
    const allPosts = [...posts, newPost];
    setPosts(allPosts);
    setPostTitle("");
    setPostBody("");
    navigate("/");
  };

The above code snippet defines a handleSubmit function used for handling the form submission in a post creation scenario. Here's an explanation of the code:

  1. Importing the format function from the "date-fns" library:
    • import { format } from "date-fns";: This line imports the format function from the "date-fns" library. The format function is a utility function that helps format dates according to specific patterns.
  2. The handleSubmit function:
    • const handleSubmit = (e) => { ... }: This line declares an arrow function named handleSubmit that takes an event object e as its parameter. The function is triggered when the form is submitted.
  3. Preventing default form submission behavior:
    • e.preventDefault();: This line calls the preventDefault() method on the event object e. It prevents the default behavior of form submission, which includes a page refresh.
  4. Generating a unique ID for the new post:
    • const id = posts.length ? posts[posts.length - 1] + 1 : 1;: This line checks if there are existing posts (posts.length > 0). If there are, it assigns a new ID by incrementing the ID of the last post in the posts array (posts[posts.length - 1] + 1). Otherwise, if there are no existing posts, it assigns the ID as 1.
  5. Formatting the current date and time:
    • const datetime = format(new Date(), "MMMM dd, yyyy pp");: This line uses the format function from the "date-fns" library to format the current date and time. It creates a new Date object with new Date() and formats it as a string using the pattern "MMMM dd, yyyy pp". The pattern represents the month (MMMM), day (dd), year (yyyy), and time (pp in a localized format).
  6. Creating a new post object:
    • const newPost = { id, title: postTitle, datetime, body: postBody };: This line creates a new object named newPost with properties id, title, datetime, and body. The values of these properties are derived from the form input fields: id from the generated ID, title from the postTitle state, datetime from the formatted date and time, and body from the postBody state.
  7. Updating the state with the new post:
    • const allPosts = [...posts, newPost];: This line creates a new array named allPosts by spreading the existing posts array and adding the newPost object to it.
  8. Updating the state and clearing the form:
    • setPosts(allPosts);: This line calls the setPosts function (presumably from the state hook) to update the state variable posts with the allPosts array, which includes the new post.
    • setPostTitle("");: This line calls the setPostTitle function to clear the postTitle state, setting it to an empty string.
    • setPostBody("");: This line calls the setPostBody function to clear the postBody state, setting it to an empty string.
  9. Navigating to the home page:
    • navigate("/");: This line likely uses the navigate function (presumably from the useNavigate hook) to navigate to the home page ("/") after the form submission. It redirects the user to the specified route.

In summary, this code defines a handleSubmit

function that handles the form submission for creating a new post. It generates a unique ID, formats the current date and time, creates a new post object, updates the state with the new post, clears the form input fields, and navigates to the home page.

Create Search Feature

Next, we will build the search functionality within the App.js file in the following way:

const [search, setSearch] = useState([]);
const [searchResults, setSearchResults] = useState([]);

useEffect(() => {
    const filteredResults = posts.filter(
      (post) =>
        post.body.toLowerCase().includes(search) ||
        post.title.toLowerCase().includes(search)
    );

    setSearchResults(filteredResults.reverse());
  }, [posts, search]);

The above code snippet demonstrates the usage of the useState and useEffect hooks in a React component. Here's an explanation of the code:

  1. State variables:
    • const [search, setSearch] = useState([]);: This line initializes a state variable search using the useState hook. The initial value of search is an empty array ([]). It represents the search query entered by the user.
    • const [searchResults, setSearchResults] = useState([]);: This line initializes another state variable searchResults using the useState hook. The initial value of searchResults is an empty array ([]). It represents the filtered search results based on the search query.
  2. The useEffect hook:
    • useEffect(() => { ... }, [posts, search]);: This hook is used to perform side effects in the component. It takes a callback function as its first argument and a dependency array as its second argument. The callback function will be executed when any of the dependencies in the array (posts and search) change.
  3. Filtering the search results:
    • const filteredResults = posts.filter(...);: This line filters the posts array based on the search query entered by the user. It uses the filter method to iterate over each post object in the posts array and checks if the lowercase version of the post's body or title includes the lowercase version of the search query.
  4. Updating the search results state:
    • setSearchResults(filteredResults.reverse());: This line updates the searchResults state variable by setting it to the filteredResults array. Additionally, the reverse method is called to reverse the order of the filtered results. This ensures that the most recent matching posts appear first in the search results.

Overall, this code sets up state variables for storing the search query and search results. It uses the useEffect hook to update the search results whenever the posts or search dependencies change. The filtered search results are stored in the searchResults state variable after being reversed.



Also, we will pass the searchResults state variable as props in the Home component in the Route JSX in App.js in the following way:

<Route index element={<Home posts={searchResults} />} />

Create Error Page Component

With this, we are done with the major portion to the application and now we will write the logic for the Missing.js page which will appear if the page doesn’t exist in the following way:

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

const Missing = () => {
  return (
    <main className="Missing">
      <h2>Page Not Found</h2>
      <p>Well, that's disappointing</p>
      <p>
        <Link to="/">Visit Our Home Page</Link>
      </p>
    </main>
  );
};

export default Missing;

The above code defines a React component named Missing, which represents a page that is not found (404 error). Here's a brief explanation of the code:

  1. Importing the Link component from "react-router-dom":
    • import { Link } from "react-router-dom";: This line imports the Link component from the "react-router-dom" library. The Link component is used to create links within the application.
  2. The Missing component:
    • const Missing = () => { ... }: This line defines a functional component named Missing using arrow function syntax. The component has no props and doesn't receive any data from its parent component.
  3. Returning JSX:
    • The component returns JSX elements that represent the content of the "Page Not Found" page. It includes:
      • <main className="Missing">: A main element with the CSS class name "Missing".
      • <h2>Page Not Found</h2>: A heading displaying the text "Page Not Found".
      • <p>Well, that's disappointing</p>: A paragraph displaying the text "Well, that's disappointing".
      • <p><Link to="/">Visit Our Home Page</Link></p>: A paragraph containing a Link component that links to the home page ("/"). When clicked, it will navigate the user to the home page.
  4. Exporting the component:
    • export default Missing;: This line exports the Missing component as the default export of the module. It allows other parts of the application to import and use the Missing component.

In summary, this code defines a React component called Missing that represents a page not found. It displays a message and provides a link to the home page for the user to navigate back.

Create Footer Component

Next, we will write a simple piece of code for the Footer.js file to display the footer in the following way:

const Footer = () => {
  const today = new Date();

  return (
    <footer className="Footer">
      <p>Copyright &copy; {today.getFullYear()}</p>
    </footer>
  );
};

export default Footer;

The above code defines a functional component named Footer in React. Here's a breakdown of the code:

  1. The Footer component:
    • const Footer = () => { ... }: This line defines a functional component named Footer using arrow function syntax. The component has no props and doesn't receive any data from its parent component.
  2. Getting the current year:
    • const today = new Date();: This line creates a new Date object, which represents the current date and time.
  3. Returning JSX:
    • The component returns JSX elements that represent the content of the footer section. It includes:
      • <footer className="Footer">: A footer element with the CSS class name "Footer".
      • <p>Copyright &copy; {today.getFullYear()}</p>: A paragraph displaying the copyright symbol ("©") and the current year obtained from today.getFullYear(). This ensures that the year displayed in the footer will always be the current year.
  4. Exporting the component:
    • export default Footer;: This line exports the Footer component as the default export of the module. It allows other parts of the application to import and use the Footer component.

In summary, this code defines a React component called Footer that represents the footer section of a webpage. It displays the current year in the copyright notice.

Lastly, we will write some simple code for the About.js page in the following way:

const About = () => {
  return (
    <main className="About">
      <h2>About</h2>
      <p style={{ marginTop: "1rem" }}>
        This blog app is a project to learn and master the fundamentals of the
        React Framework.
      </p>
    </main>
  );
};

export default About;

The above code defines a functional component named About in React. Here's a breakdown of the code:

  1. The About component:
    • const About = () => { ... }: This line defines a functional component named About using arrow function syntax. The component has no props and doesn't receive any data from its parent component.
  2. Returning JSX:
    • The component returns JSX elements that represent the content of the About page. It includes:
      • <main className="About">: A main element with the CSS class name "About".
      • <h2>About</h2>: A heading displaying the text "About".
      • <p style={{ marginTop: "1rem" }}>... </p>: A paragraph displaying a description of the blog app project. The description is wrapped inside the style attribute with inline CSS to set a margin-top of "1rem" for the paragraph.
  3. Exporting the component:
    • export default About;: This line exports the About component as the default export of the module. It allows other parts of the application to import and use the About component.

In summary, this code defines a React component called About that represents the About page of a blog app. It displays a heading and a paragraph describing the purpose of the blog app project. The component's JSX code is responsible for rendering the content to be displayed on the About page.



Conclusion

In conclusion, React Routing is a powerful tool that enables developers to create seamless and dynamic user experiences in web applications. This blog post has guided you through the process of using React Routing to build a simple yet impressive blog site. By understanding the importance of responsive navigation and implementing routing, you now have the ability to create engaging and interactive blog sites. Take what you've learned, continue exploring the capabilities of React Routing, and enhance your web development skills. Happy coding!

Popular Posts

Perform CRUD (Create, Read, Update, Delete) Operations using PHP, MySQL and MAMP : Part 4. My First Angular-15 Ionic-7 App

Visualize Your Data: Showcasing Interactive Charts for Numerical Data using Charts JS Library (Part 23) in Your Angular-15 Ionic-7 App

How to Build a Unit Converter for Your Baking Needs with HTML, CSS & Vanilla JavaScript