Build the Gumtree Clone in ReactJS (Tutorial for Beginners)

Build the Gumtree Clone in ReactJS (Tutorial for Beginners)

This article will guide you in building a clone of the popular classifieds site Gumtree using React. Gumtree allows listing items for sale in categories and searching/viewing ads. We will replicate these core functions to construct a full-featured classifieds clone from scratch with React.

What is Gumtree Clone?

A Gumtree clone script is a classified advertising website that allows users to list items for sale in various categories. It enables users to browse listings, view detailed product information, contact sellers and much more, mimicking the key features available on the popular online classifieds platform Gumtree.

Why Choose ReactJS?

Here are some key reasons why ReactJS is an excellent choice for building the Gumtree clone:

Component-Based Architecture

A major component in classified platforms is listings which have a consistent structure. React supports building reusable components for items/listings that can be rendered conditionally as needed. This compartmentalizes code and promotes reusability.

Fast Rendering with Virtual DOM

Listings pages generally contain a dynamic list of items that need frequent updates. React's virtual DOM implementation optimizes rendering of updated UI by synchronizing changes with the real DOM. This ensures listings pages load seamlessly even with thousands of listings.

Large Developer Community

Being one of the most popular JavaScript frameworks, React has a huge community and ecosystem backing it. This means there is ample documentation, tutorials and plugins available to help during development. Issues can also be easily resolved by referring discussions online.

Works Well with Node.js/Express and MongoDB

For the backend, Node.js and Express are highly scalable choices. MongoDB's flexible document model is well-suited for storing classified data. Combining React, Node.js, Express and MongoDB forms a very powerful tech stack that is production-ready.

Easy to Learn and Maintain Code

React uses JavaScript and its concepts like components, state, and props are relatively easy to learn. This simplifies onboarding new developers. The predictable nature of React also makes the codebase clean and maintainable in the long run.

Step 1: Project Setup

To get started, we initialize a new React app using Create React App (CRA) which is the easiest way to set up a React development environment.

npx create-react-app gumtree-clone
cd gumtree-clone

CRA sets up a default React project structure with build tools configured. Next, we design basic app layout with common elements like Header, Sidebar and main Content area as reusable components:

// App.js

function App() {
  return (
    <div>
      <Header />
      <div className="container">
        <Sidebar />
        <Content />  
      </div>
    </div>
  );
}

We add some css for basic styling and our app skeleton is ready. Next step is adding routing to navigate between different pages.

Step 2: Router Setup

We install React Router to enable routing within our React app:

npm install react-router-dom

React Router allows defining route components which get rendered conditionally based on URL path. We create Route components for important pages like Home, Listings and Single Listing:

// Router.js

<Router>
  <Route exact path="/" component={Home} />
  <Route path="/listings" component={Listings} /> 
  <Route path="/listing/:id" component={SingleListing} />
</Router>

We can now render these routes inside App component:

// App.js

function App() {
  return (
    <div>
      {/* Header, Sidebar */}  

      <Routes />

    </div>
  );
}

Users can navigate between pages via browser URL or <Link> components. Our basic routing is ready allowing navigation around the app.

Step 3: Authentication

User authentication is essential for activities like posting/managing listings. We integrate authentication with JSON Web Tokens (JWT) for securely authorizing users.

First, we setup API endpoints with Express/Mongoose to handle sign up, login:

// api/auth.js

router.post('/signup', async (req, res) => {
  // signup logic 
});

router.post('/login', async (req, res) => {
  // login logic
  res.send({token});
});

In React, we create Auth components and context for managing state:

// context/AuthContext.js

export const AuthContext = React.createContext(); 

export const AuthProvider = ({children}) => {

  const [user, setUser] = useState();

  // login, logout functions

  return (
    <AuthContext.Provider value={{user, login, logout}}>
      {children}
    </AuthContext.Provider>
  )
}

Each protected route is wrapped in an authorization check:

// Route.js

<Route 
  path="/user"
  component={() => (
    <AuthCheck>
      <UserPage />
    </AuthCheck>
  )}
/>

Our authentication flow is complete allowing users to signup, login and gain access to authorized areas.

Step 4: Data Models

To store classifieds data, we first design database models. Using MongoDB's flexible schema, items can be represented as:

// models/Item.js

const itemSchema = new Schema({
  title: {type: String, required: true},
  description: String, 
  image: String,
  category: String, 
  price: Number,

  postedBy: {
    type: Schema.Types.ObjectId,
    ref: 'User' 
  }
});

export default mongoose.model('Item', itemSchema);

User model to store profile data:

// models/User.js

const userSchema = new Schema({
  name: String,
  email: {type: String, required: true, unique: true},
  password: {type: String, required: true},

  items: [{type: Schema.Types.ObjectId, ref: 'Item'}]
});

With models defined, we can now work on connected React components for interface.

Step 5: Listings Page

Users primarily come to browse and search listings. We build a Listings component to display items dynamically fetched from backend API:

// Listings.js

const Listings = () => {

  const [listings, setListings] = useState([]);

  useEffect(() => {
    fetchListings();
  }, []);

  const fetchListings = async () => {
    const res = await api.get('/listings');
    setListings(res.data);
  }

  return (
    <div className="listings">
      {listings.map(listing => (
        <ListingItem listing={listing} key={listing._id}/>  
      ))}
    </div>
  )
}

We render reusable <ListingItem> component for each item passing necessary props. Users should also be able to filter results using sidebar filters:

// Sidebar.js

<FilterSection>
  <h4>Category</h4>

  <div>
    <input 
      type="checkbox" 
      name="cars"
      onChange={handleFilters} 
    />
    Cars
  </div>

  //...other filters

</FilterSection>

Our listings page with basic search/filter is ready allowing users to browse items.

Step 6: Single Listing Page

When user clicks on a listing, it should render its detailed contents. We build a SingleListing component fetching item data based on URL id param:

// SingleListing.js

const SingleListing = ({match}) => {

  const [listing, setListing] = useState({});

  useEffect(() => {
    fetchListing(match.params.id);
  }, [match]);

  const fetchListing = async(id) => {
    const res = await api.get(`/listings/${id}`);
    setListing(res.data);
  }

  return (
    <div className="listing">
      <img src={listing.image} />

      <h3>{listing.title}</h3>

      <p>{listing.description}</p>

      //...other details
    </div>
  )
}

Users can now view complete info of each item easily. We add contact seller feature by sending form data to backend.

Step 7: Database Connectivity

To store and manage our app's data, we integrate a MongoDB database using Mongoose as the Object Data Modeling (ODM) library. Mongoose provides a cleaner and more intuitive interface to MongoDB compared to directly using the native MongoDB driver.

We first install Mongoose:

npm install mongoose

Then in our server.js file, we connect to MongoDB by passing the connection string:

// server.js

const mongoose = require('mongoose');

mongoose.connect(connectionString, {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

mongoose.connection.on('connected', () => {
  console.log('Connected to MongoDB');
});

Now our server is connected to the remote MongoDB database. We can define Mongoose schemas for our data models like Item and User based on the requirements.

With database connectivity established, we have a seamless way to perform CRUD operations on our classifieds data from both the API routes and React frontend. Overall, combining Mongoose with MongoDB provides a scalable and flexible database solution for our classifieds clone app.

Step 8: Post/Edit Listings

Allowing users to post new listings is crucial for any classifieds site. We create a PostListing component with a dynamic form rendering each field:

// PostListing.js

const PostListing = () => {

  const [form, setForm] = useState({
    title: '', 
    description: ''
    //...other fields 
  });

  const handleChange = (e) => {
    setForm({...form, [e.target.name]: e.target.value});
  }

  const submitListing = async () => {
    await api.post('/listings', form);
  }

  return (
    <form onSubmit={submitListing}>
      <input 
        name="title"
        value={form.title}
        onChange={handleChange}  
      />

      <textarea
        name="description" 
        value={form.description}
        onChange={handleChange}
      />

      //...other fields

      <button type="submit">Post Listing</button>
    </form>
  )
}

Similarly, we build an EditListing component to populate existing data and allow updates.

On backend, API routes handle posting/updating listing documents in MongoDB collection.

Step 9: User Profile

Authenticated users should have a profile page to manage their account. We develop a Profile component fetching user details:

// Profile.js

const Profile = () => {

  const { user } = useAuth();

  const [profile, setProfile] = useState({});

  useEffect(() => {
    fetchProfile(user._id); 
  }, [user]);

  const fetchProfile = async(id) => {
    const res = await api.get(`/users/${id}`);
    setProfile(res.data);
  }

  return (
    <div>
      <h2>Profile</h2>

      <p>Name: {profile.name}</p>

      <p>Email: {profile.email}</p>

      // edit form
    </div>
  )
}

Users can view/update basic info and listings posted by them from this dashboard.

Step 10: CRUD Operations

We add CRUD routes and API logic for items as well as other data models like users.

For listings:

// api/listings.js

router.get('/', async (req, res) => {
  res.send(await Item.find());
});

router.post('/', async (req, res) => {
  res.send(await Item.create(req.body));
});

// update, delete routes

Similarly for users:

// api/users.js

router.get('/:id', async (req, res) => {
  res.send(await User.findById(req.params.id));
});

router.patch('/:id', async (req, res) => {
  res.send(await User.findByIdAndUpdate(req.params.id, req.body));  
});

// delete route

Our full CRUD system for items and users data is complete now.

Step 11: Deployment

We deploy our finished Gumtree clone app using modern hosting platforms like Vercel for global availability. Some main deployment steps:

  • Build React app for production: npm run build

  • Configure Vercel project linking GitHub repo

  • Setup environment variables for mongo URI etc

  • Import and link database remotely

  • Verify functioning through Vercel deployment preview

  • Publish changes for live at custom domain

Users anywhere can now access our live classifieds site on the web!

Conclusion

In this detailed tutorial, we went through building a fully-functional Gumtree clone web application from scratch using React, Node.js and MongoDB. React's component architecture helped deliver large scalable interfaces.

Authentication, listing posting/editing and user profile modules round up key features. Deploying the app globally demonstrates a complete workflow. Overall, React is indeed well-suited to develop classified listing platforms like Gumtree.