AirBnB clone with MERN stack -2

Nabendu Biswas
15 min readDec 22, 2024

--

In this post, we are going to complete the AirBnB clone using MERN stack. The part-1 of the series can be found here.

We will start where we left and start with showing places. So, first in the server side, inside models folder create Place.js file. Here, we have imported mongoose first and after that given type of different fields.

Notice that owner have reference to the User model.

Now, in App.jsx file we will change the components for /bookings and /places.

Now, create a Bookings.jsx file inside the components folder. Here, we are showing the AccountNav and just a text for now.

Now, create a Places.jsx file inside the components folder. Here, we are showing the AccountNav and a Link which takes us to /account/places/new.

Now, we will update the Profile.jsx file. Here, if the subpage is equal to places we will show the Places component.

Now, Clicking on My bookings will take us to http://localhost:5173/account/bookings which will show the basic Bookings Page.

Now, Clicking on My accomodations will take us to http://localhost:5173/account/bookings which will show the basic Places Page.

Now, click on My profile which will take us to http://localhost:5173/account .It will show that we have token. Now, click on Logout button.

After clicking on thew Logout previously, we are taken to the home page, where the token is empty.

Now, create a PlacesForm.jsx file in the components folder. Here, we have states which we are going to use soon. After that we have three functions, which we are going to use soon.

Now, inside the return statement we are first rendering AccountNav component. After that we have a form, inside it using the preInput() we are showing the text.

We have various input boxes for all the states created earlier.

Back in the App.jsx file we will have a new route for /account/places/new which will render the PlacesForm component.

Now, going to http://localhost:5173/account/places/new we will see the whole form.

Now, we will add Perks in http://localhost:5173/account/places/new. For this create a Perks.jsx file inside src folder. Put the below code in it, which will contain and handleCBClick function targeting the name and checked for two inputs for wifi and parking.

Now, we will put the rest of the input and icons for tv, radio, pets and entrance in Perks.jsx file.

We will render the Perks from the PlacesForm.jsx file.

Now, in http://localhost:5173/account/places/new we will see the Perks been added.

Now, we will add the image-downlader package from terminal and also import it in index.js file in api folder. We have also created an uploads folder inside the api folder.

Next, we will create a new link called /upload-by-link for POST in the index.js file. Here, we are taking the link from req.body and after that we are creating a newName.

Using the imageDownloader we are downloading the image to the /uploads folder.

Now, create a PhotosUploader.jsx file inside the src folder. Here, we are getting the props of addedPhotos and onChange. After that we have an state of photoLink which we are using in the input field.

We also have a button, which is calling the function addPhotoByLink. This function is doing API call to /upload-by-link and passing the photoLink.

Now, we will call this PhotosUploader from PlacesForm.jsx file.

Next, we will add path with express.static() in index.js file.

Now, in the PhotosUploader.jsx file we will take the addedPhotos and map through it and show them with Image component. And a button with icon to remove the photo.

Now, create an Image.jsx file in the src folder and add the below code in it. Here, we are taking the src and rest of the params. We are then adding http://localhost:4000/uploads/ to src. Then we are returning the img with src, alt and rest parameter.

Now, goto http://localhost:5173/account/places/new and add image links and click on Add photo. We have added two photos which are been shown.

If we go to uploads folder, we can see the two photos been uploaded.

Now, we will write the logic to upload images from local computer. So, create a function uploadPhoto in PhotosUploader.jsx file. Here, we are taking the files and sending it through axios to /upload endpoint.

We also have an input type file with an icon, which calls the function uploadPhoto.

Now, go back to index.js file of the server and import multer and fs. We have also added the external package of multer.

Now, we will create a photosMiddleware with multer. After that a POST request to /upload which uses photosMiddleware array. Next we will loop through the files and add it in the uploads folder.

Now, goto http://localhost:5173/account/places/new and click on Upload. It will open the explorer. Here, add two images.

We have the new images been added sucessfully.

We have some warnings from react and it is also sucessting way to fix it. So, chose the warning and change it into camelCase.

Now, go to PlacesForm.jsx and add a function savePlace. It will take all the data and do a POST request to a new endpoint od /places.

Now, in the index.js file we will add the model of Place first. After that we will create the /places POST. Here, we are taking the token first from the cookies. Next, we are extracting title, address and other things from req.body.

After verifying the token using jwt.verify() method, we are adding the data in databse using Place.create(). Lastly, we are passing the placeDoc data back.

Now, go to http://localhost:5173/account/places/new and add all data including photos and click Save.

All of the data will be added in the places model in MongoDB database.

Now, we will add the codes to show the added places. So, we will create a new endpoint of /user-places in index.js file. It will take the token from the cookies. It is then use jwt.verify() and get the id from userData and use it to find the place.

We will also create a file of PlaceImg.jsx. It will take some params and show the image from place.photos[index].

Now, back in Places.jsx file, we will create a state of places. The using useEffect we will call /user-places endpoint and get the data. We will set the data to places array.

Now, inside the return statement we will loop through the places and show a Link tag, with PlaceImg passing the place.

Now, the data of the place will be shown in http://localhost:5173/account/places

Now, we write the logic for single place. So, we will add a new route in App.jsx file, for /account/places/:id , which will render the PlacesForm component.

Now, in the PlacesForm.jsx file we will get the id from useParams(). Next, in an useEffect with dependency on id, we will do the GET api call to /places/:id.

In the response we will get the data, which we will set in title, address and other states.

Now, we will create the GET api for /places/:id in index.js file. Here, we are first getting the id from the params. After that with findById() we are getting details of the place.

Now, click on an existing place in localhost and we will get the details updated in the place.

Now, in the PlaceForm.jsx file we will also add the logic for id, which will do a PUT request to /places and pass the id and the complete data.

Now, in the index.js file we will create a PUT request for /places. Here, we will first get the token from cookies and the data from body.

After that in jwt.verify(), we will find the place with findById(). Then if the id and owner id are same, we will use set to update and save to save the data.

Now, click on a place and change something and then click on Save button.

Now, in http://localhost:5173/account/places the updated description will be shown.

We have uploaded two new photos and it’s also working fine.

Now, we will add logic to remove a phot or make another photo as main photo. By default the first photo is the main photo.

So, in PhotosUploader.jsx file, in the remove button we have the removePhoto function. We have create a new button to make the photo as main photo.

Here, the function selectAsMainPhoto is taking link as a parameter. If the link is equal to the first photo we are showing a filled star, or else an outlined star.

Now, we have created the function removePhoto and selectAsMainPhoto in PhotosUploader.jsx file. Here, in removePhoto we are making the sending the selected photo by filter.

On selectAsMainPhoto we are adding it ahead of all the other photos through filter.

Now, make some other photo as main photo in localhost and click on Save button.

Now, the selected photo will be shown as main photo in http://localhost:5173/account/places

Next, we are going to show all places in the home page. For this we will create a new GET route to /places in index.js file.

Now, in the Index.jsx file, we have added places state. In the useEffect, we are calling the /places endpoint and setting the places to data.

Inside the return we are showing the first photo, address, title and price.

In the Layout.jsx we have added a little bit of paddding.

Now, in http://localhost:5173/ we will see the only place with photo and other details.

We have added another place and we are seeing the same in http://localhost:5173/

Now, we will add the logic to show the details of a single place when the user clicks on it in the home page. For this in App.jsx file we will add a new route of /place/:id which will call the Place component.

Now, we will create a Place.jsx file inside the components folder. Here, in initial code we are doing some imports. And after that getting the id from the useParams() hook. We have a place state and the useEffect is doing an API GET call to /places/id and getting the place details in response.

Now, we will update the return in Place.jsx file. Here, we are showing various details like title, description, checkIn, checkOut, maxGuests, extraInfo.

We are also passing address and place to AddressLink and PlaceGallery components, which we are going to create next.

Now, create an AddressLink.jsx file in src folder and add the below code in it. Here, we are showing an anchor tag with google maps taking the address.

Now, create a PlaceGallery.jsx file in src folder and add the below code in it. Here, using various styling we are showing the photo one and photo two. We also have a button with Show more photos.

Next, in the PlaceGallery.jsx file we will have a showAllPhotos state. Next, if the showAllPhotos is true, we will show all the photos by mapping through it and passing each detail to Image component.

We also have a Close photos button, which close this toggle.

Now, when we click on a place three photos with other details will be open. Here, click on the Show more photos button.

Now, we will get all the photos expanded and the Close photos button also. Clicking this button will close this toggle.

Now, we will create the booking logic. So, first call a new component BookingWidget from Place.jsx and pass the place detail in it.

Now, create a BookingWidget.jsx file inside the src folder. Here, we have various states of checkIn, checkOut, numberOfGuests, name, phone. Inside the return statement, we are showing the price.

We have five input box to change the checkIn, checkOut, numberOfGuests, name, phone.

Now, for a place we will also get the booking in form.

We will now add a date utility library called date-fns.

Back in the BookingWidget.jsx file, we will import the function differenceInCalendarDays from date-fns. Now, we will get the numberOfNights from the checkOut and checkIn date.

We are using the numberOfNights inside the return statement. Here, we are showing the name and phone input fields if only the numberOfNights is greater then 0. In Book this place, we are also using it to show the total price.

Now, when we are changing the Check in and Check out, we will see the total price in localhost.

Now, we will create the Booking.js file inside the models folder. In this file we will create the schema for booking in which the place will be reference to the Place model.

Now, include the Booking model in the index.js file. Here, we have also created a function called getUserDataFromReq. This function will return a Promise.

It will do jwt.verify and verify the token and send the userData back in resolve.

Now, we will create the POST and GET for /bookings. In the post, we are getting the userData and also place, checkIn and other things from req.body. Then with Booking.create() we ar adding the data in MongoDB database and sending back the doc.

In the get, , we are getting the userData and getting the id from it. Next, we are using Booking.find() to fing the bookings.

Back in BookingWidget.jsx file we are importing axios and Navigate. Then inside the bookThisPlace function we are passing the data to /bookings POST endpoint.

Now, create a Booking.jsx file in the components folder. We are going to complete it later on.

Next, in App.jsx file we will add route for /account/bookings and /account/bookings/:id which will call the Bookings and Booking components respectively.

Now, create a BookingDates.jsx file inside the src folder. This helper component will be used to show number of nights and dates using date-fns functions and icons.

Next, create the Bookings.jsx file inside the components folder. Here, we are importing required thing. And then inside the useEffect calling the GET endpoint of /bookings and updating the bookings state.

Now, inside the return statement we will use the AccountNav component first. After that will map through the bookings array. Here, we will show the title and price. Also, will pass the booking to BookingDates component created earlier.

Back inside a place in localhost, add details for bookings and click on Book this place button.

The POST api to /bookings was sucessful and we landed in Booking component with Booking text.

Now, in http://localhost:5173/account/bookings we will see our booking with some details.

Now, we will update the Booking.jsx file. Here, we have a booking state and then from a useEffect we are hitting the GET API for /bookings.

Inside the return statement, we are showing various details like title, address, price. Also, passing address and place props to AddressLink and PlaceGallery respectively.

Now, once a booking is done we will see a nice booking detail page in localhost.

This completes the final part of the post. You can find code for the same in this github repo.

--

--

Nabendu Biswas
Nabendu Biswas

Written by Nabendu Biswas

Architect, ReactJS & Ecosystem Expert, Youtuber, Blogger

Responses (1)