Week-14.1 Intro To Nextjs
Week-14.1 Intro To Nextjs
Week-14.1 Intro To Nextjs
Week 14.1
In this lecture, Harkirat introduces Next.js, a React framework that simplifies the development of
server-rendered React applications. He highlights the benefits of Next.js over traditional React
applications, such as built-in server-side rendering , API routes , file-based routing , and
optimizations for bundle size and static site generation. Harkirat addresses the waterfalling issue,
explores Next.js offerings, and guides through project setup, file structure , routing , layouts ,
and the use of client components .
Pre-requisites
Next.js Introduction
SEO Optimization
Next.js Solution
Waterfalling Problem
Next.js Solution
https://app.100xdevs.com/pdf/252 1/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
Automatic Optimizations
File Structure
Initial Setup
Routing in Next.js
Server-Side Rendering
Layouts
Nested Layouts
Merging Routes
Pre-requisites
Before diving into Next.js 14, it's essential to have a solid understanding of basic frontend
development concepts and React. Here's what you need to know:
1. HTML, CSS, and JavaScript: Familiarize yourself with the fundamentals of web development,
including HTML for structuring content, CSS for styling, and JavaScript for adding interactivity.
2. React Basics: Learn the core concepts of React, such as components, state, props, and lifecycle
methods. Understand how to create simple React applications and manage component
hierarchies.
function Counter() {
const [count, setCount] = useState(0);
https://app.100xdevs.com/pdf/252 2/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
setCount(count + 1);
};
return (
<div>
<h1>Count: {count}</h1>
<button onClick={incrementCount}>Increment</button>
</div>
);
}
This is a simple React component that demonstrates state management and event handling.
Next.js Introduction
Next.js is a popular React framework that simplifies the development of server-rendered React
applications. It was introduced to address some of the challenges and inconveniences faced in
traditional React development.
2. Routing: React doesn't provide out-of-the-box routing capabilities, requiring the use of third-
party libraries like React Router. Next.js comes with a built-in file-based routing system, making it
easier to manage routes and handle navigation.
3. SEO Optimization: React applications are rendered on the client-side by default, which can
negatively impact search engine optimization (SEO). Next.js supports server-side rendering (SSR)
and static site generation (SSG), ensuring that your application is SEO-friendly and optimized for
search engines.
4. Waterfalling Problem: In traditional React applications, the entire application bundle needs to be
downloaded before the application can start rendering, leading to a "waterfalling" effect. Next.js
addresses this issue by supporting code splitting and lazy loading, allowing for faster initial load
times and improved performance.
https://app.100xdevs.com/pdf/252 3/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
In the next sections, we'll explore these problems and the solutions provided by
Next.js in greater depth, along with code examples and practical
implementations.
SEO Optimization
Search engines like Google and Bing use web crawlers (also known as bots or spiders) to discover
and index websites. These crawlers visit websites and analyze the HTML content to understand what
the website is about and how to rank it in search results.
However, traditional client-side rendered (CSR) React applications pose a challenge for SEO because
the crawlers typically don't execute JavaScript and render the page to see the final output. Instead,
they rely on the initial HTML response received from the server.
When you visit this URL, you'll notice that the initial HTML response doesn't contain any meaningful
content related to the application. It only shows a basic HTML structure with references to Vite, React,
and TypeScript.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<script type="module" crossorigin src="/assets/index.d3f2e1c9.js"></script>
<link rel="stylesheet" href="/assets/index.e1c4b2f0.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
This is because the actual content of the React application is rendered dynamically using JavaScript
after the initial HTML is loaded. While the content will eventually be rendered and visible to users,
https://app.100xdevs.com/pdf/252 4/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
search engine crawlers like Googlebot may have difficulty discovering and indexing this dynamically
generated content effectively.
Next.js Solution
Next.js addresses this SEO challenge by providing two powerful rendering techniques: Server-Side
Rendering (SSR) and Static Site Generation (SSG).
By leveraging SSR and SSG, Next.js provides a robust solution for SEO
optimization, ensuring that your React application's content is easily
discoverable and indexable by search engines like Google and Bing.
https://app.100xdevs.com/pdf/252 5/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
Waterfalling Problem
The waterfalling problem is a common issue in traditional client-side rendered (CSR) React
applications, where data fetching operations are chained or dependent on each other, leading to
inefficient loading behavior. Let's understand this problem with an example of a blogging website
built with React.
1. Fetch the index.html from the CDN: The browser first fetches the initial HTML file, which
typically contains minimal content and a reference to the JavaScript bundle.
2. Fetch the script.js from the CDN: After the initial HTML is loaded, the browser fetches the
JavaScript bundle, which contains the React application code.
3. Check if the user is logged in: Once the JavaScript bundle is loaded and executed, the application
checks if the user is logged in. If the user is not logged in, they are redirected to the /login page.
4. Fetch the actual blog posts: If the user is logged in, the application fetches the actual blog post
data from an API or a database.
https://app.100xdevs.com/pdf/252 6/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
In this scenario, there are four round trips that happen sequentially, one after the other. The browser
has to wait for each step to complete before moving on to the next one, resulting in a "waterfalling"
effect. This can lead to slow initial load times and a poor user experience, especially on slower
network connections.
Next.js Solution
Next.js provides several features and optimizations to address the waterfalling problem and improve
the overall performance and loading behavior of your React application.
Automatic Optimizations
Next.js automatically applies various optimizations to your application, such as:
Image Optimization: Next.js optimizes images by resizing, compressing, and serving them in
modern formats like WebP, reducing the overall page weight and improving load times.
Script Loading Optimization: Next.js automatically optimizes the loading of JavaScript bundles,
ensuring that they are loaded efficiently and in the correct order.
SEO Optimization: Next.js's SSR and SSG capabilities ensure that your application's content is
easily discoverable and indexable by search engines like Google and Bing.
API Routes: Next.js allows you to handle API requests directly within your application, eliminating
the need for a separate backend project.
File-based Routing: Next.js provides a file-based routing system, making it easier to manage
routes and handle navigation without the need for third-party libraries like React Router.
Bundle Size Optimizations: Next.js automatically optimizes the bundle size of your application,
reducing the overall page weight and improving load times.
Maintained by the Vercel Team: Next.js is maintained by the Vercel team, ensuring regular
updates, bug fixes, and new features.
However, it's important to note that Next.js also has some downsides:
Deployment Considerations: Next.js applications can't be distributed via a CDN and always
require a server running to handle server-side rendering, which can be more expensive compared
https://app.100xdevs.com/pdf/252 8/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
npx create-next-app@latest
https://app.100xdevs.com/pdf/252 9/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
File Structure
After the project is created, you'll see the following file structure:
my-next-app/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ └── globals.css
├── node_modules/
├── package.json
├── package-lock.json
├── next.config.mjs
├── postcss.config.js
├── tailwind.config.js
├── .eslintrc.json
https://app.100xdevs.com/pdf/252 10/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
├── tsconfig.json
└── README.md
app/ : This directory contains all your code, components, layouts, routes, and APIs.
Initial Setup
Before we start building our application, let's clean up the initial files:
1. Open app/page.tsx and remove everything inside the Home component. Replace it with an
empty div :
1. Open app/globals.css and remove the CSS styles, leaving only the Tailwind CSS imports:
@tailwind base;
@tailwind components;
@tailwind utilities;
{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": ["./app/*"]
}
}
}
This configuration sets the base URL to the project root and maps the @/ alias to the app/
directory.
https://app.100xdevs.com/pdf/252 11/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
Open your browser and navigate to http://localhost:3000 . You should see an empty page, which is
expected since we removed the initial content from app/page.tsx .
Routing in Next.js
In traditional React applications, routing is typically handled using a third-party library like React
Router. However, Next.js takes a different approach by providing a file-based routing system out of
the box.
Next.js has a built-in file-based router, which means that the way you structure your files and folders
determines how routes are rendered. This approach is more intuitive and easier to reason about
https://app.100xdevs.com/pdf/252 12/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
my-next-app/
└── app/
└── signup/
1. Inside the signup folder, create a new file called page.tsx and add a React component that will
be rendered for the /signup route:
// app/signup/page.tsx
export default function SignUp() {
return (
<div>
<h1>Sign Up</h1>
{/* Add your sign-up form or content here */}
</div>
);
}
1. Start the development server by running the following command in your terminal:
1. Once the development server is running, you can access the /signup route by visiting
http://localhost:3000/signup in your browser.
You should now see the content from the SignUp component rendered on the /signup route.
https://app.100xdevs.com/pdf/252 13/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
By following this file-based routing system, you can easily create new routes by adding folders and
files within the app directory. This approach makes it straightforward to manage and organize your
application's routes without the need for a separate routing library.
https://app.100xdevs.com/pdf/252 14/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
interface LabelledInputType {
label: string;
placeholder: string;
type?: string;
}
https://app.100xdevs.com/pdf/252 15/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
Server-Side Rendering
Next.js provides server-side rendering out of the box, which means that the initial page load is
rendered on the server and sent as a fully rendered HTML page to the client. This approach has
several benefits, including improved SEO and better initial load performance.
Run the following command in your terminal to start the Next.js development server:
https://app.100xdevs.com/pdf/252 16/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
Once the development server is running, open your browser and visit
http://localhost:3000/signup . This will render the SignIn component that we created earlier for
the /signup route.
Right-click on the page and select "View Page Source" (or similar option depending on your
browser). You should see the fully rendered HTML content of the SignIn component, including the
form elements and the associated Tailwind CSS styles.
Notice that the HTML response contains the fully rendered content of the SignIn component,
including the form title, input fields, and the sign-in button. This is the result of server-side rendering.
1. SEO Implications
Now, when search engine crawlers like Googlebot try to scrape your page, they will receive this fully
rendered HTML response, which clearly indicates that this is a sign-in page. This is a significant
advantage over traditional client-side rendered React applications, where the initial HTML response
would be minimal, and the crawlers would have difficulty understanding the content and purpose of
the page.
It's important to note that while SSR provides SEO benefits, it also introduces
some additional complexity and overhead compared to static site generation
(SSG) or client-side rendering (CSR). Next.js provides a flexible approach,
https://app.100xdevs.com/pdf/252 17/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
allowing you to choose the rendering method that best suits your application's
needs.
Layouts
Layouts in Next.js are a powerful feature that allow you to define shared UI components across
multiple routes in your application. They provide a way to wrap child pages or nested layouts with
common elements, such as headers, footers, sidebars, or any other reusable UI components.
https://app.100xdevs.com/pdf/252 18/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
The app directory in a Next.js application must include a root layout.tsx (or layout.js )
file.
This root layout is responsible for defining the <html> and <body> tags, as well as any
globally shared UI components.
It acts as the top-most layout, wrapping all other layouts and pages in your application.
You can create nested layouts by adding layout.tsx files inside specific route segments
(folders).
These nested layouts wrap the child pages or nested layouts within their respective route
segments.
Nested layouts allow you to define UI components that are shared among a subset of
routes in your application.
Layouts wrap the child pages or nested layouts using the children prop.
The children prop is populated with the component of the child page or nested layout
during rendering.
This allows you to surround the child content with your desired UI components, such as
headers, footers, or sidebars.
Layouts can access dynamic route parameters through the params prop.
This prop contains an object with the dynamic route parameters from the root segment
down to the current layout.
You can use these parameters to conditionally render UI components or fetch data based
on the current route.
On navigation, layouts preserve their state and remain interactive, without re-rendering.
Layouts can maintain state across route changes, providing a seamless user experience.
6. Fetch Data:
This allows you to fetch data for shared UI components within your layouts.
https://app.100xdevs.com/pdf/252 19/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
Nested Layouts
Next.js allows you to create nested layouts for specific route segments, enabling you to define shared
UI components for a subset of routes in your application. This feature is particularly useful when you
need to add additional or niche shared UI components for certain routes.
1. Create a folder for the sub-route: First, create a folder for the sub-route where you want to add
the shared UI component. For example, if you want to add a shared UI component for all routes
starting with /signin , create a signin folder inside the app directory.
2. Add a layout.tsx file: Inside the signin folder, create a new file called layout.tsx (or
layout.js for JavaScript). This file will define the layout component for the /signin sub-route.
Here's an example of how you can create a layout for the /signin sub-route with a banner that says
"Login now to get 20% off":
// app/signin/layout.tsx
import React from 'react';
In this example, the SignInLayout component renders a banner with the message "Login now to get
20% off" using Tailwind CSS classes. The children prop is where the child pages or nested layouts
will be rendered.
https://app.100xdevs.com/pdf/252 21/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
You can also nest layouts further by creating additional folders inside the signin folder and adding
layout.tsx files within those folders. This allows you to create even more specific shared UI
components for nested routes.
Merging Routes
Next.js provides two approaches to merge routes and share UI components across multiple routes.
Let's explore both approaches:
https://app.100xdevs.com/pdf/252 22/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
2. Inside the auth folder, create a layout.tsx file and define the shared UI component (e.g., the
banner).
// app/auth/layout.tsx
import React from 'react';
1. Move the signup and signin folders inside the auth folder.
app/
└── auth/
├── signup/
│ └── page.tsx
└── signin/
└── page.tsx
With this structure, the AuthLayout component will wrap both the /signup and /signin routes,
displaying the "Login now to get 20% off" banner at the top.
You can access these routes at:
http://localhost:3000/auth/signup
http://localhost:3000/auth/signin
1. Create a new folder with parentheses around its name, e.g., (shared) , inside the app directory.
https://app.100xdevs.com/pdf/252 23/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
2. Inside the (shared) folder, create a layout.tsx file and define the shared UI component (e.g.,
the banner).
// app/(shared)/layout.tsx
import React from 'react';
1. Move the signup and signin folders inside the app directory.
app/
├── (shared)/
│ └── layout.tsx
├── signup/
│ └── page.tsx
└── signin/
└── page.tsx
With this structure, the SharedLayout component will wrap both the /signup and /signin routes,
displaying the "Login now to get 20% off" banner at the top.
You can access these routes at:
http://localhost:3000/signup
http://localhost:3000/signin
Both approaches allow you to share UI components across multiple routes, but
the choice between them depends on your preference and the organization of
your application's routes. The nested approach (Approach #1) is suitable when
you have a logical parent route that encompasses multiple child routes. The
parenthesized folder approach (Approach #2) is useful when you want to share
UI components across routes that don't have a common parent route.
https://app.100xdevs.com/pdf/252 24/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
Move the sign-in component logic from app/(auth)/signin/page.tsx to this new file.
// components/Signin.tsx
import React from 'react';
interface LabelledInputType {
label: string;
placeholder: string;
type?: string;
}
https://app.100xdevs.com/pdf/252 25/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
>
<div>
<div className="px-10">
<div className="text-3xl font-extrabold">Sign in</div>
</div>
<div className="pt-2">
<LabelledInput label="Username" placeholder="[email protected]" />
<LabelledInput label="Password" type={"password"} placeholder="123456" />
<button
type="button"
className="mt-8 w-full text-white bg-gray-800 focus:ring-4 focus:ring-gray-300
>
Sign in
</button>
</div>
</div>
</a>
</div>
</div>
);
}
// app/(auth)/signin/page.tsx
import { Signin } from '@/components/Signin';
By following this approach, you've separated the sign-in component logic into a reusable component
within the components directory. This promotes code reusability and maintainability, as you can now
easily import and use the Signin component in other parts of your application if needed.
When you add an onClick handler to the sign-in button in the Signin component, you might
encounter an error in the browser console. This error occurs because the Signin component is
rendered on the server-side during the initial page load, and the browser doesn't have access to the
console object on the server.
1. Server-Side Rendering (SSR): When you visit the /signin route, Next.js renders
the Signin component on the server-side, which includes executing the onClick handler and
the console.log statement.
3. Hydration: After the initial server-rendered HTML is sent to the browser, Next.js "hydrates" the
application by attaching event handlers and making it interactive on the client-side.
4. Client-Side Rendering: Once the application is hydrated on the client-side, the onClick handler
and the console.log statement will work as expecte, as the browser environment has access to
the console object.
https://app.100xdevs.com/pdf/252 27/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
https://app.100xdevs.com/pdf/252 28/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
However, there are certain scenarios where you need to create client components, which are
rendered on the client-side (in the browser). You should create client components whenever you
encounter an error related to using browser-specific APIs or when you need to use React hooks or
event handlers that are not supported on the server.
Here are some common scenarios where you should create client components:
1. Using Browser APIs: If you need to use browser APIs like window , document , navigator , or
localStorage , you must create a client component. These APIs are not available on the server-
side, and attempting to use them in a server component will result in an error.
2. Using React Hooks: React hooks like useState , useEffect , useContext , and others are designed
to work in the client-side environment. If you try to use them in a server component, you'll
encounter an error because these hooks rely on browser-specific functionality.
3. Handling User Events: If you need to handle user events like onClick , onSubmit , or onChange ,
you should create a client component. These events are triggered by user interactions in the
browser, and they don't make sense in a server-side environment.
4. Rendering Dynamic Content: If you need to render dynamic content that changes based on user
interactions or browser state, you should create a client component. Server components are
designed to render static content during the initial page load.
https://app.100xdevs.com/pdf/252 29/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
The general rule of thumb is to defer the creation of client components as much as possible and
keep the majority of your application logic in server components. This approach helps optimize
performance by reducing the amount of client-side JavaScript that needs to be downloaded and
executed.
When you encounter an error related to using browser-specific APIs or React hooks in a server
component, Next.js will typically provide a clear error message suggesting that you create a client
component. In such cases, you can add the 'use client' directive at the top of your component file
to mark it as a client component.
Here's an example of how to create a client component:
useEffect(() => {
// This code will run on the client-side
const handleWindowResize = () => {
// Access the window object
console.log('Window resized');
};
window.addEventListener('resize', handleWindowResize);
return () => {
window.removeEventListener('resize', handleWindowResize);
};
}, []);
return (
https://app.100xdevs.com/pdf/252 30/31
8/18/24, 12:10 AM Take your development skills from 0 to 100 and join the 100xdevs community
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
In this example, the ClientComponent uses the useState hook to manage state, the useEffect hook
to handle window resize events, and an onClick event handler. Since these features rely on browser
functionality, the 'use client' directive is added at the top of the file to mark it as a client
component.
https://app.100xdevs.com/pdf/252 31/31