Next.js(nextjs.org) is one of the most popular React-based frameworks for building modern websites and web applications. It provides developers with features such as routing, server-side rendering, static page generation, API development, image optimization, font management, data caching, and SEO optimization.
In a standard React project, you often need to install and configure several libraries and tools separately to implement these features. Next.js provides many of these capabilities out of the box, allowing you to focus more on user interface design, application logic, and product development.
According to the official documentation, Next.js is a React framework for building full-stack applications. It also automatically configures lower-level tools such as the bundler and compiler.
In this article, we will cover:
Next.js is an open-source framework built on React. It is used to create fast, dynamic, and scalable websites and web applications.
React is primarily a library for building user interfaces. It does not define how routing, data fetching, server-side rendering, API development, metadata management, or image optimization should be handled. Next.js, on the other hand, provides a complete structure for developing an application.
With Next.js, you can manage the user interface and part of the server-side logic in a single codebase. This is why Next.js is commonly described as a full-stack React framework.
The most important Next.js features include:
robots.txt managementThe official Next.js documentation introduces two routing systems: App Router, which is the newer system, and Pages Router, which is the older system but is still supported.
React and Next.js are not competitors. Next.js is built on top of React.
In React, you create user interface components. Next.js keeps the same React component model while adding more features for building a complete application.
| Feature | React | Next.js |
|---|---|---|
| Tool type | User interface library | React framework |
| Routing | Requires an external library | Built-in and file-system-based |
| Server-side rendering | Requires separate configuration | Built-in support |
| Static page generation | Requires additional tools | Built-in support |
| API development | Not directly included | Route Handlers and API Routes |
| Metadata management | Manual or library-based | Metadata API |
| Image optimization | Not built in | Image component |
| Font optimization | Not built in | next/font module |
| Full-stack development | Usually requires a separate backend | Possible for many projects |
You still need to understand React to build user interfaces with Next.js. HTML, CSS, JavaScript, and the core concepts of React are suitable prerequisites for learning Next.js. The official documentation also recommends familiarity with these technologies.
Next.js divides application code into two main categories:
This separation can reduce the amount of JavaScript sent to the browser. It also allows tasks such as reading data from a database, using secret keys, and generating HTML to be performed on the server.
In App Router, pages and layouts are Server Components by default. When you need state, events such as onClick, the useEffect hook, or browser APIs such as window and localStorage, you must use a Client Component.
During the build process, Next.js compiles and optimizes the code, generates pages that can be pre-rendered, and prepares the files required for deployment.
App Router is the newer Next.js routing system and uses the app directory. It is built around features such as React Server Components, nested layouts, Streaming, error handling, and loading states.
App Router is generally recommended for new projects. Pages Router is still supported, but the official documentation recommends App Router for access to newer React capabilities.
A simple App Router project structure may look like this:
my-app/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ ├── globals.css
│ ├── about/
│ │ └── page.tsx
│ ├── blog/
│ │ ├── page.tsx
│ │ └── [slug]/
│ │ └── page.tsx
│ └── api/
│ └── posts/
│ └── route.ts
├── public/
├── next.config.ts
├── package.json
└── tsconfig.json
In this structure:
app/page.tsx is the home page.app/layout.tsx defines the main application layout.app/about/page.tsx creates the /about route.[slug] directory is used for dynamic routes.route.ts is used to build an API endpoint.public directory stores public files such as images.Pages Router is the older Next.js routing system and uses the pages directory.
For example:
pages/
├── index.tsx
├── about.tsx
├── blog/
│ └── [slug].tsx
└── api/
└── posts.ts
In Pages Router, every file inside the pages directory becomes a route. Data is commonly fetched with functions such as:
getStaticProps
getServerSideProps
getStaticPaths
Pages Router is still supported and maintained in current versions of Next.js, so older projects do not need to migrate immediately. However, App Router is usually the better starting point for a new project.
Before starting a Next.js tutorial, it is helpful to understand:
useState and useEffectYou do not need to be a React expert to begin. However, understanding Server Components, Client Components, Props, State, and form handling will be difficult without basic React knowledge.
The fastest way to create a Next.js project is to use create-next-app. This tool automatically creates the project structure and installs the required dependencies.
First, make sure Node.js is installed on your system. Then run:
npx create-next-app@latest my-next-app
Enter the project directory:
cd my-next-app
Start the development server:
npm run dev
Open the following address in your browser:
http://localhost:3000
The main project commands are:
npm run dev
npm run build
npm run start
npm run lint
Each command has a different purpose:
npm run dev runs the project in development mode.npm run build creates the production build.npm run start runs the generated production build.npm run lint checks the code for quality and linting issues.The current recommended create-next-app setup can enable features such as TypeScript, ESLint, Tailwind CSS, and App Router. In recent versions, Turbopack is also used as the default development bundler.
Open app/page.tsx and add the following code:
export default function HomePage() {
return (
<main>
<h1>Next.js Tutorial</h1>
<p>My first project was built with Next.js.</p>
</main>
)
}
The page.tsx file must default-export a React component.
To create an About page, create the following directory and file:
app/about/page.tsx
Add this content:
export default function AboutPage() {
return (
<main>
<h1>About Us</h1>
<p>This page was created with App Router.</p>
</main>
)
}
The new page will be displayed when you open /about.
Next.js routing is based on the file structure. In other words, the directories and files inside app determine the URLs through which pages are available.
A layout is a shared user interface used across multiple pages. For example, you can place the site header, main navigation, and footer inside a layout so they do not need to be repeated on every page.
The root layout is located at:
app/layout.tsx
Example:
import type { Metadata } from 'next'
import './globals.css'
export const metadata: Metadata = {
title: 'Next.js Tutorial',
description: 'An educational website built with Next.js',
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body>
<header>Website Header</header>
{children}
<footer>Website Footer</footer>
</body>
</html>
)
}
The children variable displays the content of the current page.
You can create separate layouts for different parts of the application. For example, an admin dashboard can use a different layout from the public website.
To create a dynamic page, place the directory name inside square brackets.
For example:
app/blog/[slug]/page.tsx
This route can handle URLs such as:
/blog/learn-next-js
/blog/react-tutorial
/blog/web-development
Example dynamic page:
type PageProps = {
params: Promise<{
slug: string
}>
}
export default async function BlogPostPage({ params }: PageProps) {
const { slug } = await params
return (
<main>
<h1>Article: {slug}</h1>
</main>
)
}
The slug value can be used to retrieve an article from a database, a file, or a content management system.
Use the Link component to create links between internal pages:
import Link from 'next/link'
export default function Navigation() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/about">About</Link>
<Link href="/blog">Blog</Link>
</nav>
)
}
The Link component enables client-side navigation. Next.js can also prefetch routes that appear in the user’s viewport, helping page transitions feel faster.
In App Router, components are Server Components by default.
A Server Component runs on the server and is suitable for:
Example:
async function getPosts() {
const response = await fetch('https://example.com/api/posts')
if (!response.ok) {
throw new Error('Failed to fetch posts')
}
return response.json()
}
export default async function PostsPage() {
const posts = await getPosts()
return (
<main>
<h1>Posts</h1>
<ul>
{posts.map((post: { id: number; title: string }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}
Server Components are not added to the client-side JavaScript bundle and can fetch data directly on the server. However, they cannot use State, browser events, or APIs such as window.
Whenever you need user interaction, State, event handlers, or browser APIs, you must use a Client Component.
To turn a file into a Client Component, add this directive at the beginning:
'use client'
Counter example:
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<section>
<p>Click count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increase
</button>
</section>
)
}
Client Components are suitable for:
useStateuseEffectlocalStoragewindow and documentOnly make components client-side when interaction is actually required. Turning an entire page into a Client Component can increase the amount of JavaScript sent to the browser.
One of the most important advantages of Next.js is its support for multiple rendering methods.
With static rendering, page content is generated before the user requests it. This method is suitable for:
Static pages are usually very fast and can easily be delivered through a CDN.
With dynamic rendering, content is generated on the server when a user makes a request.
This method is suitable for:
SSR stands for Server-Side Rendering. In this method, the page HTML is generated on the server and then sent to the browser.
SSR can be useful for pages whose data changes with every request. However, using dynamic rendering unnecessarily can increase the load on your server.
SSG stands for Static Site Generation. In this method, page HTML is generated during the build process.
SSG is suitable for content that changes infrequently. High performance and easy CDN caching are among its main advantages.
ISR stands for Incremental Static Regeneration. It allows static content to be updated without rebuilding the entire project.
With newer Next.js caching models, content can be revalidated after a specific period or after a particular update occurs. For example, after publishing a new article, the blog page cache can be cleared or refreshed.
Streaming allows completed parts of a page to be sent to the user immediately, while sections that depend on slower data are displayed later.
You can use a loading.tsx file or the Suspense component for this purpose. In App Router, Streaming allows users to see the initial page structure without waiting for every piece of data to become available.
Inside a Server Component, you can use fetch, an ORM, or a database directly.
Example:
type Product = {
id: number
title: string
price: number
}
async function getProducts(): Promise<Product[]> {
const response = await fetch('https://example.com/api/products')
if (!response.ok) {
throw new Error('Failed to fetch products')
}
return response.json()
}
export default async function ProductsPage() {
const products = await getProducts()
return (
<main>
<h1>Products</h1>
{products.map((product) => (
<article key={product.id}>
<h2>{product.title}</h2>
<p>${product.price.toLocaleString('en-US')}</p>
</article>
))}
</main>
)
}
According to the current documentation, fetch requests are not permanently cached by default. To cache data, you must explicitly define the desired behavior or use Cache Components. This differs from the behavior described in some older Next.js tutorials.
Caching is one of the most powerful, but also one of the more complex, parts of Next.js.
The purpose of caching is to reuse data or rendered output without repeating the same request. This can improve application performance and reduce pressure on APIs and databases.
In the newer Cache Components model, you first enable the feature in the configuration file:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
You can then cache a function:
import { cacheLife } from 'next/cache'
export async function getProducts() {
'use cache'
cacheLife('hours')
const response = await fetch('https://example.com/api/products')
if (!response.ok) {
throw new Error('Failed to fetch products')
}
return response.json()
}
Next.js provides several tools for refreshing cached content:
revalidatePath
revalidateTag
updateTag
cacheLife
cacheTag
The two main revalidation methods are:
For example, after editing a product, you can refresh the cache associated with the products tag.
Cache Components are optional and allow static, cached, and dynamic content to be combined within the same route.
The loading.tsx file is used to display a loading state.
Structure:
app/products/loading.tsx
Example:
export default function Loading() {
return (
<div role="status">
Loading products...
</div>
)
}
This interface is displayed while the products page is being prepared.
For a better user experience, use a Skeleton that resembles the final page layout instead of displaying only a simple loading message.
You can create an error.tsx file to handle unexpected errors within a route.
'use client'
import { useEffect } from 'react'
type ErrorPageProps = {
error: Error & {
digest?: string
}
unstable_retry: () => void
}
export default function ErrorPage({
error,
unstable_retry,
}: ErrorPageProps) {
useEffect(() => {
console.error(error)
}, [error])
return (
<section>
<h2>Something went wrong</h2>
<button onClick={unstable_retry}>
Try Again
</button>
</section>
)
}
The error.tsx file behaves like an Error Boundary. Instead of allowing the entire application to fail, it displays a fallback interface.
For a custom 404 page, create:
app/not-found.tsx
Example:
import Link from 'next/link'
export default function NotFoundPage() {
return (
<main>
<h1>Page Not Found</h1>
<Link href="/">Return to Home</Link>
</main>
)
}
Next.js provides separate tools for handling expected errors, missing pages, unexpected exceptions, and nested Error Boundaries.
A Server Action is a function that runs on the server and can be connected directly to a form or a Client Component.
Server Actions are useful for:
Example:
'use server'
import { revalidatePath } from 'next/cache'
export async function createMessage(formData: FormData) {
const name = String(formData.get('name') ?? '')
const message = String(formData.get('message') ?? '')
if (!name.trim() || !message.trim()) {
return {
success: false,
message: 'Please complete all fields.',
}
}
// Save the data to the database
revalidatePath('/messages')
return {
success: true,
message: 'Your message was submitted successfully.',
}
}
Using the action in a form:
import { createMessage } from './actions'
export default function ContactForm() {
return (
<form action={createMessage}>
<input name="name" placeholder="Name" required />
<textarea name="message" placeholder="Message" required />
<button type="submit">Send Message</button>
</form>
)
}
Server Actions are integrated with the Next.js caching architecture. After changing data, they can revalidate a related route or tag.
All data received by a Server Action must be validated. Authentication and authorization must also be checked in server-side code. Hiding a button in the user interface does not provide security by itself.
In App Router, APIs are built with Route Handlers.
Create the following file:
app/api/posts/route.ts
GET example:
export async function GET() {
const posts = [
{
id: 1,
title: 'Next.js Tutorial',
},
{
id: 2,
title: 'React Tutorial',
},
]
return Response.json(posts)
}
POST example:
export async function POST(request: Request) {
const body = await request.json()
if (!body.title) {
return Response.json(
{
message: 'A title is required.',
},
{
status: 400,
},
)
}
return Response.json(
{
id: 3,
title: body.title,
},
{
status: 201,
},
)
}
Route Handlers support common HTTP methods such as GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. A Route Handler is defined inside the app directory and serves a role similar to API Routes in Pages Router.
Next.js can cover a large portion of the backend requirements of small and medium-sized projects. For example, you can use it to:
However, Next.js is not always a complete replacement for a separate backend.
For projects with heavy processing, complex queues, microservice architecture, long-running connections, or multiple API consumers, a separate backend built with Node.js, NestJS, Laravel, Django, or another technology may be more appropriate.
In many projects, Next.js acts as a Backend for Frontend. This means that APIs and server-side logic are designed specifically for the needs of the user interface. The official documentation also provides a dedicated guide for using Next.js as a Backend for Frontend.
Next.js supports several styling approaches:
The official documentation lists all of these approaches as supported options.
Create the following file:
app/components/button.module.css
Content:
.button {
padding: 12px 20px;
border: 0;
border-radius: 8px;
cursor: pointer;
font-weight: 700;
}
Component:
import styles from './button.module.css'
export default function Button() {
return (
<button className={styles.button}>
Learn More
</button>
)
}
CSS Modules scope class names locally, reducing the chance of style conflicts between components.
The Image component provides more features than a standard HTML img element.
Example:
import Image from 'next/image'
export default function HeroImage() {
return (
<Image
src="/images/next-js-course.jpg"
alt="Complete Next.js course"
width={1200}
height={630}
priority
/>
)
}
Important next/image features include:
According to the official documentation, the Image component can serve correctly sized images, prevent layout shifts, and lazy-load images outside the user’s viewport.
For SEO, always use descriptive and relevant alt text. Avoid unnaturally repeating the primary keyword in every image description.
The next/font module is used to load and optimize fonts.
Example using a local font:
import localFont from 'next/font/local'
const customFont = localFont({
src: './fonts/CustomFont.woff2',
display: 'swap',
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={customFont.className}>
{children}
</body>
</html>
)
}
The next/font module serves fonts in an optimized and self-hosted way, reducing external font requests. This can improve privacy, performance, and layout stability.
One of the main reasons developers choose Next.js is its strong support for technical SEO.
However, installing Next.js alone does not guarantee search engine rankings. Content quality, internal linking, performance, user experience, domain authority, site structure, and satisfying search intent are still essential.
Next.js provides technical tools for managing:
robots.txtsitemap.xmlThe Metadata API is specifically designed for adding titles, descriptions, SEO information, and social sharing metadata.
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'What Is Next.js? A Complete Next.js Tutorial',
description:
'Learn about Next.js, App Router, rendering, SEO, and project development in this complete guide.',
alternates: {
canonical: 'https://example.com/next-js',
},
openGraph: {
title: 'Complete Next.js Tutorial',
description:
'A complete guide to learning and using Next.js',
url: 'https://example.com/next-js',
type: 'article',
locale: 'en_US',
},
}
For article and product pages, you can use generateMetadata:
import type { Metadata } from 'next'
type PageProps = {
params: Promise<{
slug: string
}>
}
export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
return {
title: post.seoTitle || post.title,
description: post.seoDescription || post.excerpt,
alternates: {
canonical: `https://example.com/blog/${post.slug}`,
},
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
images: [post.image],
},
}
}
The generateMetadata function and the metadata object can only be used in Server Components.
Create the following file:
app/sitemap.ts
Example:
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://example.com',
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1,
},
{
url: 'https://example.com/next-js',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
]
}
The sitemap.ts file allows Next.js to generate a standard sitemap that helps search engines crawl and index your pages.
Create:
app/robots.ts
Example:
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/admin/', '/api/'],
},
sitemap: 'https://example.com/sitemap.xml',
}
}
The robots.ts file defines which routes search engine crawlers are allowed to access.
You can add Article schema to a blog post:
const articleSchema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: 'What Is Next.js?',
description: 'A complete Next.js tutorial from zero to deployment',
author: {
'@type': 'Person',
name: 'Author Name',
},
datePublished: '2026-07-22',
dateModified: '2026-07-22',
}
export default function ArticlePage() {
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(articleSchema).replace(
/</g,
'\\u003c',
),
}}
/>
<article>
<h1>What Is Next.js?</h1>
</article>
</>
)
}
The official Next.js documentation provides a separate guide for implementing JSON-LD.
Use the following recommendations to improve the SEO of a Next.js project.
Do not use the same title across all pages. A title should describe the page’s main topic and may include the brand name when appropriate.
Write a dedicated description for each important page. The description should provide an accurate summary of the page content.
Place the page’s primary heading inside an H1 and organize supporting sections with H2 and H3 headings.
Connect related articles using descriptive anchor text.
Instead of:
Click here
Use a more descriptive phrase:
Learn how to build an API in Next.js
Internal search pages, duplicate filters, and test routes may need controlled blocking or a noindex directive.
Use correct dimensions, suitable image formats, and descriptive alternative text.
Do not load the main content of an article or product page only after client-side JavaScript runs. Server Components can include important content in the initial HTML sent to browsers and search engine crawlers.
Using Next.js does not guarantee that a website will always be fast. Heavy scripts, oversized images, unnecessary Client Components, and excessive requests can still reduce performance.
You can build an authentication system using several approaches:
A general authentication flow includes:
Sensitive information and permission checks must be handled on the server. Variables that must remain private should not use the public client-side prefix.
Environment variables are commonly stored in .env.local:
DATABASE_URL="..."
AUTH_SECRET="..."
PAYMENT_API_KEY="..."
NEXT_PUBLIC_SITE_URL="https://example.com"
Variables beginning with NEXT_PUBLIC_ can be included in browser-side code. Therefore, private keys, passwords, and database credentials must never use this prefix.
Example usage in a Server Component:
const databaseUrl = process.env.DATABASE_URL
Do not commit .env.local to a public Git repository.
Next.js can connect to many database systems, including:
You can use an ORM or Query Builder to manage database operations.
Database connection logic must run only on the server. Server Components, Server Actions, and Route Handlers are suitable places for this code.
Example structure:
app/
├── actions.ts
├── api/
├── products/
└── users/
lib/
├── db.ts
├── auth.ts
└── validations.ts
The lib/db.ts file can store the database connection or database client.
Before deployment, create a production build:
npm run build
If the build completes successfully, run it with:
npm run start
Common deployment options include:
To use all dynamic Next.js features, the hosting environment must support Node.js or a compatible adapter. Next.js can also be self-hosted on Node.js, deployed with Docker, or exported as a static site.
For a website that does not require server-side features, you can create a static export:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
output: 'export',
}
export default nextConfig
After the build, Next.js generates static HTML, CSS, and JavaScript files.
Features that require a server runtime, including some Route Handlers, Server Actions, and dynamic rendering capabilities, cannot be used in this mode.
The ability to render HTML, manage metadata, generate sitemaps and robots.txt, and add structured data makes Next.js suitable for content-focused projects.
Server-side rendering, static generation, Streaming, caching, and image and font optimization can all improve website performance.
You do not need to install a separate routing library to create application routes.
You can manage the user interface, APIs, forms, and part of the server-side logic in a single project.
Next.js includes built-in TypeScript support. When you use .ts or .tsx files, it creates the required configuration.
Fast Refresh, a clear project structure, useful development errors, and built-in tools make the development process more organized.
Different parts of the same project can use static, cached, or dynamic content depending on their requirements.
Server Components, Client Components, caching, Streaming, Server Actions, and rendering strategies can be difficult to understand at first.
Some APIs and recommended development approaches change between major versions. As a result, older tutorials may not match the behavior of newer Next.js versions.
Understanding which data is cached, when it is revalidated, and which sections are dynamic requires experience.
To use dynamic rendering, Server Actions, and other runtime capabilities, you need a compatible hosting environment.
For a very simple page or a fully client-side application, a lighter tool may be sufficient.
Next.js is a strong choice for:
Next.js is particularly valuable when a project combines public pages that require SEO with interactive areas and user dashboards.
You should also consider other options when:
Vite is a fast build tool and development server. Next.js is a complete React framework.
Vite is commonly suitable for:
Next.js is commonly more suitable for:
Neither option is always better. The correct choice depends on the requirements of the project.
You can follow this path to learn Next.js effectively:
create-next-appThe best way to learn is to build a real project, such as a blog, a simple online store, or a task management application.
Yes. Next.js is built on React. You do not need to be a React expert, but you should understand components, Props, State, hooks, and data fetching.
Yes. Next.js provides tools such as server-side rendering, static pages, the Metadata API, sitemaps, robots.txt, and Open Graph metadata. However, SEO success still depends on content quality, site architecture, internal linking, and user experience.
No. Route Handlers, Server Actions, and Server Components allow you to implement server-side logic. However, a separate backend may still be a better choice for complex projects.
App Router is generally the recommended choice for new projects because it supports newer React features such as Server Components. Pages Router is still supported for maintaining older projects.
No. You can use JavaScript, but TypeScript can reduce errors and improve code readability in medium-sized and large projects.
Only when the hosting provider supports Node.js and the requirements of the Next.js version you are using. Otherwise, you must use a static export or deploy the project to a compatible server or platform.
Yes. In App Router, you can create a Route Handler with a route.ts file. Pages Router also supports API Routes.
No. Node.js is a JavaScript runtime for server environments, while Next.js is a framework that can run on Node.js.
Yes. Product pages, metadata management, server-side rendering, caching, APIs, authentication, and payment gateway integration make Next.js suitable for e-commerce websites. However, the architecture should be designed according to the number of products, traffic level, and business requirements.
Next.js is a powerful React-based framework for building modern websites and web applications. It provides routing, server-side rendering, static page generation, Server Components, API development, form handling, caching, image optimization, font management, and SEO tools within an integrated structure.
The most important part of learning Next.js is not simply memorizing commands and filenames. You must understand the difference between code that runs on the server and code that runs in the browser, the available rendering strategies, caching behavior, and the way data moves between components.
For new projects, App Router is a suitable starting point. Begin with a small project, then gradually add features such as a database, authentication, Server Actions, caching, and SEO.
Next.js delivers the best results when its capabilities are used according to the real requirements of a project. Unnecessarily converting components into Client Components, using dynamic rendering without a valid reason, and configuring caching incorrectly can reduce the benefits of the framework.