AirBnB clone with NextJS 14 -Part 1

Nabendu Biswas
18 min read1 hour ago

--

In this post, we are going to built an AirBnB clone using NextJS 14 and Tailwind CSS. We are also going to use Appwrite for login, register and storage features. It is similar to firebase and make backend interaction easier.

This post have been inspired by the awesome YouTube video by Traversy Media. You can find the link here.

So, let’s get started and create a new NextJS 14 project by giving the create-next-app command in terminal. Give the exact command and also No or Yes similar to the screenshot.

Now, change to the new folder bnb-clone, then open in VS Code and start the app with npm run dev command.

Now, open the app in http://localhost:3000/ and we will see the below NextJS screen.

Next, go to page.js file which is the Home screen and remove everything and add a h1 tag

From the globals.css file, we have removed all code except the tailwind part.

Next in layout.js file, which contains code for all files, we have removed all earlier code and kept a basic code with a new font.

Now create an assets folder in the root directory. Inside it create an images folder and put a logo.svg file. Also, create a components folder in root directory and create a Header.jsx file inside it.

Here, we have first imported Image, Link from Next and recently added logo. In the return, we have a header element containing the logo and the nav bar elements.

After that we have added the right side menu and also the Mobile menu in the Header.jsx file.

Now, our http://localhost:3000/ in desktop view looks like below.

The mobile menu is responsive and looks like below.

Now, we will add react-icons in our project to show icons. So, add the same from the terminal.

Now, we have first imported react-icons in Header.jsx file. After that added the respectative icons instead of the <i> tags.

The http://localhost:3000/ now shows perfect icons in the navbar.

Next, we will create the footer component. So, create a file Footer.jsx inside the components folder and add the below code in it.

Next, we will add the footer component in the layout.js file.

Now, go to http://localhost:3000/ and we will see the footer component.

Now, create a data folder in the root directory and add a rooms.json file in it. We will use this array of object to show rooms with details on home page.

Next, we will create a public folder in the root directory. Inside it will create images and inside it rooms folder. We will put five room images in it.

We have also created a RoomCard.jsx file inside the components folder. It is taking the room props and image, address, availability and price.

Now, in page.js file we will first loop through rooms from rooms.json file. And pass each room to the RoomCard component.

Back in http://localhost:3000/ we will see the rooms showing properly.

We will create a file Heading.jsx in the components folder, which takes title as props and show it with tailwind styles.

Now, we will call the Heading component from page.js file and pass a title.

Now, home page in http://localhost:3000/ we will also get the heading.

Now, we will create the page to show a single room. Inside the app folder, create a rooms folder. In the rooms folder, create an [id] folder. Create a page.jsx file in it and add the below code in it.

Here, we are getting params as a prop and extracting id from it. From the id we are finding a particular room from the rooms array using find. Inside the return we are showing the Image and the room description.

Now, click on any room in the home page and we will get the image and description of it.

Now, we will show all the other details of the room using an ul in the rooms page.jsx file.

Now, the room will show more details in localhost.

Now, we will write the code for Booking Form which will be added in the room page. So, add a BookingForm.jsx file in the components folder. Here, we have four input and a button.

Now, add the BookingForm component in the rooms page.jsx file.

Now, the room will show the form also in localhost.

Now, we will implement appwrite, which is a backend service as firebase. Through it we can add a NoSQL database with ease. First login to it and click on Create project button.

Now, give the project a name and project id and click on Next button.

After that we have to select a region, which is only Frankfurt at the moment. Click on the Create button.

Now, click on Web in the next screen.

In the next screen for Hostname registration give a name and make sure Hostname is localhost.

In the next screen just click on the Next button.

In the next screen also, just click on the Next button.

In the next screen click on the Go to dashboard button.

Now, click on Databases tab.

Now, click on Create database button which will open a pop-up. Here, give a name and database id.

Now, since it’s a NoSQL database we need to create a collection. So, click on Create collection button which will open a pop-up. Here, give a name and Collection ID. And then click on Create button.

After that we will be taken inside the collection. Here, click on Attributes tab and click on Create attribute button, which will open a drop-down. Here, click on String.

A pop-up will opne in which give the Attribute Key as user_id, give a Size and make it Required. After that clci on the Create button.

Now, create attributes for name, description, address, location, availability, sqft, capacity, price_per_hour, amenities and image following the similar process. All are String and some are Required.

Next, go to Auth tab and click on Create user button.

Here, give the Name, Email and Password and click on Create button.

Now, we will get an inique user id. Just copy it.

Now, go back to the Database tab and click on rooms collection. Here go to Documents tab and click on Create document button.

Here, in the user_id give the same id copied earlier from Auth. Give the name and description also. We are taking all from rooms.json file.

Fill the rest of the fields also and click on the Next button.

In the next screen just click on the Create button.

Add three more records from rooms.json file, following the same process. After that click on the Settings tab.

After that scroll a bit down and give the permissions and click on Update button.

Now, go to Overview tab and click on API keys and then Create API key button.

In the next screen give the name and keep the Expiration Date as Never. Click on the Next button after that.

In the next screen click on Select all and then Create button.

Next, copy the API key secret from the next screen.

Now, create a .env file and inside it some constants. Here, in NEXT_APPWRITE_KEY paste the API key secret from the earlier screen.

Now, open a terminal and install node-appwrite, which is used to connect the React code to appwrite.

Next, create a config folder in the root directory and create a appwrite.js file inside it. Here, we are first importing from node-appwrite. After that creating a async function createAdminClient where we are creating a client first, from the secrets.

After that we are returning account, databases and storage by using the client.

After this in appwrite.js file, create an async function of createSessionClient. Here, again we are creating a client first and returning account and databases.

Finally, we are exporting both createAdminClient and createSessionClient.

Now, through actions we will get all data from backend. So, create a folder actions inside app folder and a file getAllRooms.js inside it.

Here, we have a getAllRooms function. We are calling createAdminClient to get access to the databases.

After that we are getting all the rooms and returning it.

Now, go back to the page.js file. Here, we are first making the Home function async and calling the getAllRooms with await.

Now, in http://localhost:3000/ we will get the rooms from the database.

Now, we will get the data for a single room. So, create a function getSingleRoom.js inside the actions folder. It is similar to the getAllRooms file, the difference is that it is taking the id to get the single room.

Now, use the getSingleRoom in page.jsx inside rooms=>[id] folder.

Now, when we click on a room we will get the details of it from the database.

We will update the href in Header.jsx file as they were not right.

Now, create a folder login inside app and a page.jsx file in it. Here, we have an input for email and password. And a button for login, also have a link to register.

Now, go to http://localhost:3000/login and we will see the Login page.

Now, create a folder register inside app and a page.jsx file in it. Here, we have an input for name, email, password and confirm password. And a button for register, also have a link to login.

Now, go to http://localhost:3000/register or click on Register in homepage and we will see the register page.

Now, we will create a middleware.js file in the root directory. It runs everytime we go to the route in the config. Inside the middleware function, we are hard-coding isAuthenticated as false. Then we have a if statement checking !isAuthenticated, which will redirect to /login.

Now, create a folder bookings inside app and a page.jsx file in it. Here, we just have a basic file.

Now, when we click on Bookings, it is re-directed to /login page.

Now, create a new action of createSession. Here, we are first just getting the email and password and showing them in console.

Now, go back to the login->page.jsx file. Here, we have made the file client side first by using use client at top. After that we have import react hook of useEffect and useFormState and also createSession, which is recently created.

Now, inside the LoginPage component we are using the useFormState hook by passing createSession and an empty object to it. In the form we have made the action as formAction.

Now, in http://localhost:3000/login give a email and password and click on Login button. We will see the email and password been logged in the console.

We are also going to use React toast in our project. So, install react-toastify package from the terminal. Next in layout.js add the ToastContainer and css as import. Now, add ToastContainer inside the body.

Now, we will update the createSession.js file we check the non-availability of email and password first and return an error or return success true.

Back in the LoginPage component, import the toast and useRouter first. After that we are using the useEffect hook. Here, in case of error, we are throwing a toast with erro.

In case of success we are showing toast with success and also going back to the home(/) route.

Now, go back to login page and enter a email and password and click on Login button.

We will see the toast message and also the redirection to http://localhost:3000/

Now, we will complete the createSession.js file. Here, we are first importing createAdminClient and cookies. After that inside the function, we are destructuring account from createAdminClient.

Inside a try statement, we are creating a const session from email and password been passed to in-built function of createEmailPasswordSession. After that we are creating a cookies and naming it appwrite-session and passing some required parameters.

Next, we are returning success. In the catch block we are loggin the erro and returning an error.

Now, go back to Login page and give the real email which we have given earlier in appwrite site. But give a wrong credential and click the Login button.

Here, we will get the toast of Invalid Credentials. But if we give the right credentials, we will be able to login.

Now, we will create the logic for Sign Out. So, create a destroySession.js file inside the actions folder. Here inside the function we are getting the cookies appwrite-session first.

Next, inside the try block we are getting the account again and deleting the current session. After that clearing the session cookie and returning sucess true. In case of error returning error through catch block.

Now, in the Header.jsx file, we are making it client component first. After that we are importing destroySession, useRouter and toast.

Inside the Header we are using the router and writing the logic for handleLogout function. This function calls the destroySession function. In the case of success, go back to home route.

In case of failure shows the error as toast message. We have also updated the Link to a button calling the handleLogout on click.

Now, go to http://localhost:3000/ and click on Login button. Also, notice that we have a cookie called appwrite-session before clicking.

We will see the toast message for Logged in sucessfully .

Once we click on Sign Out we will see the cookie been deleted and we will also be re-directed to http://localhost:3000/login

Now, we will create the code to check the authentication. So, create a file checkAuth.js inside the actions folder. Here, we are first get the cookies of appwrite-session. If the cookie is not available, we are setting isAuthenticated as false.

Inside a try block, we are first getting the account using createSessionClient. After that we are getting the user. Next, we are returning isAuthenticated as true along with the user details of id, name and email. The catch block is returning the isAuthenticated as false.

Next, inside the Header.jsx file we are first importing the checkAuth and react hook of useEffect and useState. Inside the Header component, we are create a state of isAuthenticated.

Then in the useEffect calling checkAuth and setting isAuthenticated to the result.isAuthenticated.

Now, inside the return statement of Header.jsx file, we are wrapping the Booking and Add Room with isAuthenticated. Then wrapping the Login and Register with not isAuthenticated. Again My Rooms is wrapped with isAuthenticated.

Also, in the Mobile menu of Header.jsx file wrapping the Booking and Add Room with isAuthenticated.

We want to shift the authentication logic to Context API, so that it can be used through out the app. So, create a new folder context in the root directory. Here, create a file authContext.js.

Here, we are first importing required things from React and also checkAuth. Then created a variable AuthContext from createContext. We are exporting the AuthProvider, which takes a children props.

Here, we have create the state of isAuthenticated and currentUser. After that in useEffect we are calling checkAuth and setting the isAuthenticated and currentUser.

Finally, we are returning the Provider with the required value and children. We have also created a custom hook called useAuth. Here using the useContext hook, we are setting a variable of context and returning it.

We can use this directly in the layout.js file, but then to use a React feature we have to make it client. That means the whole app will become client side and we don’t want it. So, we will create a simple wrapper component. Create a file AuthWrapper.jsx inside the components folder.

In this client component, we are importing AuthProvider first. After that in a function AuthWrapper, we have children props. This returns the children props inside AuthProvider.

Now, in the layout.js file first import AuthWrapper and then in the return wrap all the code.

Next, in the Header.jsx file we will remove the earlier code for local state and use data from context. So, we are importing useAuth first. After that inside the Header calling the useAuth and getting isAuthenticated and setIsAuthenticated from it.

Now, inside the handleLogout we are setting the isAuthenticated to flase in case of success.

Now, in the root page.jsx file for LoginPage, we are calling the useAuth and extracting setIsAuthenticated from it. Now, inside the useEffect we are setting the isAuthenticated to true in case of success.

Now go to http://localhost:3000/ and if we are not logged in, we will not see any session variable. So, click on the Login button.

Once, we are logged in we will see the correct buttons and also the session of appwrite-session.

Now, in the middleware.js file, we will call the checkAuth to get the isAuthenticated instead of hardcoded false.

Now go to http://localhost:3000/bookings and if logged in we will see the component.

Now, we will write the logic to Register a new user. So, create a file createUser.js inside actions folder. It is similar to createSession.js file, but we are taking name, password and confirmPassword also.

After doing some checks we are extracting account from createAdminClient. Inside the try block, we are using account.create and passing a unique id, email, password and name. After that we are returning success.

In the case of error we are catching it in the catch block and returning the error.

Now, in the RegisterPage component, we are first making it client. Then importing the required things including createUser action.

Inside the component function, we are using useFormState and passing createUser to it. In a useEffect in case of error showing it in toast. In case of success, showing a toast of success. Also, pushing the user to login page.

For using this we just need ot make the form action as formAction.

Now, Log out and click on Register or go to http://localhost:3000/register . Here, we are giving a 4 character password and getting the correct error toast.

Now, give passwords which don’t match and you will get the the correct error toast.

Now, give everything correct and a new user will be created. We can check the same in appwrite.

Now, go to http://localhost:3000/login and login with the newly created user.

We are able to successfully login with the new user.

We still have a lot to complete, but that will be done in part 2 of the post. You can find the code here.

--

--

Nabendu Biswas

Architect, ReactJS & Ecosystem Expert, Youtuber, Blogger