Adding client component

We are now ready to move to client components.

Let's start by creating a new components folder inside src:

mkdir src/components

Inside this folder we create a Posts.tsx file. Posts is a client component that will render the posts from the API:

Client components are defined by adding "use client" to the top of the file.

src/components/Posts.tsx
"use client";

import type { Post } from "../services/schema";

export default function Posts({ posts }: { posts: readonly Post[] }) {
  return (
    <div>
      {posts.map((post) => (
        <div key={post.id}>
          <h1>{post.title}</h1>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
}

This client component receives posts as props from the server component, and it will render them on the client.

Inside src/pages/index.tsx we render Posts passing posts as a prop:

src/pages/index.tsx
export default async function HomePage() {
  return (
    <div>
      <title>Index</title>
      {await RuntimeServer.runPromise(
        main.pipe(
          Effect.match({
            onFailure: Match.valueTags({
              ParseError: (error) => <span>ParseError</span>,
              RequestError: (error) => <span>RequestError</span>,
              ResponseError: (error) => <span>ResponseError</span>,
            }),
            onSuccess: (posts) => <Posts posts={posts} />,
          })
        )
      )}
    </div>
  );
}

We now have the following:

  • Effect services independent of both server and client components
  • Server component with a main effect that executes the Api service and renders Posts
  • Client component that receives posts as props and renders them on the client

But wait! We notice an error when running the code at this point:

Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
  [{userId: 1, id: 1, title: ..., body: ...}, ..., ..., ...]
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^