LetGo is one of the most popular marketplace apps for local buying and selling. In this article, we will learn how to build a full-fledged LetGo Clone using the React JavaScript library and Material UI framework.
The app we will build will have all the core features of LetGo like user authentication, listing products, searching and filtering listings, messaging between users, payment integration and more.
We’ll cover the full development process in a step-by-step manner with detailed code snippets at each step. By the end of this article, you’ll have a strong understanding of full-stack web development with MERN stack.
So without further ado, let’s get started!
1. Setting Up the Development Environment
The first step is to set up our development environment. We’ll be using:
- Node.js and NPM for package management
- React for building the frontend UI
- Material UI for styled UI components
- MongoDB and Mongoose ORM for the database
Create a folder and initialize an NPM project:
mkdir letgo-clone
cd letgo-clone
npm init
Install required packages:
npm install react react-dom
npm install @material-ui/core
npm install express mongoose bcryptjs jsonwebtoken
Create client/
and server/
folders. In client/
, create App.jsx
and render a React component. In server/
, create server.js
and add Express server code.
Now our dev environment is ready!
2. Designing the UI and App Layout
In this step, we’ll design the UI using Material UI.
Import AppBar
, Toolbar
, Grid
etc from Material UI in App.jsx
.
Create route components like Home
, Products
etc and render routes in App.jsx:
<BrowserRouter>
<AppBar>
<Toolbar>
<Button>ProductsButton>
Toolbar>
AppBar>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/products" element={<Products/>} />
Routes>
BrowserRouter>
Import Container
, Card
, CardMedia
etc to build product cards:
<Grid container spacing={2}>
<Grid item xs={3}>
<Card>
<CardMedia
image="/product.jpg"
/>
Card>
Grid>
Grid>
Import styled components to design pages. Our basic UI is ready!
3. Building the Authentication System
Now let’s add authentication using MongoDB, Mongoose and JWT.
First, define a User model and auth routes in server.js
:
const userSchema = new Schema({
name: String,
email: String,
password: String
});
app.post('/register', async (req, res) => {
// Hash password and create user
});
app.post('/login', async (req, res) => {
// Validate user and return JWT
});
In client
, create Login.jsx
component with a login form:
const Login = () => {
const [email, setEmail] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.post('/login', {
email,
password
});
// Save token
} catch (err) {
console.error(err);
}
}
return (
<form onSubmit={handleSubmit}>
<input
name="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<button type="submit">Loginbutton>
form>
)
}
Similarly, build registration page. Now users can register and login!
4. Developing Listings Feature
Now let’s add the core listing feature to sell products.
First, define a Product model:
const productSchema = new Schema({
name: String,
price: Number,
description: String,
images: [String],
userId: {type: Schema.Types.ObjectId, ref: 'User'}
});
Create CRUD routes to add/get products.
In Products.jsx
, import Avatar
, Button
etc:
const Products = () => {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchProducts();
}, []);
const fetchProducts = async () => {
const res = await axios.get('/products');
setProducts(res.data);
}
return (
<Grid>
{products.map(product => (
<Card key={product._id}>
<CardMedia
image={product.images[0]}
/>
<CardContent>
<Typography>{product.name}Typography>
CardContent>
Card>
))}
Grid>
)
}
Now users can view listings! Let’s create an AddProduct.jsx
form next.
5. Implementing Search & Filter
To search and filter listings, add a SearchBar component:
const SearchBar = ({setSearchTerm}) => {
const handleChange = (e) => {
setSearchTerm(e.target.value);
}
return (
"Search"
onChange={handleChange}
/>
)
}
Then filter products on frontend:
const Products = () => {
const [searchTerm, setSearchTerm] = useState('');
const handleSearch = (products) => {
return products.filter(product =>
product.name.toLowerCase().includes(searchTerm)
)
}
return (
<>
{handleSearch(products).map(product => (
// product cards
))}
>
)
}
Add location, category filters similarly. Search and filters work!
6. Building Chat Feature
Now let’s build real time chat between users using Socket.IO:
// server
const io = require('socket.io')(server);
io.on('connection', (socket) => {
socket.on('newMessage', ({from, to, message}) => {
// Broadcast message
});
});
Create Message.jsx
model and routes.
In Chat.jsx
:
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
useEffect(() => {
socket.on('newMessage', message => {
setMessages([...messages, message]);
});
}, [messages]);
const handleSubmit = async () => {
socket.emit('newMessage', {
from: user._id,
to,
message: newMessage
});
setNewMessage('');
}
return (
<div>
{/* messages */}
<input
value={newMessage}
onChange={e => setNewMessage(e.target.value)}
/>
<button onClick={handleSubmit}>Sendbutton>
div>
)
Now chat in real time!
7. Integrating Payments
For payments, we’ll integrate with PayPal sandbox.
Create Order
model to store order details:
const orderSchema = new Schema({
product: {type: ObjectId, ref: 'Product'},
userId: {type: ObjectId, ref: 'User'},
price: Number,
// etc
});
Configure PayPal credentials in server. Handle payment on frontend:
const handlePayment = async () => {
// Call API to create order
const response = await axios.post('/create-payment', {
// order details
});
// Redirect to PayPal checkout
}
return (
actions.order.create({
// order details
})}
onApprove={() => handlePayment()}
/>
)
On success, call API to capture payment. Now payments work!
8. Adding User Profile
Let’s build a profile page to view user details and listings.
Create Profile.jsx
component to get user and listings:
// server.js
app.get('/me', auth, async (req, res) => {
const user = await User.findById(req.user._id);
res.json({ user });
});
We’ll make a request to this route from our profile component to populate the user data:
// Profile.jsx
const Profile = () => {
const [user, setUser] = useState({});
useEffect(() => {
fetchUser();
}, []);
const fetchUser = async () => {
const res = await axios.get('/me');
setUser(res.data.user);
}
return (
<div>
<h2>{user.name}h2>
div>
)
}
Next, we’ll build out the edit profile form. We’ll keep the existing user data in state:
const [name, setName] = useState(user.name);
const [email, setEmail] = useState(user.email);
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.patch('/me', {
name,
email
});
} catch (err) {
// handle error
}
}
return (
) This allows the user to view and edit their profile details.
9. Deploying to Production
To deploy our app, we’ll use Heroku for the backend and Vercel for the frontend.
First, modify server code for production:
- Add env variables for keys
- Use MongoDB Atlas instead of local db
- Add CORS middleware
- Use production middleware
if(process.env.NODE_ENV === 'production') {
app.use(prodMidddleware);
}
Add build
script to package.json
for production build:
"scripts": {
"start": "node index.js",
"build": "cd client && npm run build"
}
Now deploy to Heroku:
heroku create
git push heroku main
heroku open
For frontend, deploy React app to Vercel:
vercel
That’s it! Our LetGo clone is now live on Heroku+Vercel.
Wrapping Up
In this article we learned how to develop a full-fledged LetGo clone application using React, Material UI and Node.js. The step-by-step guide covered setting up authentication, building product listings, implementing search and chat along with payments integration. Following this tutorial one can now develop a powerful classifieds marketplace of their own to facilitate local buying and selling within their community.