Table of contents
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.