ReactJS and NextJS Comparison
Read this if you always confused in Reactjs and Nextjs
Installation Setup
//ReactJS
npm create vite@latest
//NextJS
npx create-next-app@latest my-next-app
Tailwind
//ReactJS
Install Tailwind from react-vite tailwind doc on tailwind site
//NextJS
Already BuiltIn
Routing
//ReactJS
npm i react-router-dom
//wrap your application in browerrouter in main.tsx file
<BrowserRouter>
<App />
</BrowserRouter>
//In app.tsx
<Routes>
<Route path='/' element={<Home/>}/> // current page
<Route path='/about' element={<About/>}/>
<Route path="/profile/:id" element={<Profile />} />
</Routes>
//NextJS
Routing Already BuiltIn in NextJs
in app : every folder is a page
src/app/(Home)/page.tsx -> HomePage
profile/
│ └─ [id]/
│ └─ page.tsx → /profile/123
Component Structures
//ReactJS
<Route path='/' element={<Home/>}/> -> Home is our component: import Home from './pages/Home'
//Inside src/pages
import React from 'react'
const Home = () => {
return (
<div>
<SideBar/> -> Home page component make it separate src/component folder
</div>
)
}
export default Home
//NextJS
//src/app/(Home)/page.tsx
export default function HomePage(){
return (
<div className="bg-green-600 flex items-center justify-center">
<SideBar/> -> inside src/app/(Home)/_component/SideBar.tsx : Homepage component for shared component we can make separte folder in src/component
</div>
)
}
Routing Hooks & Navigations
1. Link
//Link in ReactJS
import { Link } from "react-router-dom";
<Link to="/profile/10">Profile</Link>
//Link in NextJS
import Link from "next/link";
<Link href="/profile/10">Profile</Link>
2. useNavigate() vs useRouter()
//useNavigate() in ReactJS
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
navigate("/login");
navigate(-1); // go back
//useRouter in Nextjs (in client component)
"use client";
import { useRouter } from "next/navigation";
const router = useRouter();
router.push("/login");
router.back();
3. Dynimic Route - useParams()
//In ReactJS
<Route path="/profile/:id" element={<Profile />} />
import { useParams } from "react-router-dom";
const { id } = useParams(); // extract like this
//In Nextjs
app/profile/[id]/page.tsx
//In Server compoenent
export default function Page({ params }) {
return <h1>Profile {params.id}</h1>;
}
//In Client component
"use client";
import { useParams } from "next/navigation";
const params = useParams();
console.log(params.id);
4. Query Params- useLocation() vs useSearchParams()
//reactsjs
import { useLocation } from "react-router-dom";
const location = useLocation();
const query = new URLSearchParams(location.search);
query.get("page"); // ?page=2
//Nextjs
"use client";
import { useSearchParams } from "next/navigation";
const searchParams = useSearchParams();
searchParams.get("page");
5. Current Route Info.: useLocation() vs usePathname()
//Reactsjs
const location = useLocation();
location.pathname;
location.search;
//Nextjs
"use client";
import { usePathname } from "next/navigation";
const pathname = usePathname();
6. Rediredirects
//Reactjs
import { Navigate } from "react-router-dom";
return isAuth ? <Dashboard /> : <Navigate to="/login" />;
//Nextjs (server side)
if (!isAuth) redirect("/login");
7. Protected Route
//Reactjs
const ProtectedRoute = ({ children }) =>
isAuth ? children : <Navigate to="/login" />;
//Nextjs
if (!session) redirect("/login");
redirect() vs useRouter in nextJS()
redirect() is a server-side navigation killer move ⚔️. It stops rendering immediately and tells Next.js:
useRouter() is client-side navigation 🚀. It works after the page is already rendered in the browser.
Data Fetching using axios(In Both ReactJS and NextJS)
Here we create two axiosInstances :
1. axiosInstanceWithToken → pass token to each req in headres
2. axiosInstanceWithoutToken → for routes like /login , /signin , /otp etc
//1. Create axiosInstance(WITH TOKEN) const createAxiosInstance = () => { return axios.create({ baseURL: "https://backend.lol.in/api/v1", // baseURL: "http://localhost:8080/api/v1", }); }; const axiosInstance = createAxiosInstance(); //Request Interceptor -> Runs before api call from this instance // It add Authorization: <token> , platform: StudyNotion with every req // No need to pass headres manually with each req. like this: axios.get(`${url}/userorders`, {headers:{token}}); axiosInstance.interceptors.request.use((config) => { const token = localStorage.getItem("token"); if (token) { config.headers.authorization = token; config.headers.platform = "Studynotion"; } return config; }); //Response Interceptor -> Runs after api response axiosInstance.interceptors.response.use( (response) => response, // response is sucess it return response (error) => { // if got error it return error if (error.response && error.response.status === 401) { //Token expired / invalid const unprotectedRoutes = [ //should NOT force logout , should NOT redirect to login "/", "/payment-routes" ]; if (!unprotectedRoutes.includes(window.location.pathname)) { toast.error("Session expired. Please login again."); localStorage.removeItem("token"); window.location.href = "/onboarding/sign-in"; } } return Promise.reject(error); } ); export default axiosInstance;//WITHOUT TOKEN import axios from "axios"; const createAxiosInstance = () => { return axios.create({ baseURL: "https://backend.lol.in/api/v1", // baseURL: "http://localhost:8080/api/v1", }); }; const axiosInstanceWithoutToken = createAxiosInstance(); axiosInstanceWithoutToken.interceptors.request.use((config) => { // optional if cant user it as header in backend config.headers.platform = "STUDYNOTION"; return config; }); export default axiosInstanceWithoutToken;// fetch apis like this in try catch const response = await axiosInstance.get("/user/get-employment-details"); const response = await axiosInstance.post("/user/post-employment-details", {employmentMode: selectedIncomeType, organization: organization,});
Concept of Private Routes and Open Routes
Even you create interceptor that only runs when on calling api you cant prevent user to visit private route if that page have no api call so we use privateRoute concept
// In ReactJS
import React from 'react'
import { useSelector } from 'react-redux'
import { Navigate } from 'react-router-dom';
const PrivateRoute = ({children}) => {
const {token} = useSelector((state) => state.auth);// store token in redux state(or context) while login
if(token !== null)
return children
else
return <Navigate to="/login" />
}
export default PrivateRoute
// This will prevent authenticated users from accessing this route
function OpenRoute({ children }) {
const { token } = useSelector((state) => state.auth)
if (token === null) {
return children
} else {
return <Navigate to="/dashboard/my-profile" />
}
}
export default OpenRoute
//How to use above
<Route path='/' element={<Home/>}/> -> public route
<Route path='/login' element={<OpenRoute><Login/></OpenRoute> }/>
<Route path ='/dashboard' element={<PrivateRoute><Dashboard/></PrivateRoute>}>
//In Nextjs
// Nextjs uses middleware.ts to do above things
// If you store your token in localstorage then you do like this, cuz we can not access local storage in server component
"use client"
import PrivateRoute from "@/components/PrivateRoute"
export default function DashboardLayout({ children }) { // create layout.tsx for every page
return <PrivateRoute>{children}</PrivateRoute>
}
//If you store your token in cookies(BEST PRACTICE) then we can handle this concept in middlewere.ts in nextjs
// middleware.ts is a server component
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
const PRIVATE_ROUTES = [
"/dashboard",
"/profile",
"/settings",
"/courses",
"/admin",
]
const OPEN_ROUTES = [
"/login",
"/register",
"/forgot-password",
]
const isPrivateRoute = (pathname: string) =>
PRIVATE_ROUTES.some(
(route) =>
pathname === route || pathname.startsWith(`${route}/`)
)
const isOpenRoute = (pathname: string) =>
OPEN_ROUTES.includes(pathname)
export function middleware(req: NextRequest) {
const token = req.cookies.get("token")?.value
const pathname = req.nextUrl.pathname
// 1️⃣ Private route → must be logged in
if (isPrivateRoute(pathname)) {
if (!token) {
return NextResponse.redirect(new URL("/login", req.url))
}
}
// 2️⃣ Open route → must be logged OUT
if (isOpenRoute(pathname)) {
if (token) {
return NextResponse.redirect(
new URL("/dashboard/my-profile", req.url)
)
}
}
// 3️⃣ Public route → no checks
return NextResponse.next()
}
export const config = { //Run middleware on everything EXCEPT these paths:_next/static , _next/image
matcher: [
"/((?!api|_next/static|_next/image|favicon.ico).*)",
],
}
//OR
export const config = { // run middleware on these pages only
matcher: [
"/dashboard/:path*",
"/login",
],
}