Next Level Nextjs
Next Level Nextjs
js
SSR search
ders + Suspense
Properly use Loa
SQL injections)
ctions (against
Protect server a
1 2 ... 9 10
Index 00
Table Of Contents
Introduction 01
Infinite Scroll 02
Pagination 11
Search 36
Next Level Next.js 01
Introduction
Welcome to the Next Level Next.js: Master Advanced Features, Powerful
Libraries, and Fortify with Robust Security Practices. Here you’ll find all
the cool advanced features you wanted to implement in the latest
Next.js 14 but weren’t sure how to do it.
Without wasting too many words on the preface, let’s dive straight into
the world of code
P.S., Before starting any of these features, take your time to think how
you would go for it and then see how we do it. It helps improve logical
thinking
Next Level Next.js 02
Infinite Scroll
(Before moving on to reading the next words, think carefully. Stress your
muscle memory)
First of all, fetch your data on the server page (say the first 5 elements)
return <main>...</main>;
Now to automatically fetch the next 5 elements, we’ll first need to show
some kind of indicator or spinner that will be seen at the end of the list
Next Level Next.js 03
function LoadMore() {
return (
<div role="status">
<span class='sr-only'>Loading...</span>
</div>
</div>
);
(You can get the actual styles and SVGs in the full source code)
return (
<main>
...
<LoadMore />
</div>
</main>
);
To automatically fetch the next page the moment we reach the end of
the current list, we’ll have to keep track of whether we reached the end
of the list or not. Kind of inspection.
Luckily, there is a li rary that can tell if something is in view of the user or
b
not.
Next Level Next.js 04
react-intersection-observer
NPM Link
Since we have placed the spinner at the end of the list, using this react-
intersection-observer we can see if the spinner is in view or not.
"use client";
function LoadMore() {
useEffect(() => {
fetchNextPage();
}, [inView]);
return (
{inView && (
<div role="status">
<span class='sr-only'>Loading...</span>
</div>
)}
</div>
);
If you have gone through our Next.js course or even watched a few of
our videos, you would know why it’s marked as a client component
We want to trigger the fetch call from the LoadMore component, which
is a client child of the server component, i.e., Home.
We’ll manage the page that we’ll fetch in our URL, and from there, we’ll
retrigger the fetch.
In a nutshell,
Next Level Next.js 06
that we reached the end and it’s time to get new items
fetch call inside the Home component will look out for the URL
changes and then retrigger the fetch to get the next items
And repeat
"use client";
params: searchParams.toString(),
key: "page",
value,
});
};
useEffect(() => {
fetchNextPage();
}, [inView]);
return();
}
Next Level Next.js 07
In the above code, you’ll see we’re forming a new URL by passing the
value to formUrlQuery. It’s nothing new. Instead of doing
router.push(/?page=2), we’re using a library called query-string to
update the URL carefully for us.
query-string
NPM Link
Why?
Doing it manually, in this case, will work for sure. But imagine if there is
something already in the URL that you don’t know. For example, /?
filter=newest Then doing the direct router.push(/?page=2) would
override what URL has previously.
Sure, we can first check what’s in the URL, append it first, and then add
new things and do it this way - /?filter=newest&page=2 but that’s much
effort. This problem is solved effortlessly by the above library. Now you
know why…
currentUrl[key] = value;
return qs.stringifyUrl(
url: window.location.pathname,
query: currentUrl,
},
{ skipNull: true }
);
All we need to do is get the page number we set in the URL from
LoadMore on the Home page and do a little math
const MAX_ITEMS = 5;
`http://localhost:8000/tasks?_start=0&_end=${page * MAX_ITEMS}`
);
return (
<main>
...
Next Level Next.js 09
const MAX_ITEMS = 5;
`http://localhost:8000/tasks?_start=0&_end=${page * MAX_ITEMS}`
);
return (
<main>
...
</div>
</main>
);
Over there, you’ll also see we’re passing isNext prop to the LoadMore
component. This is to ensure that we won’t show the spinner all the time
if there is no more data coming from the database.
You can see the full source code here. Feel free to give it a try!
P.S., if you don’t want to put the page param in the URL or basically want
to hide it from the user, there is another way too. We recently did a
video where we taught how to do infinite scroll using a different
approach on a real-world API. Check it out here.
Pagination
If we had to think about how it works, it’s almost the same as Infinite
Fetch a sample of data, say the first 5 elements, on the server side
return <main>...</main>;
"use client";
function Pagination() {
const currentPage = 1 ;
};
Next Level Next.js 12
return (
<button
>
Previous
</button>
<button
>
Next
</button>
</div>
</div>
);
(You can get the actual styles in the full source code)
Yes, there are event handlers and interactions occurring that rely on the
user's browser functions, so it must be a 'client' component.
return (
<main>
...
<Pagination />
</div>
</main>
);
Now how will we manage which page the user has requested?
Yes, no need for any kind of React State or Context API that will be
responsible for holding the paginated page value.
And this isn’t something new in Next.js, FYI. This method has been there
for a long time but it took a new version release of Next.js to make us
aware of it.
Visit your favorite e-commerce site, for example, Amazon, and see what
happens when you search for something or request a new page from
pagination. Keep an eye on “URL” while you’re doing this.
"use client";
params: searchParams.toString(),
key: "page",
value,
});
router.push(newUrl);
};
Make sense?
Next Level Next.js 15
And finally, we’ll consume/use this URL page value on our Home page,
which is going to be handling the fetch
const MAX_ITEMS = 5;
`http://localhost:8000/tasks?_start=${(page - 1) * MAX_ITEMS}&_end=${
page * MAX_ITEMS
}`
);
return (
....
</main>
);
A ask for you — How should we decide if there is a Next page or not?
What will be the logic? THINK!
You can see the full source code here. Feel free to give it a go!
How can we make cool page animations in Next.js without losing its
server-side powers?
Yes, doing animations like that would mean we have to do some client
stuff but that doesn't mean that we have to make everything “client”
code and just give up on Next.js server-side capabilities. One just has to
do smart thinking.
At the time when Next.js was rolling out its different features, from
If we visit the website and see what that is, you’ll read this:
page. Unlike layouts that persist across routes and maintain state,
on navigation”.
What that means is when a user navigates between routes that share
a template, a new instance of the component is mounted, DOM
elements are recreated, state is not preserved, and effects are re-
synchronized. (Reference from Next.js Template)
Next Level Next.js 17
"use client";
return (
<section className="overflow-hidden">
<div
}`}
>
{children}
</div>
</section>
);
And if we place this inside the root of the folder, it’ll apply the animation
for all kinds of page routes you’ll create
Next Level Next.js 19
That’s it. If you want to see how these animations will work, check out
the full source code here
But that’s pure CSS. How can one implement Framer Motion animations
in Next.js?
Next Level Next.js 20
"use client";
const variants = {
hidden: { x: "100%" },
enter: { x: 0 },
exit: { x: "-100%" },
};
return (
<motion.main
variants={variants}
initial="hidden"
animate="enter"
exit="exit"
transition={transition}
>
{children}
</motion.main>
);
And now you’re free to do any kind of server side logic you want to
Do note that, in the above code, we’re applying page transitions to all
pages as we have kept the template file in the root of the app folder. A
root Template.
But you can create a specific template for a specific bunch of routes
GitHub Link
Next Level Next.js 22
Not clear enough, but it does say that we’re trying something with
Why?
its animations and thus we can’t render them on the server side.
We play SMART.
"use client";
Like this,
const variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
Next Level Next.js 24
return (
<MotionDiv
variants={variants}
initial="hidden"
animate="visible"
transition={{
ease: "easeInOut",
duration: 0.6,
}}
key={index}
>
<Image
src={photo.imageUrl}
alt={photo.title}
fill
className="object-cover rounded"
/>
</MotionDiv>
))}
</div>
</main>
);
Nah!
client component:
"use client";
return (
<>
<ServerComponent />
</>
);
"use client";
return (
<>
{children}
</>
);
You may have seen a common example of it in the Next.js layout way
often:
import "./globals.css";
};
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
Next Level Next.js 27
Although it’s not a client component, it’s a common pattern in React for
passing props.
In the above ClientComponent example, it’ll not know what {children} is,
<Client>
<ServerComponent />
</Client>
But that’s the only way of creating and exporting motion elements. You
"use client";
return (
<motion.div
key={index}
>
{children}
</motion.div>
);
};
const variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
return (
</AnimatedDiv>
))}
</div>
</main>
);
API
concept
<ContextProvider>
{children}
</ContextProvider>
How to do it exactly?
component
"use client";
setUser("John Doe");
};
setUser(null);
};
Next Level Next.js 30
return (
{children}
</AuthContext.Provider>
);
};
if (!context) {
return context;
};
Now we can use this AuthProvider inside our global Layout file to wrap
the whole application inside it:
import "./globals.css";
};
Next Level Next.js 31
return (
<html lang="en">
<body className={inter.className}>
<AuthProvider>{children}</AuthProvider>
</body>
</html>
);
To spice things up, let’s render the Home page content in such a way
that if there is a user, it’ll show the list of fetched data from the server
otherwise, it’ll show a login button. How would you go about it?
Create the Home page where we would like to show the login content
initially (as there would be no user)
function Home() {
return (
<Client />
</main>
);
Create the Client component that consumes the context API hook, i.e.,
useAuth
Next Level Next.js 32
"use client";
return (
<div>
<AuthButton />
</div>
</div>
);
"use client";
function AuthButton() {
if (user) {
logoff();
} else {
login();
};
return (
Next Level Next.js 33
return (
<button
onClick={handleAuth}
</button>
);
return (
<AuthButton />
</div>
</h1>
{data.map((tweet) => (
{tweet.author}
<span className='text-gray-500'>
{new Date(tweet.timestamp).toLocaleString()}
Next Level Next.js 34
</span>
</div>
</div>
))}
</div>
</div>
);
Simple,
function Home() {
return (
<Client>
<Server />
</Client>
</main>
);
And then modifying the Client component code to render the children's
content according to the condition:
Next Level Next.js 35
"use client";
return (
<div>
{user ? (
children
) : (
<AuthButton />
</div>
)}
</div>
);
The complete source code of the above example is here. Feel free to
mess around with it!
Search
How to implement proper search functionality using Next.js Server
Side features?
If you think, it’ll be similar to what we did with pagination or even infinite
scroll. It all comes down to the same concept — URL state management.
return (
{data.map((task) => (
<p className="text-gray-600">{task.description}</p>
{task.tag}
</span>
</div>
))}
</div>
</main>
);
Now implement the Search component (Do I have to specify where and
what kind of component it will be?)
"use client";
function Search() {
return (
<input
type="text"
placeholder="Search..."
value={searchTerm}
/>
);
Now import the client component inside our main Server component,
i.e., Home
return (
<Search />
{data.map((task) => (
<p className="text-gray-600">{task.description}</p>
{task.tag}
</span>
</div>
))}
</div>
</main>
);
So now that we have separated the concerns, i.e., client and server, how
do we pass the data from the Search client component to the Home
server component?
Now let’s construct the URL whenever the user types something in the
URL, i.e., /query=${searchTerm}
Next Level Next.js 39
"use client";
function Search() {
useEffect(() => {
if (searchTerm) {
params: searchParams.toString(),
key: "query",
value: searchTerm,
});
} else {
router.push("/");
}, 300);
return (
<input
type="text"
placeholder="Search..."
value={searchTerm}
/>
);
Okay, lots of things are happening above. Let’s tackle them one by one,
Debounce
See the useEffect and we’re doing something with setTimeout. It’s
called debouncing.
Imagine a user typing in a search bar, and the search function triggers
with every keystroke. It will fire too many requests for each keystroke
user will do! How scary and costly it would be!
And here comes Debouncing. With this method, the user waits a
moment before searching. This prevents a flood of unnecessary search
requests for each keystroke and ensures the search is performed when
the user pauses, giving the user more relevant results and saving us
from costing our company.
It's about optimizing the search process for a smoother and more
efficient experience.
In the above code, we’re doing the same. We’re setting some value only
after 0.3s has passed
But what are we setting?
URL State Management
We’re setting the value we’re getting in useState searchTerm inside the
URL.
So if searchTerm is coding, then we’ll create a new URL, i.e., /?
query=coding, and push it using the router method.
Next Level Next.js 41
Whatever the value the user types there, it’ll be added in the URL after
0.3s of time span.
formUrlQuery function is the same as you have seen before in the
pagination or infinite scroll example. It simply makes changes to the URL
and sends a new URL form based on the parameters we sent to it
And that’s what we need. Now that we have set the data in the URL in
the form of search parameters, we can easily access it on any page we
want.
So heading back to the home page, all we have to do is:
import Search from "./Search";
? `http://localhost:8000/tasks?q=${query}`
: "http://localhost:8000/tasks";
return (
<Search />
{data.map((task) => (
<p className="text-gray-600">{task.description}</p>
{task.tag}
<span>
</div>
))}
</div>
</main>
)}
Next Level Next.js 42
That’s it. Get the search param value and pass it to the API.
The API (dummy JSON server) I am using has two separate endpoints.
That’s why you see a condition there, i.e., use the first endpoint only
when there is nothing else in the URL!
As usual, you can see the full source code here. Do test it out!
Thank you!