Build a Static Website with React and HeroTofu

Build a Static Website with React and HeroTofu

·

16 min read

React is renowned for its ability to build complex and dynamic web applications. However, it's also commonly used to create simple, static websites with limited functionality. In this guide, you'll learn how to build a static website for a cleaning company using React and Herotofu. Note that this tutorial focuses solely on development and does not cover website publishing.

Preview of the Project

Here's a preview of the final project. The website will include a responsive navigation menu, along with a hero, services, why-choose-us, about-us, and footer sections, as well as a functional contact form.

Figure 1: A preview of the completed website.

Tools and Technologies

For this project, you will use the following tools and technologies:

  • Node.js: An open-source JavaScript runtime environment.

  • React: A JavaScript library for building user interfaces.

  • CSS Modules: A styling approach where class names are locally scoped by default, preventing global scoping issues.

  • HeroTofu: A tool for easily building contact forms. HeroTofu handles the backend infrastructure, allowing you to focus on the form's front-end design.

Prerequisites

To follow along with this tutorial, you should be familiar with the following concepts:

  • Props and props drilling in React

  • State and the useState hook

  • CSS Modules

  • API endpoints

Setting Things Up

Before writing any code, follow these steps to set up your development environment:

  1. Install Node.js: If Node.js is not installed on your device, download and install the runtime environment first.

  2. Create a new React app: After installing Node.js, run the following command to set up a new React application:

     //npm
     npx create-react-app my-app
    
     //yarn
     yarn create react-app my-app
    
     // Replace "my-app" above with your project name.
     // If you run into timeout errors, attach timeout flag to the commands as shown below:
     npx create-react-app my-app -timeout=10000
    
  3. Test the newly installed React app by initializing it with the following commands:

     //move inside the new app
     cd my-app
    
     //start the react app (npm)
     npm start
    
     //start the react app (yarn)
     yarn start
    
  4. Install the react-icons package: This package provides customizable icons for React applications. To install react-icons, run the following command:

     //npm
     npm install react-icons –save
    
     // Yarn
     yarn add react-icons
    
  5. Get a HeroTofu account: Sign up for a free HeroTofu account. After signing up, navigate to the Form section to create your first form. Once you submit the form, you will receive an auto-generated endpoint URL that should resemble the highlighted link in the image below.

    Copy the URL and store it in a safe place; you'll need it when you work on the contact form later. Now everything is ready, it's time to dive in.

Define the File Structure

A well-structured file system keeps your work organized and manageable. In this section, you’ll create folders and subfolders to store the code for different sections of the site. The CSS rules and images needed for the project are all in the project folder.

Step 1: Create an Images Folder

Inside the src folder, create a new folder named Images to store the image files for this project.

Step 2: Create a Components Folder

Create another new folder named Components within the src folder. This folder will contain all the components required for the project.

Step 3: Create Subfolders

Each component will have its own subfolder within the Components folder. To set this up, create subfolders with the following names in the Components folder:

  • Navigation

  • Hero

  • Services

  • WhyChoseUs

  • AboutUs

  • ContactUs

  • CompanyInformation

  • Footer

Step 4: Create Component Files

Within each subfolder created in the previous step, create a new file named after the subfolder. For example, the Navigation subfolder should contain a file named Navigation.js, and the Hero subfolder should contain a file named Hero.js. The image below illustrates this structure:

Step 5: Populate and Export the Components

The code snippet below demonstrates the content of Navigation.js. Use this pattern to populate the files you created in the previous step.

//Navigation.js

const Navigation = () => {
    return(
        <div></div>
    )
}

export default Navigation;

//Each file should have a stateless function with a default export.
//Repeat this for every file created in step 4, replacing "Navigation" with the appropriate component name.

Step 6: Import All Components

In the App.js file, import and render each of the newly created components as demonstrated below:

//App.js

import Navigation from './Components/Navigation/Navigation';
import Hero from './Components/Hero/Hero';
import Services from './Components/Services/Services';
import WhyChoseUs from './Components/WhyChoseUs/WhyChoseUs';
import ContactUs from './Components/ContactUs/ContactUs';
import Footer from './Components/Footer/Footer';
import AboutUs from './Components/AboutUs/AboutUs';

function App() {
  return (
    <>
      <Navigation />
      <Hero />
      <Services />
      <WhyChoseUs />
      <AboutUs />
      <ContactUs />
      <Footer />
    </>
  );
}

export default App;

Develop the Components

This section will guide you through building the components for the website. You’ll start with the navigation menu and work your way down to the footer.

Navigation

The website needs a fully responsive navigation menu. To start working on that, create the following files in the Navigation subfolder:

  • NavLinks.js

  • DesktopNavigation.js

  • MobileNavigation.js

  • Navigation.css

The NavLinks.js file is a reusable component that renders an unordered list of navigation menu items. See the code block below for details on the implementation.

//NavLinks.js

import './Navigation.css';

const NavLinks = ({isClicked, closeMenu}) => {
    return ( 
        <nav className="NavLinks">
            <ul>
                <li onClick={() => isClicked && closeMenu()}>
                    <a href="Hero">HOME</a>
                </li>
                <li onClick={() => isClicked && closeMenu()}>
                    <a href="/#Service">SERVICES</a>
                </li>
                <li onClick={() => isClicked && closeMenu()}>
                    <a href="/#About">ABOUT</a>
                </li>
                <li onClick={() => isClicked && closeMenu()}>
                    <a href="/#Contact">CONTACT</a>
                </li>
            </ul>
            <button className='Button' type="button">
                <span>Call us on:</span>
                <span>  (123) 456-78910</span>
            </button>
        </nav>
     );
}

export default NavLinks;

The DesktopNavigation.js file renders a menu bar for desktop devices. It imports the NavLinks.js and Navigation.css files, as well as an image file from the images folder. See the code snippet below for details.

//DesktopNavigation.js
import NavLinks from "./NavLinks";
import './Navigation.css';
import logo from './../../Images/Extraclean.png';

const DesktopNavigation = () =>{
    return(
        <nav className="DesktopNavigation">
            <img src={logo} alt="logo"></img>
            <NavLinks />
        </nav>
    )
}

export default DesktopNavigation;

The MobileNavigation.js file renders a menu bar for mobile devices. It uses the useState hook and a ternary operator to display a clickable hamburger icon that toggles the navigation menu on and off for mobile users.

//MobileNavigation.js
import NavLinks from "./NavLinks";
import './Navigation.css';
import {MdOutlineMenu} from 'react-icons/md'
import { useState } from "react";
import {MdClose} from 'react-icons/md';
import logo from './../../Images/Extraclean.png'

const MobileNavigation = () =>{
    const [click, setclick] = useState(false);
    const Hamburger = <MdOutlineMenu className="HamburgerMenu"
           size="30px" color="black"
           onClick={() => setclick(!click)} />
    const Close = <MdClose className="HamburgerMenu"
            size="30px" color="black"
           onClick={() => setclick(!click)} />
    const closeMenu = () => setclick(false);

    return(
        <nav className="MobileNavigation">
             <img src={logo} alt="logo"></img>
             { click ? Close : Hamburger}
             {click && <NavLinks isClicked={true} closeMenu={closeMenu}/>}
        </nav>
    )
}
export default MobileNavigation;

The Navigation.js file, created earlier, conditionally renders either the DesktopNavigation or MobileNavigation component based on the media query that applies to each user's device. Here's how to implement that:

//Navigation.js
import DesktopNavigation from './DesktopNavigation';
import MobileNavigation from './MobileNavigation';
import './Navigation.css';

const Navigation = () => {
    return(
        <div className='Navigation'>
            <DesktopNavigation />
            <MobileNavigation />
        </div>
    )
}
export default Navigation;

Finally, the Navigation.css file contains the style rules for all the components that make up the navigation menu. Copy the code from the project folder and paste it into your Navigation.module.css file. You should now see the following rendered in your browser:

Hero

Here, you'll take care of the hero section of the site. To set it up, add the following file to the Hero subfolder:

  • Hero.module.css

The Hero.js file created previously renders some hero text and a background image. The text highlights the company's service areas, while the background image enhances the visual appeal. The following code block contains the implementation details:

//Hero.js
import classes from './Hero.module.css';
import image from './../../Images/background.jpg';

const Hero = () => {
    return(
        <div id='Hero' className={classes.Hero} style={{ backgroundImage: `url(${image})`, 
        backgroundRepeat:"no-repeat", backgroundSize: "cover", backgroundPosition: "center"}}>
            <div className={classes.Container}>
                <h1>Professional Cleaning Service in San Francisco and surrounding areas</h1>
                <h3>We are a team of professional cleaners serving San Francisco and the entire Bay Area</h3>
            </div>
        </div>
    );
}
export default Hero;

The Hero.module.css file is the stylesheet for this component. Find the corresponding file in the project folder, copy its contents, and paste them into this file. You should then see an output similar to this:

Services

This section outlines how to build the component that displays the cleaning services offered by the company. To begin, add the following file to the existing Services.js file in the Services subfolder:

  • Serices.module.css

In the Services.js file, each service offered by the company will be rendered on a card. Each card includes a hero text, an image, and a short description at the bottom. The following snippet demonstrates this layout:

//Services.js

import classes from './Services.module.css';
import image1 from './../../Images/move-in-and-move-out.jpg';
import image2 from './../../Images/home-cleaning.jpg';
import image3 from './../../Images/office-cleaning.jpg';
import image4 from './../../Images/post-construction-cleaning.jpg';

const Services = () => {
    return(
        <div className ={classes.cardContainer}>
           <h3> Services we Offer</h3>
                  <div className={classes.BoxContainer}>
                        <div className={classes.Cards}>
                              <h4 style={{ backgroundImage: `url(${image2})`,
                                    backgroundRepeat:"no-repeat", backgroundSize: "cover", 
                                    backgroundPosition: "center"}}>
                              </h4>
                              <p className={classes.P}>
                                    <p>Home Cleaning</p>
                                    <span>We provide custom home cleaning service designed to fit your needs.</span>
                              </p>
                        </div>
                        <div className={classes.Cards}>
                              <h4 style={{ backgroundImage: `url(${image4})`, 
                                    backgroundRepeat:"no-repeat", backgroundSize: "cover", 
                                    backgroundPosition: "center"}}>
                              </h4>
                              <p className={classes.P}>
                                    <p>Post-construction</p>
                                    <span>Have a newly constucted building? We can handle all the debris expertly.</span>
                              </p>
                        </div>
                        <div className={classes.Cards}>
                              <h4 style={{ backgroundImage: `url(${image3})`, 
                                    backgroundRepeat:"no-repeat", backgroundSize: "cover", 
                                    backgroundPosition: "center"}}>
                              </h4>
                              <p className={classes.P}>
                                    <p>Office Cleaning</p>
                                    <span>We'll take care of your office space while you focus on serving your customers.</span>
                              </p>
                        </div>

                        <div className={classes.Cards}>
                              <h4 style={{ backgroundImage: `url(${image1})`, 
                                    backgroundRepeat:"no-repeat", backgroundSize: "cover", 
                                    backgroundPosition: "center"}}>
                              </h4>
                              <p className={classes.P}>
                                    <p>Move in/ Move out</p>
                                    <span>Moving in or out of an apartment? you don't have to add cleaning to your task. 
                                          We'll leave your space sparkling clean.
                                    </span>
                              </p>
                        </div>
                  </div>
        </div>
    );
}

export default Services;

Next, locate the CSS rules for this component in the project folder and copy them into your newly created Services.module.css file. Your services section should now resemble this:

WhyChoseUs

This section will guide you in building the component that renders cards displaying the company’s core values. To begin, add the following file to the WhyChoseUs subfolder:

  • Services.module.css

Before working on the WhyChoseUs.js file, visit the react-icons website to search for icons that match the company’s core values. On the react-icons website, enter each of the following keywords into the search bar in turn:

  • fast

  • quality

  • experience

  • money

When you identify a relevant icon for each term above, click on that icon to copy its name to your clipboard. The image below shows the result for the keyword "fast":

Import the icon into the WhyChoseUs.js file, placing each icon in a variable as demonstrated below:

//WhyChoseUs.js

//import statements for the icons
import {FaShippingFast} from 'react-icons/fa'; // for "fast" keyword
import {GiTakeMyMoney} from 'react-icons/gi';
import {FaAward} from 'react-icons/fa';
import {AiOutlineCheck} from 'react-icons/ai';

//place each imported icon in a variable and specify the sizes
const charges = <GiTakeMyMoney size='30px' />
const quality = <FaAward size='30px' />
const experienced = <AiOutlineCheck size='30px'/>
const quick = <FaShippingFast size='30px'/>

You’re using icons in this manner because you previously installed the react-icons package. To complete this component, retrieve the full code for both WhyChoseUs.js and WhyChoseUs.module.css from this link, and paste them into the corresponding files in your project. You should then see the following:

AboutUs

The AboutUs section is a straightforward component that renders text to the browser. To set it up, add the following file to the AboutUs subfolder:

  • AboutUs.module.css

In the AboutUs.js file, create a title for this component and include text that describes the company. The following code snippet shows how to do this:

//AboutUs.js

import classes from './AboutUs.module.css';

const AboutUs = () => {
    return (
        <div id="About" className={classes.AboutUs}>
                <div className={classes.Title}>
                    <h2>About Us</h2>
                </div>
                <div className={classes.Content}>
                    <p>
                    Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
                    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
                    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
                    Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
                    </p>
                </div>
        </div>
    );
}
export default AboutUs;

The AboutUs.module.css file in the project folder contains the styling for this component. Copy the style rules from that file and paste them into your AboutUs.module.css file to achieve the output shown below.

CompanyInformation

In this step, you’ll create a reusable component that displays the company’s basic information, such as the address, phone number, and hours of operation. To set it up, add the following file to the CompanyInformation subfolder:

  • CompanyInformation.module.css

Next, follow the steps outlined in the WhyChoseUs section to obtain icons for the keywords "contact" and "phone." The snippet below demonstrates how to render these icons alongside the relevant text:

//CompanyInformation.js

import classes from './CompanyInformation.module.css';
import {FiPhoneCall} from 'react-icons/fi';
import {FaAddressBook} from 'react-icons/fa';

const phone = <FiPhoneCall color='black' size='15px'/>
const address = <FaAddressBook color='black' size='20px'/>

const CompanyInformation = () => {
    return (
        <div className={classes.Information}>
                <div className={classes.Address}>
                    <h3>Office Address</h3>
                    {address}
                    <p>43 Sample Avenue, San Francisco,</p>
                    <p>California.</p>
                </div>
                <div className={classes.Phone}>
                    <h3>Get in touch with us</h3>
                    <div className={classes.icon}>{phone}</div>
                    <p>(123) 456-78910</p>
                    <p>Available 8am - 5pm daily.</p>
                </div>
            </div>
    );
}

export default CompanyInformation;

You can obtain the CSS code for this component from the CompanyInformation.module.css file in the project folder. Copy the code and paste it into your CompanyInformation.module.css file to obtain the output shown below:

ContactUs

This section will help you set up a functional contact form using HeroTofu. To begin, add the following files to the ContactUs subfolder:

  • Form.js

  • ContactUs.module.css

  • Form.module.css

Visit this page on HeroTofu's website to obtain the code for a contact form. Make sure to copy the code from the tab labeled "without TailwindCSS," since you’re not using TailwindCSS for this project. Refer to the following image for guidance:

Paste the code you copied into the ContactUs.js file you created earlier. Your code should be similar to the snippet shown below:

//ContactUs.js (code from HeroTofu)
//Paste the following code into your ContactUs.js file

import React, { useState } from "react";

const FORM_ENDPOINT = "https://herotofu.com/start"; 
// Update to the correct endpoint by replacing the URL above with the one you saved earlier.

const ContactForm = () => {
  const [submitted, setSubmitted] = useState(false);
  const handleSubmit = (e) => {
    e.preventDefault();

    const inputs = e.target.elements;
    const data = {};

    for (let i = 0; i < inputs.length; i++) {
      if (inputs[i].name) {
        data[inputs[i].name] = inputs[i].value;
      }
    }

    fetch(FORM_ENDPOINT, {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    })
      .then((response) => {
        if (!response.ok) {
          throw new Error('Form response was not ok');
        }

        setSubmitted(true);
      })
      .catch((err) => {
        // Submit the form manually
        e.target.submit();
      });
  };

  if (submitted) {
    return (
      <>
        <h2>Thank you!</h2>
        <div>We'll be in touch soon.</div>
      </>
    );
  }

  return (
    //Move this form element to the Form.js file you created
    <form
          action={FORM_ENDPOINT}
          onSubmit={handleSubmit}
          method="POST"
    >
              <div>
                <input type="text" placeholder="Your name" name="name" required />
              </div>
              <div>
                <input type="email" placeholder="Email" name="email" required />
              </div>
              <div>
                <textarea placeholder="Your message" name="message" required />
              </div>
              <div>
                <button type="submit"> Send a message </button>
              </div>
    </form>
  );
};

export default ContactForm;

Examining the code block above reveals that it makes a call to an endpoint in HeroTofu's API. For the call to be successful, replace the current value of the FORM_ENDPOINT variable with the URL you obtained after creating your first form. Here's what it should look like:

// Update to the correct endpoint by replacing the URL below
// with the one you saved earlier.
const FORM_ENDPOINT = "https://herotofu.com/start";

Next, tidy up the ContactUs.js file by following these steps:

  1. Move the form element into the Form.js file. Here's what the form element looks like:

     <form
               action={FORM_ENDPOINT}
               onSubmit={handleSubmit}
               method="POST"
         >
                   <div>
                     <input type="text" placeholder="Your name" name="name" required />
                   </div>
                   <div>
                     <input type="email" placeholder="Email" name="email" required />
                   </div>
                   <div>
                     <textarea placeholder="Your message" name="message" required />
                   </div>
                   <div>
                     <button type="submit"> Send a message </button>
                   </div>
         </form>
    
  2. In the now-empty return block of ContactUs.js, render the Form component, passing FORM_ENDPOINT and handleSubmit as props:

     //ContactUs.js
    
     return(
         <Form FORM_ENDPOINT={FORM_ENDPOINT} handleSubmit={handleSubmit}/>
     )
    
     //remember to import the Form component into this file first.
    
  3. In the Form.js file, catch and destructure the props to keep the code clean. Your Form component should now look like this:

     //Form.js
    
     import classes from './Form.module.css';
    
     const Form = ({FORM_ENDPOINT, handleSubmit}) => {
         return (
             <form
                 className={classes.Form}
                 action={FORM_ENDPOINT}
                 onSubmit={handleSubmit}
                 method="POST"
               >
                 <div className={classes.Container}>
                     <div className={classes.Name}>
                       <label for="name">Name
                         <input type="text" placeholder="Your name" name="name" required />
                       </label>
                     </div>
                     <div className={classes.Email}>
                       <label for="email">Email
                         <input type="email" placeholder="Email" name="email" required />
                       </label>
                     </div>
                     <div className={classes.Message}>
                       <label for="message">Message
                           <textarea placeholder="Your message" name="message" required />
                       </label>
                     </div>
                     <div className={classes.Button}>
                         <button type="submit"> <span>Get in touch</span> </button>
                     </div>
                 </div>
           </form>
         );
     }
    
     export default Form;
    
  4. In the ContactUs.js file, render the CompanyInformation component, a background image, and some text. The following snippet demonstrates how to do this:

     //ContactUs.js 
    
     import React, { useState } from "react";
     import CompanyInformation from '../CompanyInformation/CompanyInformation';
     import Form from './Form';
     import classes from './ContactUs.module.css';
     import image from './../../Images/AboutUs.jpg'
    
     const FORM_ENDPOINT = "https://public.herotofu.com/v1/YOUR OWN URL HERE";
    
     const ContactUs = () => {
       const [submitted, setSubmitted] = useState(false);
       const handleSubmit = (e) => {
         e.preventDefault();
    
         const inputs = e.target.elements;
         const data = {};
    
         for (let i = 0; i < inputs.length; i++) {
           if (inputs[i].name) {
             data[inputs[i].name] = inputs[i].value;
           }
         }
    
         fetch(FORM_ENDPOINT, {
           method: 'POST',
           headers: {
             Accept: 'application/json',
             'Content-Type': 'application/json',
           },
           body: JSON.stringify(data),
         })
           .then((response) => {
             if (!response.ok) {
               throw new Error('Form response was not ok');
             }
    
             setSubmitted(true);
           })
           .catch((err) => {
             // Submit the form manually
             e.target.submit();
           });
       };
    
       if (submitted) {
         return (
           <div className={classes.Container}>
             <h2>Thank you!</h2>
             <p>We'll be in touch soon.</p>
           </div>
         );
       }
    
       return (
         <div id="Contact" className={classes.Header} style={{ backgroundImage: `url(${image})`, 
               backgroundRepeat:"no-repeat", backgroundSize: "cover", 
               backgroundPosition: "center"}}>
             <div>
               <h2>Contact Us</h2>
               <h3>We're only a call or email away! All inquiries are treated with utmost priority</h3>
             </div>
             <Form FORM_ENDPOINT={FORM_ENDPOINT} handleSubmit={handleSubmit}/>
             <CompanyInformation />
         </div>
       );
     };
    
     export default ContactUs;
    

Finally, style the two components that make up this contact form by copying the contents of the Form.module.css and ContactUs.module.css files from the project folder. Paste the code into your Form.module.css and ContactUs.module.css files, respectively. You should now have the following contact form:

When completed, the footer component will render a footer section to the browser. To start, add the file below to the Footer subfolder:

  • Footer.module.css

In the existing Footer.js file, return a copyright logo and a Facebook icon that will link to the company's Facebook page. To have the Facebook icon, which is imported from the react-icons package, open in a new tab, set the target attribute of the a tag to '_blank'. The snippet below demonstrates this:

//Footer.js
import React from 'react';
import classes from './Footer.module.css';
import {FaFacebook} from 'react-icons/fa';

const fb = <FaFacebook size='20px' color='white'/>

const Footer = () => {
    return(
          <div className={classes.Footer}>
            <a href='https://wwww.facebook.com' target='_blank' rel='noreferrer'>{fb}</a>
            <p>&copy;2023 OneTouch llc</p>
          </div>
    )
}
export default Footer;

To complete this section, locate the Footer.module.css file in the project folder and copy its code. Then, paste it into the Footer.module.css file you created. Your footer section should now appear as shown below:

With the footer section complete, your website is now ready. The final step is to analyze the site's responsiveness.

Make the Website Responsive

The site is already responsive, thanks to the media queries and relative units included in the CSS files you copied during development. No additional adjustments are required, but you can tweak the values as needed to better fit your specific requirements.

Final Words and Recommendations

You've successfully built a static site using React and HeroTofu. This guide covered everything from setting up the project structure and developing individual components to integrating a functional contact form and ensuring the site is responsive across various devices. You can further enhance this project by incorporating additional features, such as integrating a map to display the company’s service areas, adding user accounts for personalized services, or any other feature that fits your specific needs.