👋 Hey there, today in this blog we will start a new series where we will learn “how to make a fullstack blogging website using MERN stack” basically we will make a website very similar to medium.com.
You probably think it is just some other tutorial claiming to make a fullstack website with just simple things but I will say, you should see the website demo first available below. You will see a lot of features there from having a fully working modern editor for writing blogs, to features like, like a blog, comment on blog and even replies to any comment ( basically nested comments ) on top of that a very good dashboard UI to manage blog and view insights like “like count, read count, comments count” and did I mention any interaction you do on website will store in database as a notification for other user. In short, its a kind of project which will just shine in your portfolio.
Okay so let’s see how we can start building this project, I can’t wait to start this blog.
This project is so big so the tutorial of this project will be in parts. In video formats it will be 4 or 5 parts that you can find on “Modern Web” youtube channel and In blogs there will be 3 - 4 parts for every video part
Website Demo
If you want to see the full website in action, all features from simple to advance. You can watch the demo below. The video below also include this blog in video format.
You can download full source code of this project if you want. Download Source Code
Github Initialisation
Okay, in order to follow this project, you need to download this github repo.
You can fork the repo in your account and then clone it on your system or you can just download the repo without forking it to your account. Once you have the repo, you will get 2 folders inside it
blogging website - frontend
- this folder is frontend ( react ) code
server
- this folder is backend ( node ) code
open both of these files on terminal separately because we need to install the packages in order to make this project. You don’t have to worry about any package name or version all of these things are mentioned in package.json
file already.
blogging website - frontend
folder setup
Once this folder is open in terminal, first run npm install
cmd to install all the packages, after that you just need to start the frontend vite server whenever you want to access the website. You can do that by running npm run dev
cmd in terminal.
server
folder setup
Once this folder is open in terminal, first run npm install
cmd to install all the packages.
That’s sit once you have installed all the packages we can start working on the project.
Navbar
Let’s start our project by working on our Navbar first. Let’s create navigation for easy user interaction. We will create navbar as a react component because we want to render navbar on multiple pages/routes. So open navbar.component.jsx
under components folder. Create a very basic Navbar Functional component.
const Navbar = () => {
return (
<h1> Navbar </h1>
)
}
export default Navbar; // default let's you import function without { }
Above component is returning an h1
to render for now so that we can check whether or not our component working properly. Also make sure to export the component as a default component.
Now to render it, we will go inside app.jsx
file which is our app file where we will setup routes for this project later. In file you will see its returning an h1
by default. so remove that and instead add <Navbar />
to render it. ( Do not forget to import the component at top, in VScode you can do that automatically if you just press enter while typing component name from the suggestion VScode gives you )
import Navbar from "./components/navbar.component";
const App = () => {
return (
<Navbar />
);
}
export default App;
So if you are getting “navbar” text on the page that means everything is working and if not then it’s time for you to recheck the code and see where you made mistake.
Okay, let’s create our navbar now. So instead of returning h1
return nav
and we will create navbar inside it. Add Classname to it navbar
because in index.css
file, there is navbar
class with bunch of tailwind CSS for navbar.
const Navbar = () => {
return (
<nav className="navbar">
{
// nav elements will go here
}
</nav>
)
}
Navbar - Logo
In our navbar, we have a logo and whenever user click on that logo we want to redirect user to homepage. So first let’s make the logo inside navbar.
<nav className="navbar">
<img src={logo} className="flex-none w-10" />
</nav>
img
element has flex-none
class, so that our navbar flex
won’t affect its size. In the src
I have given {logo}
, if you don’t know what this { }
used for then its simple inside JSX to access/write JS you can use { }
and inside the brackets you can use JS and you can even call variable inside it to access variable name. So src
is basically whatever this logo
variable is. Now what is it ?
Well in order to use any external file in react you have to import it, whether its a component, function, image, svg anything. You have to first import it and this logo is exactly the import of image at the top of navbar.component.jsx
file.
import logo from "../../imgs/logo.png"; // img path respective to components folder as the file is inside components folder
// rest of the code
Output
Okay we got img
but how we can redirect user to /
( home route ). Well one way is to wrap the img
inside an a
tag.
<a href="/">
<img src={logo} className="flex-none w-10" />
</a>
This will redirect you whenever you will click on image. But if you notice the site. You will see whenever you are clicking on image the page get’s reload and this destroy the concept of React Single Page Application concept as whenever you will click on image. Browser will ask for the website again from the server. So then, what can we use. To resolve this issue, instead of a
we will use <Link> </Link>
component offered by react-router-dom
. React router is a library that help you mimc the functionality of Browser routing insider React application. So using Link
component of that, we can redirect the user to whatever route we want without reloading the page.
So first import it at top.
import { Link } from "react-router-dom";
import logo from "../../imgs/logo.png";
// rest of the code
And now, wrap the img
inside Link
<Link to="/" className="flex-none w-10">
<img src={logo} className="w-full" />
</Link>
to
prop means on which route we want to redirect. I have changed the className
as well. That just adding the flex-none
functionality to Link
and w-full
for img
to cover full width that is w-10
But now you won’t see anything in the output. Why ? Because in order to use Link
or any other features/components of react-router-dom
you have to first wrap the <App />
component inside <BrowserRouter>
which is also a component from react-router-dom
which mimics the Browser routing functionality, and since we will have the <App />
inside it. The App
which is our whole project, will be able to access the BrowserRouter
functionalities.
So inside main.jsx
wrap the App
<BrowserRouter>
<App />
</BrowserRouter>
And make sure to import the BrowserRouter
at the top too.
import { BrowserRouter } from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root')).render(
// React Component
)
Navbar - Search Box
After logo we need a search box in our navbar. Well we will make this search box functional later. But right now, let’s get the UI done. So after Link
create a div
and let’s create a search box input like this 👇
// inside <nav> tag after <Link> .. </Link>
<div className="absolute bg-white w-full left-0 top-full mt-0.5 border-b border-grey py-4 px-[5vw] md:border-0 md:block md:relative md:inset-0 md:p-0 md:w-auto">
<input
type="text"
placeholder="Search"
className="w-full md:w-auto bg-grey p-4 pl-6 pr-[12%] md:pr-6 rounded-full placeholder:text-dark-grey md:pl-12"
/>
</div>
To understand the tailwind classes I use, I suggest you watch the video tutorial as you will get better understanding of each classes. Anyway, so basically the div
is a container for input
and the search icon that we will create next.
// after search box input
<i className="fi fi-rr-search absolute right-[10%] md:pointer-events-none md:left-5 top-1/2 -translate-y-1/2 text-xl text-dark-grey"></i>
For icons, I am using Flat Icons , in the Github files font awesome installation is already done, so you can use any font awesome icon directly in the project. The one I am using is fi fi-rr-search
( a magnifying glass icon )
After this we will see something like this.
Output - smaller screens
Output - medium screens
So it’s done, but in smaller screens, what we want is to show/hide the search box whenever user clicks on search icon visible on navbar. Something like 👇
To create that after the parent div
of search box. Make another div
and this will be a container for actions buttons on the right side in our navbar.
// after search box's div & inside nav
<div className="flex items-center gap-3 md:gap-6 ml-auto">
<button className="md:hidden bg-grey w-12 h-12 rounded-full flex items-center justify-center">
<i className="fi fi-rr-search text-xl"></i>
</button>
</div>
So this JSX element with tailwind styles will give an output like this. Where you will see the search icon button in smaller screens and as soon as you hit medium screen size, you will not get the search icon because the search box will be inside navbar in that screen size.
Output - smaller screens
Output - medium screens
Now, how we can show/hide the search box. Well to achieve this I will use React States to track whether the search box should be visible or not. So let’s create a state first to track that.
const Navbar = () => {
const [ searchBoxVisibility, setSearchBoxVisibility ] = useState(false) // default value will be false
return (
// navbar code here
)
}
Now since we have a state now, we will toggle its value from true → false → true whenever the search icon got click. To do that add onClick
to the button
and run a function to call setSearchBoxVisibility
function to change the state’s value.
// search icon button
<button className="md:hidden bg-grey w-12 h-12 rounded-full flex items-center justify-center"
onClick={() => setSearchBoxVisibility(currentVal => !currentVal)}
>
<i className="fi fi-rr-search text-xl"></i>
</button>
currentVal
is the first parameter that you can use while calling setState functions to access the most recent value of the state and to just toggle that current Boolean
value, you can return the currentVal
with !
in the beginning.
Now if you click on the button, it should change the state value from false → true → false and so on. But you won’t see anything because the state is changing but its value doesn’t affecting the UI. So to show or hide the search box. We will add conditional classes to it.
<div className={"absolute bg-white w-full left-0 top-full mt-0.5 border-b border-grey py-4 px-[5vw] md:border-0 md:block md:relative md:inset-0 md:p-0 md:w-auto " +
( searchBoxVisibility ? "show" : "hide" )}
>
{
// search box input and icon
}
</div>
Above code is just adding show
or hide
class on the basis of searchBoxVisibility
state. show/hide
classes are inside index.css
file. If you didn’t get what is happening in above code. Well first, instead of " "
for className
there is { }
is the start and the end, which means inside it we can use JS. Then, we are simply concatenating class string using “tailwind classes” + “class from the condition”
. To add condition I have used ternary operator
which is a short form of if else
that you can use inside JSX instead of actual if else
and its syntax is like this condition ? true case : false case
. Now here are 2 things to remember while using classNames like this. First wrap you condition inside ( )
otherwise it will not concatenate with the string and second add a [space]
at the end of the static classNames, like I have added space after md:w-auto
at the end. This will make sure that the classes will have space after concatenation other wise after concatenation, if you don’t have space at the end, it will look like this md:w-autoshow
instead of md:w-auto show
and this will then not apply both styles to the element.
So now you should be able to show/hide the search box by clicking on the search button.
Output
But there is an issue, if you just hide the search box, and then go to medium or large screen sizes. You will notice that there is no search box and neither the search button to show/hide the search box visibility. That is because we have hidden
class to the button on medium screen so its not showing and since the state is false, the parent div
has hide
class because of the condition. To fix this, add md:show
class to the search box’s div
. So that on medium screens even if we have hide
class, it will have show class which will overwrite the hide
styles.
<div className={"tailwind classes md:w-auto md:show " +
( searchBoxVisibility ? "show" : "hide" )}
>
{
// search box input and icon
}
</div>
Navbar - Sign in / sign up button
Now we want some sing in / sing up buttons + a “write” button in the navbar for easy navigation for the user. So let’s create that. Inside the div
where we have search button to show/hide the search box. Add a “write” Link
element which will be hidden
in smaller screens.
// search icon button
<Link to="/editor" className="hidden md:flex gap-2 link">
<i className="fi fi-rr-file-edit"></i>
<p>Write</p>
</Link>
Output
Now to create sign in / sign up buttons which will redirect the user to /signin
or /signup
route. Code this after the above code.
// search icon button & write button
<Link className="btn-dark py-2" to="/signin">
Sign In
</Link>
<Link className="btn-light py-2 hidden md:block" to="/signup">
Sign Up
</Link>
/signup
button/Link will be hidden in smaller screens as we don’t want to clutter our navbar on small devices.
Output
Route Setup
So after navbar, let’s setup some routes in react app, so that we can deliver sign in / sing up form and start working on those pages. Now since, ReactJS is a SPA means a simple page application, creating routes is difficult. So we will take help of “react-router” again in order to setup Routes. So open App.jsx
and let’s setup Routes.
So in return first remove everything and add Routes
Component which is provided by “react-router-dom”.
import { Routes } from "react-router-dom";
const App = () => {
return (
<Routes>
{
// All the routes will come here
}
</Routes>
)
}
Routes
Component basically allows you to setup routes for you projects. Now inside it we will use Route
( without s
) Component to create each and every routes. So inside it add this.
<Routes>
<Route path="/" element={<Navbar />}></Route>
<Route path="/signin" element={<h1> Sing In Form </h1>}></Route>
<Route path="/signup" element={<h1> Sing Up Form </h1>}></Route>
</Routes>
Route
component lets you define the routes using 2 props.
-
path
- it defines the route path. In the above code we have created 3 Routes with/
,/signup
,/signin
path. Where the react router deliver different Components. -
element
- it renders whatever elements you pass inside it when the path is matched. For instance, If I now access/signin
path. I will see ah1
with “Sign in form” text.
After setting these 3 routes, if you go on these 3 routes, you will see the elements being render but there is a issue. Well basically, I want to show <Navbar />
component to /signin
and /signup
routes as well, not only those but as we will work on other pages, in most pages we want the navbar. So how we can do that, well the simple fix is that inside each page, render <Navbar />
separately, means add <Navbar />
in all pages at the top before writing their HTML. But thats not a good practice. So instead of doing that we will go for second option, which is Outlet
.
Outlet
is a component again provided by react-router
which allows you to create nested routes and render multiple components at once. Let see how it works. So first of all we have to change our routes a little bit.
<Routes>
<Route path="/" element={<Navbar />}> // `Navbar` will get render whenever the path matches i.e -> "/"
<Route path="signin" element={<h1> Sing In Form </h1>} /> // h1 will get render on path -> "/signin"
<Route path="signup" element={<h1> Sing In Form </h1>} /> // h1 will get render on path -> "/signup"
</Route>
</Routes>
In the above code, I have nested the signin
and singup
route inside /
path. Which will enable you to render 2 component because when you go to /signin
let’s say, then 2 condition will be true, the first is /
where react router will render Navbar
and then the signin
route where react router will render h1
. But if you see your output, on all those 3 paths you will Navbar, but no h1.
So first, congrats as we got the navbar on all these 3 paths but we lost the h1 component. Well there is nothing to worry about it. To render components in this type of nested routes. We use Outlet
Component from “react-router”. We will add this Outlet
on the parent route component. So in this case the parent route is /
and its component is Navbar
. So open navbar.component.jsx
and at the end in return add <Outlet />
make sure to import that and also wrap your <nav>
and <Outlet />
in an empty component ( <></>
) so that react wont’s throw any error because in react you can only render 1 parent component.
This is how your navbar.component.jsx's return
will look.
return (
<>
<nav className="navbar">
// navbar code
</nav>
<Outlet />
</>
)
So as long as we have Outlet
in Navbar
Component, we will see both routes on nested routes.
So thats sit for this blog, I hope you learned a lot by following this article. Make sure you follow me to not miss the next part whenever I will post it. Also if you want, you can checkout my channel, and show you love by subscribing me and you can watch the full tutorial of this part 1. If you have any doubt feel free to ask me in comments. Till then thanks for reading.
Top comments (0)