Next.js Page Components
Welcome to our guide on Next.js Page Components! If you're just getting started with Next.js, understanding how pages work is essential as they form the backbone of your application's routing and structure.
Introduction
In Next.js, page components are special React components that represent routes in your application. What makes Next.js unique is its file-system based routing system - simply put, the files you create in the pages
directory automatically become routes in your application.
This approach simplifies the development process, making it more intuitive to create and organize the different views of your application without having to manually configure routing.
Understanding Page Components
Basic Structure
In Next.js, any file you place in the pages
directory becomes a route. The file name determines the path:
// pages/index.js -> Accessible at yourdomain.com/
export default function Home() {
return <h1>Welcome to my homepage!</h1>;
}
// pages/about.js -> Accessible at yourdomain.com/about
export default function About() {
return <h1>About Us</h1>;
}
Page Component Requirements
Every page component must:
- Be a React component
- Be exported as the default export
- Return valid JSX that can be rendered
Creating Your First Page
Let's create a simple homepage:
// pages/index.js
export default function HomePage() {
return (
<div>
<h1>Welcome to My Next.js Website</h1>
<p>This is my first Next.js page component.</p>
</div>
);
}
When you run your Next.js application and navigate to the root URL, you'll see:
Welcome to My Next.js Website
This is my first Next.js page component.
Nested Routes
Creating nested routes is as simple as adding subdirectories in your pages
folder:
// pages/blog/index.js -> Accessible at yourdomain.com/blog
export default function BlogIndex() {
return <h1>All Blog Posts</h1>;
}
// pages/blog/first-post.js -> Accessible at yourdomain.com/blog/first-post
export default function FirstPost() {
return <h1>My First Blog Post</h1>;
}
Dynamic Routes
Next.js supports dynamic routes using brackets notation:
// pages/posts/[id].js
import { useRouter } from 'next/router';
export default function Post() {
const router = useRouter();
const { id } = router.query;
return (
<div>
<h1>Post: {id}</h1>
<p>This is the content of post {id}</p>
</div>
);
}
With this setup, visiting yourdomain.com/posts/1
, yourdomain.com/posts/2
, etc., will all be handled by this single component, with the id
value available through the router.
Page Component Properties
Page components in Next.js receive special properties:
1. Props from Data Fetching
Next.js provides special functions for data fetching like getStaticProps
, getServerSideProps
, and getStaticPaths
:
// pages/products/[id].js
export default function Product({ product }) {
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
</div>
);
}
// This runs at build time in production
export async function getStaticProps({ params }) {
// Fetch product data
const res = await fetch(`https://api.example.com/products/${params.id}`);
const product = await res.json();
return {
props: { product }, // Passed to the page component as props
};
}
export async function getStaticPaths() {
// Get all possible product IDs
const res = await fetch('https://api.example.com/products');
const products = await res.json();
const paths = products.map((product) => ({
params: { id: product.id.toString() },
}));
return { paths, fallback: false };
}
2. Router Object
Every page has access to the Next.js router via the useRouter
hook:
import { useRouter } from 'next/router';
export default function DashboardPage() {
const router = useRouter();
const handleLogout = () => {
// Perform logout logic...
router.push('/login'); // Navigate to login page
};
return (
<div>
<h1>Dashboard</h1>
<button onClick={handleLogout}>Logout</button>
</div>
);
}
Real-world Example: Blog Post Page
Let's create a more complete blog post page that demonstrates several Next.js page component features:
// pages/blog/[slug].js
import { useState } from 'react';
import { useRouter } from 'next/router';
import Head from 'next/head';
import Link from 'next/link';
export default function BlogPost({ post, relatedPosts }) {
const router = useRouter();
const [likes, setLikes] = useState(0);
// If the page is still generating via fallback, show loading
if (router.isFallback) {
return <div>Loading...</div>;
}
return (
<div className="blog-post-container">
<Head>
<title>{post.title} | My Blog</title>
<meta name="description" content={post.excerpt} />
</Head>
<nav>
<Link href="/blog">
← Back to all posts
</Link>
</nav>
<article>
<h1>{post.title}</h1>
<div className="metadata">
<span>Published on {new Date(post.date).toLocaleDateString()}</span>
<span>By {post.author}</span>
</div>
<div className="content" dangerouslySetInnerHTML={{ __html: post.content }} />
<div className="interactions">
<button onClick={() => setLikes(likes + 1)}>
Like ({likes})
</button>
</div>
</article>
<section className="related-posts">
<h2>Related Posts</h2>
<ul>
{relatedPosts.map((relatedPost) => (
<li key={relatedPost.slug}>
<Link href={`/blog/${relatedPost.slug}`}>
{relatedPost.title}
</Link>
</li>
))}
</ul>
</section>
</div>
);
}
export async function getStaticProps({ params }) {
// In a real app, you would fetch from an API or database
const post = await fetchPostBySlug(params.slug);
const relatedPosts = await fetchRelatedPosts(params.slug);
return {
props: {
post,
relatedPosts,
},
// Re-generate the page at most once every 10 minutes
revalidate: 600,
};
}
export async function getStaticPaths() {
// Get all post slugs for pre-rendering
const posts = await fetchAllPosts();
const paths = posts.map((post) => ({
params: { slug: post.slug },
}));
return {
paths,
// Generate new pages on-demand if the slug doesn't exist
fallback: true,
};
}
// Mock API functions (in a real app these would call your actual API)
async function fetchPostBySlug(slug) {
// Simulating API delay
await new Promise(resolve => setTimeout(resolve, 50));
return {
title: `Post about ${slug}`,
slug,
date: new Date().toISOString(),
author: 'John Doe',
content: `<p>This is a detailed article about ${slug}.</p><p>It contains multiple paragraphs of information.</p>`,
excerpt: `Learn all about ${slug} in this comprehensive guide.`,
};
}
async function fetchRelatedPosts(currentSlug) {
// Simulating API delay
await new Promise(resolve => setTimeout(resolve, 50));
return [
{ slug: 'nextjs-basics', title: 'Next.js Basics' },
{ slug: 'react-hooks', title: 'Understanding React Hooks' },
{ slug: 'css-grid', title: 'CSS Grid Layout' },
].filter(post => post.slug !== currentSlug);
}
async function fetchAllPosts() {
// Simulating API delay
await new Promise(resolve => setTimeout(resolve, 50));
return [
{ slug: 'nextjs-basics', title: 'Next.js Basics' },
{ slug: 'react-hooks', title: 'Understanding React Hooks' },
{ slug: 'css-grid', title: 'CSS Grid Layout' },
];
}
This blog post page demonstrates:
- Dynamic routing with
[slug].js
- Head management with
next/head
- Data fetching with
getStaticProps
andgetStaticPaths
- Incremental Static Regeneration with
revalidate
- Client-side state with React hooks
- Navigation with
next/link
- Loading states with
router.isFallback
Special Pages in Next.js
Next.js offers some special page files that serve specific purposes:
1. Custom App (_app.js
)
This file allows you to override the default App component used by Next.js:
// pages/_app.js
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return (
<>
<header>My Website Header</header>
<Component {...pageProps} />
<footer>My Website Footer</footer>
</>
);
}
export default MyApp;
2. Custom Document (_document.js
)
This file allows you to customize the document structure:
// pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document';
export default function MyDocument() {
return (
<Html lang="en">
<Head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
3. Error Pages (404.js
and 500.js
)
Create custom error pages by adding these files:
// pages/404.js
export default function Custom404() {
return <h1>404 - Page Not Found</h1>;
}
// pages/500.js
export default function Custom500() {
return <h1>500 - Server-side Error Occurred</h1>;
}
Best Practices for Page Components
-
Keep pages thin: Pages should primarily handle routing and data fetching. Move complex UI logic to separate components.
-
Use appropriate data fetching: Choose the right method between
getStaticProps
,getServerSideProps
, and client-side fetching based on your needs. -
Optimize metadata: Each page should have proper metadata using the
Head
component fromnext/head
. -
Implement progressive enhancement: Make sure your pages work without JavaScript enabled (where feasible).
-
Handle loading and error states: Always provide feedback when pages are loading or when errors occur.
// Good pattern for handling different states
export default function ProductPage({ product, isLoading, error }) {
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!product) return <div>Product not found</div>;
return (
<div>
<h1>{product.name}</h1>
{/* Rest of the component */}
</div>
);
}
Summary
Page components are the heart of Next.js applications, providing:
- File-system based routing
- Built-in support for dynamic routes
- Powerful data fetching capabilities
- Automatic code splitting
- Simplified routing without complex configuration
Understanding page components is essential for building efficient and well-structured Next.js applications. By following the patterns and best practices we've covered, you'll be able to create robust, performant web applications.
Exercises
- Create a basic Next.js application with a home page, about page, and contact page.
- Build a blog with dynamic routes for individual posts.
- Implement a product catalog with categories and product detail pages.
- Add a custom 404 page with a search feature to help users find what they're looking for.
- Create a page that uses
getServerSideProps
to display personalized content based on query parameters.
Additional Resources
- Next.js Official Documentation: Pages
- Next.js Data Fetching Documentation
- Learn Next.js - Official Tutorial
- Vercel's Next.js Examples Repository
Happy coding with Next.js page components!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)