How do I pass data from server component to client component in Next.js 14 app router?

3 min read 05-10-2024
How do I pass data from server component to client component in Next.js 14 app router?


Passing Data from Server to Client Components in Next.js 14 App Router

The Next.js 14 App Router introduces a powerful new way to build your applications, offering server-side rendering and dynamic routing. This opens up opportunities for efficient data fetching and data sharing between server and client components. However, effectively passing data from the server to the client can be a bit tricky, especially if you're new to the App Router.

In this article, we'll explore how to seamlessly transfer data from your server components to client components, ensuring a smooth user experience with the latest Next.js features.

The Challenge: Bridging the Gap

Imagine you're building a blog using the Next.js App Router. You want to display a list of blog posts on the homepage, fetching the data from your database using a server component. The challenge lies in making this fetched data available to the client components that render the actual blog post list on the page.

Here's a simplified example of how a server component might be used:

// pages/blog/index.js (server component)
"use server";

import { getPosts } from "@/lib/db";

export async function GET() {
  const posts = await getPosts();
  return NextResponse.json(posts);
}

The above code fetches the posts from the database and sends the data back to the client. However, we need a way to access this data from the client components.

Solutions for Effective Data Transfer

Next.js 14 App Router provides us with several ways to achieve this data transfer:

  1. Server Actions: Server Actions, a powerful feature introduced in Next.js 13, allow you to directly trigger actions (like fetching data) from the client side and receive results. These actions are executed on the server, ensuring data integrity and security.

  2. use server and use client: Next.js provides use server and use client directives to clearly define the execution context of your components. Using use client on a component indicates that it will run in the browser, allowing you to access the data received from server components.

Example using Server Actions:

// pages/blog/index.js (server component)
"use server";

import { getPosts } from "@/lib/db";

export default async function BlogPage() {
  const posts = await getPosts();
  return (
    <div>
      <PostList posts={posts} />
    </div>
  );
}

// pages/blog/index.js (client component)
"use client";

import { useState } from 'react';

export default function PostList({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

In this example, the BlogPage component (server component) fetches the blog posts and passes them as props to the PostList component (client component) which is responsible for rendering the list on the client side.

Example using use server and use client:

// pages/blog/index.js (server component)
"use server";

import { getPosts } from "@/lib/db";

export default async function BlogPage() {
  const posts = await getPosts();
  return (
    <div>
      <PostList posts={posts} />
    </div>
  );
}

// components/PostList.js (client component)
"use client";

export default function PostList({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

This approach leverages the use client directive to define that the PostList component will be rendered on the client. The server component BlogPage then passes the fetched data to the client component via props.

Additional Considerations:

  • Performance: Server actions and server components optimize for initial page load, while client components handle dynamic updates.
  • Data Fetching: You can use the fetch API in client components to make requests to the server component, but prefer server actions for optimal performance and data synchronization.
  • State Management: For complex applications with multiple components and dynamic data, consider using state management libraries like Redux or Zustand.

Conclusion

Next.js 14 App Router provides a streamlined approach for building server-rendered applications. Passing data from server to client components is a crucial aspect, and by understanding the solutions available, you can create efficient and robust applications with seamless data flow. Remember to choose the best approach based on your specific project requirements and optimize for performance and user experience.

Resources: