Next.js Pages Concept
Next.js introduces a powerful file-based routing system through its Pages concept, which simplifies how you structure and navigate through your web application. Unlike traditional React applications where routing requires additional libraries and configuration, Next.js provides an intuitive way to create routes based on your file structure.
Introduction to Pages in Next.js
In Next.js, a page is a React Component exported from a file in the pages
directory. Each page is associated with a route based on its filename. This approach significantly reduces the complexity of setting up routes and allows you to focus on building your application's functionality.
Key Benefits of Next.js Pages
- Simple File-Based Routing: Create new routes by adding files to the
pages
directory - Automatic Code Splitting: Each page loads only the necessary JavaScript
- Built-in Server-Side Rendering: Improves performance and SEO
- API Routes Support: Create API endpoints as part of your Next.js application
- Support for Dynamic Routes: Create pages that can match variable paths
Basic Page Structure
Let's start by understanding how to create a basic page in Next.js:
// pages/index.js - this becomes your home route (/)
import React from 'react'
export default function HomePage() {
return (
<div>
<h1>Welcome to my Next.js Website</h1>
<p>This is the homepage</p>
</div>
)
}
By creating this file, Next.js automatically makes it available at the root URL of your website. No additional configuration is required!
Creating Multiple Pages
Adding more pages is as simple as creating additional files in the pages
directory:
// pages/about.js - this becomes the /about route
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our company and mission.</p>
</div>
)
}
// pages/contact.js - this becomes the /contact route
export default function ContactPage() {
return (
<div>
<h1>Contact Us</h1>
<p>Get in touch with our team.</p>
</div>
)
}
Now your application has three routes:
/
- Homepage/about
- About page/contact
- Contact page
Nested Routes
You can create nested routes by adding folders inside the pages
directory:
// pages/blog/index.js - becomes /blog
export default function BlogIndexPage() {
return (
<div>
<h1>Blog Posts</h1>
<p>Read our latest articles</p>
</div>
)
}
// pages/blog/first-post.js - becomes /blog/first-post
export default function FirstPostPage() {
return (
<div>
<h1>My First Blog Post</h1>
<p>This is the content of my first blog post.</p>
</div>
)
}
Dynamic Routes
One of the most powerful features of Next.js pages is the ability to create dynamic routes. These are pages that can handle variable paths. Dynamic segments are denoted by square brackets []
in the filename.
// pages/posts/[id].js - matches /posts/1, /posts/2, etc.
import { useRouter } from 'next/router'
export default function PostPage() {
const router = useRouter()
const { id } = router.query
return (
<div>
<h1>Post #{id}</h1>
<p>This is the post with ID: {id}</p>
</div>
)
}
When a user navigates to /posts/123
, the component will render with id
equal to "123"
. You can access this parameter using the useRouter
hook from Next.js.
Catch-All Routes
For even more flexible routing, Next.js provides catch-all routes using the spread syntax ([...param]
):
// pages/docs/[...slug].js - matches /docs/a, /docs/a/b, /docs/a/b/c, etc.
import { useRouter } from 'next/router'
export default function DocsPage() {
const router = useRouter()
const { slug } = router.query
// slug will be an array of path segments
return (
<div>
<h1>Documentation</h1>
<p>Current path: {slug ? slug.join('/') : 'loading...'}</p>
</div>
)
}
Navigating Between Pages
Next.js provides the Link
component to enable client-side navigation:
import Link from 'next/link'
export default function NavigationExample() {
return (
<nav>
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About</Link>
</li>
<li>
<Link href="/blog">Blog</Link>
</li>
<li>
<Link href="/posts/[id]" as="/posts/first-post">
First Post
</Link>
</li>
</ul>
</nav>
)
}
Using the Link
component instead of regular <a>
tags enables fast client-side transitions without full page reloads.
Pre-rendering Pages
Next.js gives you two forms of pre-rendering:
- Static Generation (Recommended): HTML is generated at build time and reused for each request.
- Server-side Rendering: HTML is generated on each request.
Static Generation with Data
For pages that need data at build time:
// pages/products.js
export default function Products({ products }) {
return (
<div>
<h1>Products</h1>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
)
}
// This function runs at build time
export async function getStaticProps() {
// Fetch data from an API
const res = await fetch('https://api.example.com/products')
const products = await res.json()
// Pass data to the page via props
return {
props: {
products
}
}
}
Server-side Rendering
For pages that need to render content that's updated on every request:
// pages/dashboard.js
export default function Dashboard({ userData }) {
return (
<div>
<h1>Dashboard</h1>
<p>Welcome back, {userData.name}</p>
<p>Latest activity: {userData.lastActive}</p>
</div>
)
}
// This function runs on every request
export async function getServerSideProps() {
// Fetch data from an API
const userData = await fetchUserData()
// Pass data to the page via props
return {
props: {
userData
}
}
}
Creating API Routes
Next.js allows you to create API endpoints within your application by adding files to the pages/api
directory:
// pages/api/hello.js
export default function handler(req, res) {
// req is an instance of http.IncomingMessage
// res is an instance of http.ServerResponse
res.status(200).json({ message: 'Hello from the API!' })
}
This creates an API endpoint at /api/hello
that returns a JSON response.
Real-world Example: Building a Blog
Let's put everything together with a more complete example of a simple blog:
// pages/index.js
import Link from 'next/link'
export default function Home({ posts }) {
return (
<div>
<h1>My Developer Blog</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>
{post.title}
</Link>
</li>
))}
</ul>
</div>
)
}
export async function getStaticProps() {
// This would typically come from a CMS or database
const posts = [
{ id: 1, title: 'Getting Started with Next.js', slug: 'getting-started-nextjs' },
{ id: 2, title: 'Why I Love React Hooks', slug: 'react-hooks-love' },
{ id: 3, title: 'Building a Portfolio with Next.js', slug: 'nextjs-portfolio' },
]
return {
props: {
posts
}
}
}
// pages/blog/[slug].js
import { useRouter } from 'next/router'
import Link from 'next/link'
export default function BlogPost({ post }) {
const router = useRouter()
// If the page is still being generated, show a loading state
if (router.isFallback) {
return <div>Loading...</div>
}
return (
<article>
<h1>{post.title}</h1>
<p>Published on {post.date}</p>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
<p>
<Link href="/">
Back to home
</Link>
</p>
</article>
)
}
export async function getStaticPaths() {
// Specify which paths to pre-render
const posts = [
{ id: 1, slug: 'getting-started-nextjs' },
{ id: 2, slug: 'react-hooks-love' },
]
const paths = posts.map((post) => ({
params: { slug: post.slug }
}))
return { paths, fallback: true }
}
export async function getStaticProps({ params }) {
// Fetch post data based on slug
// In a real app, this would come from a CMS or API
const post = {
title: `Post about ${params.slug}`,
date: '2023-04-15',
content: `<p>This is the content for ${params.slug}</p><p>It supports HTML content.</p>`,
}
return {
props: {
post
}
}
}
Special Pages
Next.js also supports special pages with specific behaviors:
Custom App Component (_app.js
)
// pages/_app.js
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return (
<div className="app-container">
<header>My Website Header</header>
<main>
<Component {...pageProps} />
</main>
<footer>© 2023 My Website</footer>
</div>
)
}
export default MyApp
This custom App component is used to:
- Persist layout between page changes
- Maintain state across pages
- Add global styles
- Handle global error boundaries
Custom Document Component (_document.js
)
// pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
The custom Document component allows you to modify the initial HTML document that gets sent to the client.
Error Page (_error.js
)
// pages/_error.js
function Error({ statusCode }) {
return (
<div>
<h1>Error {statusCode}</h1>
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: 'An error occurred on client'}
</p>
</div>
)
}
Error.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404
return { statusCode }
}
export default Error
This page is shown when server-side errors occur.
Custom 404 Page (404.js
)
// pages/404.js
export default function Custom404() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>Sorry, the page you are looking for does not exist.</p>
</div>
)
}
This page is shown when a page is not found.
Summary
Next.js Pages concept provides a powerful, yet intuitive way to structure your application:
- Routes are automatically created based on files in the
pages
directory - Dynamic routes allow for flexible URL patterns using
[param]
syntax - Pre-rendering options (Static Generation and Server-side Rendering) improve performance
- API routes enable backend functionality within your Next.js application
- Special pages like
_app.js
and_document.js
provide application-wide customization
The Pages concept is fundamental to understanding Next.js, as it represents one of the framework's key advantages over standard React applications. By leveraging this file-based routing system, you can build complex web applications with clean architecture and improved performance.
Additional Resources
Exercises
- Create a simple Next.js application with a homepage, about page, and contact page
- Implement a blog with dynamic routes for individual posts
- Add an API route that returns a list of users
- Create a custom 404 page with a link back to the homepage
- Build a product catalog with categories (nested routes) and individual product pages (dynamic routes)
By practicing these exercises, you'll gain a solid understanding of the Next.js Pages concept and be well-equipped to build modern web applications.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)