SebastianKra 5 days ago

You can...

...have a client component inside the post. For example, for each post, have a server component, that contains a <ClientDeleteButton postId={...} />.

...have a wrapper client component that takes a server components as a child. Eg. if you want to show a hover-card for each post:

    <ClientHoverCard preview={<Preview />}>
        <ServerPost />
    </ClientHoverCard>
https://nextjs.org/docs/app/building-your-application/render...

> props duplicated across items in the list, you've got to duplicate the data in the JSON

I'm pretty sure gzip would just compress that.

1
bastawhiz 4 days ago

> I'm pretty sure gzip would just compress that.

Bytes on the wire aren't nearly as important in this case. That value still has to be decompressed into a string and that string needs to be parsed into objects and that's all before you pump it into the renderer.

> have a wrapper client component that takes a server components as a child.

That doesn't work for the model defined in this post. Because now each post is a request to the server instead of one single request that returns a rendered list of posts. That's literally the point of doing this whole roundabout thing: to offload as much work as possible to the server.

> For example, for each post, have a server component, that contains a <ClientDeleteButton postId={...} />.

And now only the delete button reacts to being pressed. You can't remove the post from the page. You can't make the post semi transparent. You can't disable the other buttons on the post.

Without making a mess with contexts, state and interactivity can only happen in the client component islands.

And you know what? If you're building a page that's mostly static on a site that sees almost no code changes or deployments, this probably works great for certain cases. But it's far from an ideal practice for anything that's even mildly interactive.

Even just rendering the root of your render tree is problematic, because you probably want to show loading indicators and update the page title or whatever, and that means loading client code to load server code that runs more client code. At least with good old fashioned SSR, by the time code in the browser starts running, everything is already ready to be fully interactive.

SebastianKra 4 days ago

> That doesn't work for the model defined in this post. Because now each post is a request to the server instead of one single request that returns a rendered list of posts.

Thats where you’re wrong. The JSX snippet that I posted above gets turned into:

    { 
        type: "src/ClientHoverCard.js#ClientHoverCard", 
        props: { 
            preview: // this is already rendered on the server
            children: // this is already rendered on the server
        }
    }
If you wanted to fade the entire post when pressing the delete button without contexts, you’d create a client component like this:

    "use client"
    function DeletablePost({ children }: { children: ReactNode }) {
        const [isDeleted, setDeleted] = useState(false)
        return <div style={{ opacity: isDeleted ? 0.5 : 1 }}>
            {children}
            <DeleteButton onChange={setDeleted} />
        </div>
    }
And pass it a server component like this:

    <DeletablePost>
        <ServerPost />
    </DeletablePoast>