Lesson 10-Next.js Page Routing-Rendering

Server-Side Rendering (SSR)

Server-Side Rendering (SSR)

Using the getServerSideProps Function

To perform server-side rendering on a page, use the getServerSideProps function to fetch data and render the page on the server.

// pages/home/index.js
export default function HomePage({ data }) {
  return (
    <div> 
      <h1>Welcome to Home Page</h1>
      <p>Data: {data.message}</p>
    </div>
  );
}

export async function getServerSideProps() {
  const response = await fetch('/api/data');
  const data = await response.json();

  return {
    props: {
      data,
    },
  };
}

API Route

Create an API route to handle requests and return data.

// pages/api/data.js
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from the API!' });
}

Fetching Data with getServerSideProps

The getServerSideProps function runs on every request, allowing you to fetch the latest data on the server.

// pages/home/index.js
export default function HomePage({ data }) {
  return (
    <div>
      <h1>Welcome to Home Page</h1>
      <p>Data: {data.message}</p>
    </div>
  );
}

export async function getServerSideProps(context) {
  const { req, res } = context;

  // Simulate fetching data from a database
  const data = await fetch('/api/data', { headers: req.headers })
    .then((response) => response.json());

  return {
    props: {
      data,
    },
  };
}

Summary

  • Using getServerSideProps: Use getServerSideProps in page components to fetch data and render pages on the server.
  • Fetching Data: Fetch data on the server using getServerSideProps.
  • Conditional Rendering: Decide whether to render the page or redirect based on server-side data.

Static Site Generation (SSG)

Static Site Generation (SSG)

Using the getStaticProps Function

To perform static site generation on a page, use the getStaticProps function to fetch data and generate static pages at build time.

// pages/post/[id].js
import { useRouter } from 'next/router';

export default function PostPage({ post }) {
  const router = useRouter();
  const { id } = router.query;

  return (
    <div>
      <h1>Post ID: {id}</h1>
      <p>Title: {post.title}</p>
      <p>Content: {post.content}</p>
    </div>
  );
}

export async function getStaticProps(context) {
  const { params } = context;
  const id = params.id;

  // Simulate fetching data from a database
  const response = await fetch(`https://example.com/api/posts/${id}`);
  const post = await response.json();

  return {
    props: {
      post,
    },
  };
}

export async function getStaticPaths() {
  // Simulate fetching all post IDs from a database
  const response = await fetch('https://example.com/api/posts');
  const posts = await response.json();

  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));

  return {
    paths,
    fallback: false, // Can be set to 'blocking' or 'unstable_blocking' for dynamic routes
  };
}

Fetching Data with getStaticProps

The getStaticProps function runs once at build time, allowing you to fetch data and inject it into the page’s props.

// pages/post/[id].js
import { useRouter } from 'next/router';

export default function PostPage({ post }) {
  const router = useRouter();
  const { id } = router.query;

  return (
    <div>
      <h1>Post ID: {id}</h1>
      <p>Title: {post.title}</p>
      <p>Content: {post.content}</p>
    </div>
  );
}

export async function getStaticProps(context) {
  const { params } = context;
  const id = params.id;

  // Simulate fetching data from a database
  const response = await fetch(`https://example.com/api/posts/${id}`);
  const post = await response.json();

  return {
    props: {
      post,
    },
  };
}

Dynamic Routes with getStaticPaths

The getStaticPaths function defines parameters for dynamic routes, enabling the generation of corresponding static pages at build time.

// pages/post/[id].js
import { useRouter } from 'next/router';

export default function PostPage({ post }) {
  const router = useRouter();
  const { id } = router.query;

  return (
    <div>
      <h1>Post ID: {id}</h1>
      <p>Title: {post.title}</p>
      <p>Content: {post.content}</p>
    </div>
  );
}

export async function getStaticProps(context) {
  const { params } = context;
  const id = params.id;

  // Simulate fetching data from a database
  const response = await fetch(`https://example.com/api/posts/${id}`);
  const post = await response.json();

  return {
    props: {
      post,
    },
  };
}

export async function getStaticPaths() {
  // Simulate fetching all post IDs from a database
  const response = await fetch('https://example.com/api/posts');
  const posts = await response.json();

  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));

  return {
    paths,
    fallback: false, // Can be set to 'blocking' or 'unstable_blocking' for dynamic routes
  };
}

Using the revalidate Option

Use the revalidate option to specify when a page should be regenerated.

// pages/post/[id].js
import { useRouter } from 'next/router';

export default function PostPage({ post }) {
  const router = useRouter();
  const { id } = router.query;

  return (
    <div>
      <h1>Post ID: {id}</h1>
      <p>Title: {post.title}</p>
      <p>Content: {post.content}</p>
    </div>
  );
}

export async function getStaticProps(context) {
  const { params } = context;
  const id = params.id;

  // Simulate fetching data from a database
  const response = await fetch(`https://example.com/api/posts/${id}`);
  const post = await response.json();

  return {
    props: {
      post,
    },
    revalidate: 60, // Regenerate the page every 60 seconds
  };
}

export async function getStaticPaths() {
  // Simulate fetching all post IDs from a database
  const response = await fetch('https://example.com/api/posts');
  const posts = await response.json();

  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));

  return {
    paths,
    fallback: false, // Can be set to 'blocking' or 'unstable_blocking' for dynamic routes
  };
}

Summary

  • Using getStaticProps: Use getStaticProps in page components to fetch data and generate static pages at build time.
  • Dynamic Routes: Use getStaticPaths to define parameters for dynamic routes.
  • Regenerating Pages: Use the revalidate option to specify when pages should be regenerated.

Automatic Static Optimization

Enabling Automatic Static Optimization

Without Using Specific Static Generation or Server-Side Rendering Functions

If you do not use getStaticProps, getStaticPaths, getServerSideProps, or generateStaticParams and load functions, Next.js will automatically select the most suitable rendering strategy.

// pages/home/index.js
export default function HomePage() {
  return (
    <div>
      <h1>Welcome to Home Page</h1>
      <p>This is a static page.</p>
    </div>
  );
}

How Automatic Static Optimization Works

When Next.js detects that a page does not use the aforementioned functions, it will:

  • Static Generation: If the page does not require dynamic data or user authentication, Next.js generates it as a static page.
  • Server-Side Rendering: If the page requires dynamic data or user authentication, Next.js performs server-side rendering.

Determining If a Page Is Statically Generated

You can check the __NEXT_DATA__ object’s props property to determine if a page is statically generated.

// pages/home/index.js
export default function HomePage() {
  const isStaticGenerated = typeof window !== 'undefined' && !window.__NEXT_DATA__.props;

  return (
    <div>
      <h1>Welcome to Home Page</h1>
      <p>{isStaticGenerated ? 'This is a statically generated page.' : 'This is a server-side rendered page.'}</p>
    </div>
  );
}

When to Use Automatic Static Optimization

Automatic static optimization is suitable for the following scenarios:

  • No Dynamic Data Required: The page content does not change based on user requests.
  • No User Authentication Required: The page is accessible without user login.
  • Infrequent Updates: The page content does not require frequent updates.

Summary

  • Avoid Specific Functions: If the page does not need dynamic data or user authentication, avoid using getStaticProps, getStaticPaths, getServerSideProps, or generateStaticParams and load functions.
  • Check __NEXT_DATA__: Determine if a page is statically generated by checking the __NEXT_DATA__ object.
  • Applicable Scenarios: Suitable for pages without dynamic data or user authentication requirements.

Client-Side Rendering (CSR)

Client-Side Rendering (CSR) is a rendering mode that allows pages to dynamically load and update content in the client browser. It is typically used for applications requiring real-time interaction, such as chat applications or dynamic forms.

Enabling Client-Side Rendering

Using React Hooks like useEffect or useState

Use React Hooks such as useEffect or useState in page components to handle client-side state and side effects.

// pages/chat/index.js
import { useState, useEffect } from 'react';

export default function ChatPage() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const fetchMessages = async () => {
      const response = await fetch('/api/messages');
      const data = await response.json();
      setMessages(data);
    };

    fetchMessages();
  }, []);

  return (
    <div>
      <h1>Chat Room</h1>
      <ul>
        {messages.map((message) => (
          <li key={message.id}>{message.text}</li>
        ))}
      </ul>
    </div>
  );
}

Using useEffect and useState

Use useEffect to listen for events or perform side effects, and useState to manage page state.

// pages/chat/index.js
import { useState, useEffect } from 'react';

export default function ChatPage() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const fetchMessages = async () => {
      const response = await fetch('/api/messages');
      const data = await response.json();
      setMessages(data);
    };

    fetchMessages();

    // Listen for new messages
    const handleNewMessage = (event) => {
      const newMessage = JSON.parse(event.data);
      setMessages((prevMessages) => [...prevMessages, newMessage]);
    };

    window.addEventListener('message', handleNewMessage);

    return () => {
      window.removeEventListener('message', handleNewMessage);
    };
  }, []);

  return (
    <div>
      <h1>Chat Room</h1>
      <ul>
        {messages.map((message) => (
          <li key={message.id}>{message.text}</li>
        ))}
      </ul>
    </div>
  );
}

Dynamic Loading with useEffect

Use useEffect to handle data fetching on initial page load.

// pages/chat/index.js
import { useState, useEffect } from 'react';

export default function ChatPage() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const fetchMessages = async () => {
      const response = await fetch('/api/messages');
      const data = await response.json();
      setMessages(data);
    };

    fetchMessages();
  }, []);

  return (
    <div>
      <h1>Chat Room</h1>
      <ul>
        {messages.map((message) => (
          <li key={message.id}>{message.text}</li>
        ))}
      </ul>
    </div>
  );
}

Handling Events with useEffect

Use useEffect to listen for events or perform side effects.

// pages/chat/index.js
import { useState, useEffect } from 'react';

export default function ChatPage() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const fetchMessages = async () => {
      const response = await fetch('/api/messages');
      const data = await response.json();
      setMessages(data);
    };

    fetchMessages();

    // Listen for new messages
    const handleNewMessage = (event) => {
      const newMessage = JSON.parse(event.data);
      setMessages((prevMessages) => [...prevMessages, newMessage]);
    };

    window.addEventListener('message', handleNewMessage);

    return () => {
      window.removeEventListener('message', handleNewMessage);
    };
  }, []);

  return (
    <div>
      <h1>Chat Room</h1>
      <ul>
        {messages.map((message) => (
          <li key={message.id}>{message.text}</li>
        ))}
      </ul>
    </div>
  );
}

Summary

  • Using useState: Use useState in page components to manage state.
  • Using useEffect: Use useEffect to handle client-side side effects, such as data fetching and event listening.
  • Dynamic Data Loading: Use useEffect to handle data fetching on initial page load.
  • Event Listening: Use useEffect to listen for events or perform side effects.
Share your love