blitz-js / next-superjson-plugin

SuperJSON Plugin for Next.js Pages and Components
192 stars 13 forks source link

Plugin does not appear to be working from nested Server Components #62

Closed nielsrowinbik closed 1 year ago

nielsrowinbik commented 1 year ago

Verify Next.js canary release

Describe the bug

When I render a Client Component within a "top-level" Server Component (a page.tsx) in Next.js 13 (app directory) the plugin works like a charm.

However, when I render (let's say) a Server Component within another Server Component and render a Client Component within the first Server Component, the plugin does not seem to work.

This works:

// page.tsx
export default function Page() {
  const object = {
    date: new Date(),
    name: "Whatever",
  };

  return <ClientComponent data={object} data-superjson />;
}

This does not:

// page.tsx
export default function Page() {
  const object = {
    date: new Date(),
    name: "Whatever",
  };

  return <ServerComponent data={object} />;
}

// ServerComponent.tsx
export default function ServerComponent({ object }) {
  return <ClientComponent data={object} data-superjson />;
}

I have verified that typeof object.date returns object within ServerComponent.tsx, but within ClientComponent.tsx it's turned into a string with Next.js complaining about passing a non-plain object from a Server Component to a Client Component. This warning is eliminated using this plugin in the first example.

Expected behavior

I expect the data to be passed through as-is to the different Server Components (which it is) and handled by this plugin the moment it reaches a Client Component (this does not happen).

Reproduction link

No response

Version

0.5.3

Config

I'm not passing in any options.

Additional context

I've tried setting the data-superjson attribute on the entire component trail, but that's not solving the issue (it is, in fact, breaking my entire app).

I don't currently have a reproduction repo but I can create one if necessary.

orionmiz commented 1 year ago

I couldn't reproduce this bug.

Have you tried to clean your .next cache directory?

nielsrowinbik commented 1 year ago

Editing my comment because I've done some further digging. Here are my findings:

  1. When the page fetches the data, this plugin only works when the Client Component with the data-superjson attribute is rendered directly from the Page component (example). Nesting it within another Server Component (or multiple) does not work (example).
  2. When the page is importing the data (or defining it, as per my original report), the same applies. I have a working example and the one that does not.
  3. This plugin does not work at all when using Suspense. When the page does no fetching but instead renders an Async Component through Suspense that does, it doesn't matter whether the Async Component renders a Client Component with the data-superjson prop (example), or whether there's a Server Component within that component three (example) - neither scenario works.

I hope this helps.

Previous comment I've looked into this to try and reproduce in a clean repo and have found that I was using React Suspense. I'm sorry for not mentioning this earlier, but this is most definitely what's causing the plugin to not work. Here's the repository that contains reproduction: https://github.com/nielsrowinbik/next-superjson-plugin-repro. Would you like me to update the original bug report?
orionmiz commented 1 year ago

Place the components folder inside your app directory.

...or you can just use v0.5.5 that transforms a wider range of modules.

AryanJ-NYC commented 1 year ago

I've upgraded to 0.5.5 and I'm still getting the Date passed into my client component as a string.

My client component is also a grandchild of the page component like OP's.

orionmiz commented 1 year ago

@AryanJ-NYC Please provide a reproduction link. I'll take a look at it.

nielsrowinbik commented 1 year ago

Just upgraded my reproduction repo to 0.5.5 and I can confirm that the issue I was experiencing has now been fixed. Thanks!

AryanJ-NYC commented 1 year ago

@AryanJ-NYC Please provide a reproduction link. I'll take a look at it.

@orionmiz Here you are: https://github.com/AryanJ-NYC/superjson-monorepo

It includes a monorepo of /apps and /packages. The /apps/admin homepage imports a server component from the /packages directory which imports a client component from its same directory.

Thank you! Let me know if there's anything else I can do (such as create a separate issue) and if there's a workaround you can think of!

orionmiz commented 1 year ago

@AryanJ-NYC Check out v0.5.6 release.

AryanJ-NYC commented 1 year ago

@orionmiz this worked!

I was having a little trouble getting it to work in my project repository and learned that it was because my shared component was inside a directory called /pages (unrelated to next.js /pages) which breaks the plugin. I took it out of the /pages folder and it worked well.

In case you wanted to support having it work for {externalDir}/pages/clientComponent.tsx, I've gone ahead and updated https://github.com/AryanJ-NYC/superjson-monorepo to illustrate the bug.

Please note that I worked around this by taking it out of the /pages directory so am very happy with this. Thank you!

orionmiz commented 1 year ago

@AryanJ-NYC That's the intended behavior. There is no way for the plugin to access the context from Next.js (because it's just a compiler plugin) So I used a hacky method to determine which directory is being used by referencing both the path of the module and the current working directory (cwd). If a module is under the /pages directory, the plugin has no choice but to consider that the pages directory is being used, and the transform for the app directory could never be applied.

AryanJ-NYC commented 1 year ago

Fair enough.

Thank you for your work!

piotrkulpinski commented 1 year ago

@orionmiz this worked!

I was having a little trouble getting it to work in my project repository and learned that it was because my shared component was inside a directory called /pages (unrelated to next.js /pages) which breaks the plugin. I took it out of the /pages folder and it worked well.

In case you wanted to support having it work for {externalDir}/pages/clientComponent.tsx, I've gone ahead and updated https://github.com/AryanJ-NYC/superjson-monorepo to illustrate the bug.

Please note that I worked around this by taking it out of the /pages directory so am very happy with this. Thank you!

Thanks for sharing! I just had the same issue and it saved me hours of debugging!